diff --git a/Changelog.txt b/Changelog.txt
index fc019e944..c5c2dddf5 100644
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -10,6 +10,7 @@ Key:
Version 1.5.01-dev
+ Added new child skill; Salvage
+ Added UUID support!
+ + Added SQL connection pooling and async loading!
+ Added new feature to Herbalism. Instantly-regrown crops are protected from being broken for 1 second
+ Added option to config.yml to show the /mcstats scoreboard automatically after logging in
+ Added option to config.yml for Alchemy. Skills.Alchemy.Prevent_Hopper_Transfer_Bottles
diff --git a/pom.xml b/pom.xml
index 13b794059..2f52c8cfc 100755
--- a/pom.xml
+++ b/pom.xml
@@ -76,6 +76,8 @@
com.turt2live.metrics:MetricsExtension
+ commons-logging:commons-logging
+ net.snaq:dbpool
@@ -83,6 +85,14 @@
com.turt2live.metrics
com.gmail.nossr50.metrics.mcstats
+
+ org.apache.commons.logging
+ com.gmail.nossr50.commons.logging
+
+
+ net.snaq
+ com.gmail.nossr50.dbpool
+
@@ -136,6 +146,11 @@
MetricsExtension
0.0.5-SNAPSHOT
+
+ net.snaq
+ dbpool
+ 5.1
+
diff --git a/src/main/java/com/gmail/nossr50/api/ExperienceAPI.java b/src/main/java/com/gmail/nossr50/api/ExperienceAPI.java
index aef29050b..dbfddb519 100644
--- a/src/main/java/com/gmail/nossr50/api/ExperienceAPI.java
+++ b/src/main/java/com/gmail/nossr50/api/ExperienceAPI.java
@@ -930,7 +930,7 @@ public final class ExperienceAPI {
}
private static PlayerProfile getOfflineProfile(UUID uuid) {
- PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(uuid, false);
+ PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(uuid);
if (!profile.isLoaded()) {
throw new InvalidPlayerException();
@@ -942,7 +942,7 @@ public final class ExperienceAPI {
@Deprecated
private static PlayerProfile getOfflineProfile(String playerName) {
UUID uuid = mcMMO.p.getServer().getOfflinePlayer(playerName).getUniqueId();
- PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(uuid, false);
+ PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(uuid);
if (!profile.isLoaded()) {
throw new InvalidPlayerException();
diff --git a/src/main/java/com/gmail/nossr50/commands/database/ConvertDatabaseCommand.java b/src/main/java/com/gmail/nossr50/commands/database/ConvertDatabaseCommand.java
index 7f85a8977..8a85a67d0 100644
--- a/src/main/java/com/gmail/nossr50/commands/database/ConvertDatabaseCommand.java
+++ b/src/main/java/com/gmail/nossr50/commands/database/ConvertDatabaseCommand.java
@@ -12,6 +12,7 @@ import com.gmail.nossr50.datatypes.database.DatabaseType;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.runnables.database.DatabaseConversionTask;
+import com.gmail.nossr50.runnables.player.PlayerProfileLoadingTask;
import com.gmail.nossr50.util.player.UserManager;
public class ConvertDatabaseCommand implements CommandExecutor {
@@ -55,13 +56,13 @@ public class ConvertDatabaseCommand implements CommandExecutor {
UserManager.clearAll();
for (Player player : mcMMO.p.getServer().getOnlinePlayers()) {
- PlayerProfile profile = oldDatabase.loadPlayerProfile(player.getUniqueId(), false);
+ PlayerProfile profile = oldDatabase.loadPlayerProfile(player.getUniqueId());
if (profile.isLoaded()) {
mcMMO.getDatabaseManager().saveUser(profile);
}
- UserManager.addUser(player);
+ new PlayerProfileLoadingTask(player).runTaskTimerAsynchronously(mcMMO.p, 1, 20); // 1 Tick delay to ensure the player is marked as online before we begin loading
}
new DatabaseConversionTask(oldDatabase, sender, previousType.toString(), newType.toString()).runTaskAsynchronously(mcMMO.p);
diff --git a/src/main/java/com/gmail/nossr50/commands/experience/ConvertExperienceCommand.java b/src/main/java/com/gmail/nossr50/commands/experience/ConvertExperienceCommand.java
index 3c0a97f64..5efd18d06 100644
--- a/src/main/java/com/gmail/nossr50/commands/experience/ConvertExperienceCommand.java
+++ b/src/main/java/com/gmail/nossr50/commands/experience/ConvertExperienceCommand.java
@@ -9,6 +9,7 @@ import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.datatypes.experience.FormulaType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.runnables.database.FormulaConversionTask;
+import com.gmail.nossr50.runnables.player.PlayerProfileLoadingTask;
import com.gmail.nossr50.util.player.UserManager;
public class ConvertExperienceCommand implements CommandExecutor {
@@ -37,7 +38,7 @@ public class ConvertExperienceCommand implements CommandExecutor {
new FormulaConversionTask(sender, newType).runTaskLater(mcMMO.p, 1);
for (Player player : mcMMO.p.getServer().getOnlinePlayers()) {
- UserManager.addUser(player);
+ new PlayerProfileLoadingTask(player).runTaskTimerAsynchronously(mcMMO.p, 1, 20); // 1 Tick delay to ensure the player is marked as online before we begin loading
}
return true;
diff --git a/src/main/java/com/gmail/nossr50/config/Config.java b/src/main/java/com/gmail/nossr50/config/Config.java
index 4147af89c..f7ce43726 100644
--- a/src/main/java/com/gmail/nossr50/config/Config.java
+++ b/src/main/java/com/gmail/nossr50/config/Config.java
@@ -232,6 +232,7 @@ public class Config extends AutoUpdateConfigLoader {
/* General Settings */
public String getLocale() { return config.getString("General.Locale", "en_us"); }
public boolean getMOTDEnabled() { return config.getBoolean("General.MOTD_Enabled", true); }
+ public boolean getShowProfileLoadedMessage() { return config.getBoolean("General.Show_Profile_Loaded", true); }
public boolean getDonateMessageEnabled() { return config.getBoolean("Commands.mcmmo.Donate_Message", true); }
public int getSaveInterval() { return config.getInt("General.Save_Interval", 10); }
public boolean getStatsTrackingEnabled() { return config.getBoolean("General.Stats_Tracking", true); }
@@ -313,6 +314,8 @@ public class Config extends AutoUpdateConfigLoader {
public int getMySQLServerPort() { return config.getInt("MySQL.Server.Port", 3306); }
public String getMySQLServerName() { return config.getString("MySQL.Server.Address", "localhost"); }
public String getMySQLUserPassword() { return getStringIncludingInts("MySQL.Database.User_Password"); }
+ public int getMySQLMaxConnections() { return config.getInt("MySQL.Database.MaxConnections"); }
+ public int getMySQLMaxPoolSize() { return config.getInt("MySQL.Database.MaxPoolSize"); }
private String getStringIncludingInts(String key) {
String str = config.getString(key);
diff --git a/src/main/java/com/gmail/nossr50/database/DatabaseManager.java b/src/main/java/com/gmail/nossr50/database/DatabaseManager.java
index af0521b58..6b5e4c079 100644
--- a/src/main/java/com/gmail/nossr50/database/DatabaseManager.java
+++ b/src/main/java/com/gmail/nossr50/database/DatabaseManager.java
@@ -73,7 +73,7 @@ public interface DatabaseManager {
/**
* Load a player from the database.
*
- * @deprecated replaced by {@link #loadPlayerProfile(UUID uuid, boolean createNew)}
+ * @deprecated replaced by {@link #loadPlayerProfile(String playerName, UUID uuid, boolean createNew)}
*
* @param playerName The name of the player to load from the database
* @param createNew Whether to create a new record if the player is not
@@ -88,12 +88,9 @@ public interface DatabaseManager {
* Load a player from the database.
*
* @param uuid The uuid of the player to load from the database
- * @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 PlayerProfile loadPlayerProfile(UUID uuid, boolean createNew);
+ public PlayerProfile loadPlayerProfile(UUID uuid);
/**
* Load a player from the database. Attempt to use uuid, fall back on playername
@@ -132,4 +129,9 @@ public interface DatabaseManager {
* @return The type of database
*/
public DatabaseType getDatabaseType();
+
+ /**
+ * Called when the plugin disables
+ */
+ public void onDisable();
}
diff --git a/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java
index e5d4588eb..03710d801 100644
--- a/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java
+++ b/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java
@@ -2,7 +2,6 @@ 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;
@@ -10,6 +9,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -98,8 +98,22 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
- tryClose(in);
- tryClose(out);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
@@ -164,8 +178,22 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
- tryClose(in);
- tryClose(out);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
@@ -203,8 +231,22 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
- tryClose(in);
- tryClose(out);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
@@ -294,8 +336,22 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
return false;
}
finally {
- tryClose(in);
- tryClose(out);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
}
@@ -311,7 +367,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
public Map readRank(String playerName) {
updateLeaderboards();
- Map skills = new HashMap();
+ Map skills = new EnumMap(SkillType.class);
for (SkillType skill : SkillType.NON_CHILD_SKILLS) {
skills.put(skill, getPlayerRank(playerName, playerStatHash.get(skill)));
@@ -381,18 +437,25 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
e.printStackTrace();
}
finally {
- tryClose(out);
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
}
@Deprecated
public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
- return loadPlayerProfile(playerName, "", create);
+ return loadPlayerProfile(playerName, "", false);
}
- public PlayerProfile loadPlayerProfile(UUID uuid, boolean create) {
- return loadPlayerProfile("", uuid.toString(), create);
+ public PlayerProfile loadPlayerProfile(UUID uuid) {
+ return loadPlayerProfile("", uuid.toString(), false);
}
public PlayerProfile loadPlayerProfile(String playerName, UUID uuid, boolean create) {
@@ -448,7 +511,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
in.close();
}
catch (IOException e) {
- e.printStackTrace();
+ // Ignore
}
}
}
@@ -491,7 +554,14 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
e.printStackTrace();
}
finally {
- tryClose(in);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
}
@@ -532,8 +602,22 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
- tryClose(in);
- tryClose(out);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
@@ -573,8 +657,22 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
- tryClose(in);
- tryClose(out);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
@@ -601,7 +699,14 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
e.printStackTrace();
}
finally {
- tryClose(in);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
return users;
@@ -671,7 +776,14 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " during user " + playerName + " (Are you sure you formatted it correctly?) " + e.toString());
}
finally {
- tryClose(in);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
@@ -897,8 +1009,22 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
- tryClose(in);
- tryClose(out);
+ if (in != null) {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ // Ignore
+ }
+ }
}
}
@@ -923,18 +1049,6 @@ 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;
@@ -967,8 +1081,8 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
private PlayerProfile loadFromLine(String[] character) {
Map skills = getSkillMapFromLine(character); // Skill levels
- Map skillsXp = new HashMap(); // Skill & XP
- Map skillsDATS = new HashMap(); // Ability & Cooldown
+ Map skillsXp = new EnumMap(SkillType.class); // Skill & XP
+ Map skillsDATS = new EnumMap(AbilityType.class); // Ability & Cooldown
MobHealthbarType mobHealthbarType;
// TODO on updates, put new values in a try{} ?
@@ -1019,7 +1133,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
}
private Map getSkillMapFromLine(String[] character) {
- Map skills = new HashMap(); // Skill & Level
+ Map skills = new EnumMap(SkillType.class); // Skill & Level
skills.put(SkillType.TAMING, Integer.valueOf(character[24]));
skills.put(SkillType.MINING, Integer.valueOf(character[1]));
@@ -1041,4 +1155,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
public DatabaseType getDatabaseType() {
return DatabaseType.FLATFILE;
}
+
+ @Override
+ public void onDisable() { }
}
diff --git a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java
index 0ee9e1b90..f219039b5 100644
--- a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java
+++ b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java
@@ -1,7 +1,6 @@
package com.gmail.nossr50.database;
import java.sql.Connection;
-import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
@@ -9,6 +8,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -24,318 +24,201 @@ import com.gmail.nossr50.datatypes.database.UpgradeType;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.AbilityType;
import com.gmail.nossr50.datatypes.skills.SkillType;
-import com.gmail.nossr50.runnables.database.SQLDatabaseKeepaliveTask;
-import com.gmail.nossr50.runnables.database.SQLReconnectTask;
import com.gmail.nossr50.runnables.database.UUIDUpdateAsyncTask;
import com.gmail.nossr50.util.Misc;
+import snaq.db.ConnectionPool;
+
public final class SQLDatabaseManager implements DatabaseManager {
- private String connectionString;
+ private static final String ALL_QUERY_VERSION = "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy";
private String tablePrefix = Config.getInstance().getMySQLTablePrefix();
- private Connection connection = null;
- // Scale waiting time by this much per failed attempt
- private final double SCALING_FACTOR = 40.0;
+ private final int POOL_FETCH_TIMEOUT = 0; // How long a method will wait for a connection. Since none are on main thread, we can safely say wait for as long as you like.
- // Minimum wait in nanoseconds (default 500ms)
- private final long MIN_WAIT = 500L * 1000000L;
+ private final Map cachedUserIDs = new HashMap();
+ private final Map cachedUserIDsByName = new HashMap();
- // Maximum time to wait between reconnects (default 5 minutes)
- private final long MAX_WAIT = 5L * 60L * 1000L * 1000000L;
-
- // How long to wait when checking if connection is valid (default 3 seconds)
- private final int VALID_TIMEOUT = 3;
-
- // When next to try connecting to Database in nanoseconds
- private long nextReconnectTimestamp = 0L;
-
- // How many connection attempts have failed
- private int reconnectAttempt = 0;
+ private ConnectionPool connectionPool;
protected SQLDatabaseManager() {
+ String connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName();
+
+ try {
+ // Force driver to load if not yet loaded
+ Class.forName("com.mysql.jdbc.Driver");
+ }
+ catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ return;
+ //throw e; // aborts onEnable() Riking if you want to do this, fully implement it.
+ }
+
+ Properties connectionProperties = new Properties();
+ connectionProperties.put("user", Config.getInstance().getMySQLUserName());
+ connectionProperties.put("password", Config.getInstance().getMySQLUserPassword());
+ connectionProperties.put("autoReconnect", "false");
+ connectionProperties.put("cachePrepStmts", "true");
+ connectionProperties.put("prepStmtCacheSize", "64");
+ connectionProperties.put("prepStmtCacheSqlLimit", "2048");
+ connectionProperties.put("useServerPrepStmts", "true");
+ connectionPool = new ConnectionPool("mcMMO-Pool",
+ 1 /*Minimum of one*/,
+ Config.getInstance().getMySQLMaxPoolSize() /*max pool size */,
+ Config.getInstance().getMySQLMaxConnections() /*max num connections*/,
+ 0 /* idle timeout of connections */,
+ connectionString,
+ connectionProperties);
+ connectionPool.init(); // Init first connection
+ connectionPool.registerShutdownHook(); // Auto release on jvm exit just in case
+
checkStructure();
- new SQLDatabaseKeepaliveTask(this).runTaskTimerAsynchronously(mcMMO.p, 10, 60L * 60 * Misc.TICK_CONVERSION_FACTOR);
}
public void purgePowerlessUsers() {
- if (!checkConnected()) {
- return;
- }
-
mcMMO.p.getLogger().info("Purging powerless users...");
- Collection> 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").values();
+ Connection connection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ List usernames = new ArrayList();
- write("DELETE FROM u, e, h, s, c USING " + tablePrefix + "users u " +
- "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
- "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
- "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
- "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
- "WHERE (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");
+ try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ statement = connection.createStatement();
+ resultSet = statement.executeQuery("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");
+
+ while (resultSet.next()) {
+ usernames.add(resultSet.getString("user"));
+ }
+
+ resultSet.close();
+
+ statement.executeUpdate("DELETE FROM u, e, h, s, c USING " + tablePrefix + "users u " +
+ "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
+ "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
+ "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
+ "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
+ "WHERE (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");
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+
+ if (!usernames.isEmpty()) {
+ processPurge(usernames);
+ }
- processPurge(usernames);
mcMMO.p.getLogger().info("Purged " + usernames.size() + " users from the database.");
}
public void purgeOldUsers() {
- if (!checkConnected()) {
- return;
- }
-
- long currentTime = System.currentTimeMillis();
-
mcMMO.p.getLogger().info("Purging old users...");
- Collection> usernames = read("SELECT user FROM " + tablePrefix + "users WHERE ((" + currentTime + " - lastlogin * " + Misc.TIME_CONVERSION_FACTOR + ") > " + PURGE_TIME + ")").values();
+ Connection connection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ List usernames = new ArrayList();
- write("DELETE FROM u, e, h, s, c USING " + tablePrefix + "users u " +
- "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
- "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
- "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
- "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
- "WHERE ((" + currentTime + " - lastlogin * " + Misc.TIME_CONVERSION_FACTOR + ") > " + PURGE_TIME + ")");
+ try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ statement = connection.createStatement();
+ resultSet = statement.executeQuery("SELECT user FROM " + tablePrefix + "users WHERE ((NOW() - lastlogin * " + Misc.TIME_CONVERSION_FACTOR + ") > " + PURGE_TIME + ")");
+
+ while (resultSet.next()) {
+ usernames.add(resultSet.getString("user"));
+ }
+
+ resultSet.close();
+
+ statement.executeUpdate("DELETE FROM u, e, h, s, c USING " + tablePrefix + "users u " +
+ "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
+ "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
+ "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
+ "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
+ "WHERE ((NOW() - lastlogin * " + Misc.TIME_CONVERSION_FACTOR + ") > " + PURGE_TIME + ")");
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+
+ if (!usernames.isEmpty()) {
+ processPurge(usernames);
+ }
- processPurge(usernames);
mcMMO.p.getLogger().info("Purged " + usernames.size() + " users from the database.");
}
public boolean removeUser(String playerName) {
- if (!checkConnected()) {
- return false;
- }
-
- boolean success = update("DELETE FROM u, e, h, s, c " +
- "USING " + tablePrefix + "users u " +
- "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
- "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
- "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
- "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
- "WHERE u.user = '" + playerName + "'") != 0;
-
- Misc.profileCleanup(playerName);
-
- return success;
- }
-
- public boolean saveUser(PlayerProfile profile) {
- if (!checkConnected()) {
- return false;
- }
-
- int userId = readId(profile.getPlayerName());
- if (userId == -1) {
- newUser(profile.getPlayerName(), profile.getUniqueId().toString());
- userId = readId(profile.getPlayerName());
- if (userId == -1) {
- return false;
- }
- }
- boolean success = true;
- MobHealthbarType mobHealthbarType = profile.getMobHealthbarType();
-
- success &= saveUniqueId(userId, profile.getUniqueId().toString());
- success &= saveLogin(userId, ((int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)));
- success &= saveHuds(userId, (mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()));
- success &= saveLongs(
- "UPDATE " + tablePrefix + "cooldowns SET "
- + " mining = ?, woodcutting = ?, unarmed = ?"
- + ", herbalism = ?, excavation = ?, swords = ?"
- + ", axes = ?, blast_mining = ? WHERE user_id = ?",
- userId,
- profile.getAbilityDATS(AbilityType.SUPER_BREAKER),
- profile.getAbilityDATS(AbilityType.TREE_FELLER),
- profile.getAbilityDATS(AbilityType.BERSERK),
- profile.getAbilityDATS(AbilityType.GREEN_TERRA),
- profile.getAbilityDATS(AbilityType.GIGA_DRILL_BREAKER),
- profile.getAbilityDATS(AbilityType.SERRATED_STRIKES),
- profile.getAbilityDATS(AbilityType.SKULL_SPLITTER),
- profile.getAbilityDATS(AbilityType.BLAST_MINING));
- success &= saveIntegers(
- "UPDATE " + tablePrefix + "skills SET "
- + " taming = ?, mining = ?, repair = ?, woodcutting = ?"
- + ", unarmed = ?, herbalism = ?, excavation = ?"
- + ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
- + ", fishing = ?, alchemy = ? WHERE user_id = ?",
- profile.getSkillLevel(SkillType.TAMING),
- profile.getSkillLevel(SkillType.MINING),
- profile.getSkillLevel(SkillType.REPAIR),
- profile.getSkillLevel(SkillType.WOODCUTTING),
- profile.getSkillLevel(SkillType.UNARMED),
- profile.getSkillLevel(SkillType.HERBALISM),
- profile.getSkillLevel(SkillType.EXCAVATION),
- profile.getSkillLevel(SkillType.ARCHERY),
- profile.getSkillLevel(SkillType.SWORDS),
- profile.getSkillLevel(SkillType.AXES),
- profile.getSkillLevel(SkillType.ACROBATICS),
- profile.getSkillLevel(SkillType.FISHING),
- profile.getSkillLevel(SkillType.ALCHEMY),
- userId);
- success &= saveIntegers(
- "UPDATE " + tablePrefix + "experience SET "
- + " taming = ?, mining = ?, repair = ?, woodcutting = ?"
- + ", unarmed = ?, herbalism = ?, excavation = ?"
- + ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
- + ", fishing = ?, alchemy = ? WHERE user_id = ?",
- profile.getSkillXpLevel(SkillType.TAMING),
- profile.getSkillXpLevel(SkillType.MINING),
- profile.getSkillXpLevel(SkillType.REPAIR),
- profile.getSkillXpLevel(SkillType.WOODCUTTING),
- profile.getSkillXpLevel(SkillType.UNARMED),
- profile.getSkillXpLevel(SkillType.HERBALISM),
- profile.getSkillXpLevel(SkillType.EXCAVATION),
- profile.getSkillXpLevel(SkillType.ARCHERY),
- profile.getSkillXpLevel(SkillType.SWORDS),
- profile.getSkillXpLevel(SkillType.AXES),
- profile.getSkillXpLevel(SkillType.ACROBATICS),
- profile.getSkillXpLevel(SkillType.FISHING),
- profile.getSkillXpLevel(SkillType.ALCHEMY),
- userId);
- return success;
- }
-
- public List readLeaderboard(SkillType skill, int pageNumber, int statsPerPage) {
- List stats = new ArrayList();
-
- if (checkConnected()) {
- String query = skill == null ? "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy" : skill.name().toLowerCase();
- ResultSet resultSet;
- PreparedStatement statement = null;
-
- try {
- statement = connection.prepareStatement("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT ?, ?");
- statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage);
- statement.setInt(2, statsPerPage);
- resultSet = statement.executeQuery();
-
- while (resultSet.next()) {
- ArrayList column = new ArrayList();
-
- for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
- column.add(resultSet.getString(i));
- }
-
- stats.add(new PlayerStat(column.get(1), Integer.valueOf(column.get(0))));
- }
- }
- catch (SQLException ex) {
- printErrors(ex);
- }
- finally {
- if (statement != null) {
- try {
- statement.close();
- }
- catch (SQLException e) {
- // Ignore
- }
- }
- }
- }
-
- return stats;
- }
-
- public Map readRank(String playerName) {
- Map skills = new HashMap();
-
- if (checkConnected()) {
- ResultSet resultSet;
-
- try {
- for (SkillType skillType : SkillType.NON_CHILD_SKILLS) {
- 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.close();
-
- statement = connection.prepareStatement(sql);
- resultSet = statement.executeQuery();
-
- while (resultSet.next()) {
- if (resultSet.getString("user").equalsIgnoreCase(playerName)) {
- skills.put(skillType, 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+alchemy > 0 " +
- "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > " +
- "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " +
- "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");
-
- statement.close();
-
- sql = "SELECT user, taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " +
- "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
- "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " +
- "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy = " +
- "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " +
- "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(null, rank + resultSet.getRow());
- break;
- }
- }
-
- statement.close();
- }
- catch (SQLException ex) {
- printErrors(ex);
- }
- }
-
- return skills;
- }
-
- public void newUser(String playerName, String uuid) {
- if (!checkConnected()) {
- return;
- }
-
+ boolean success = false;
+ Connection connection = null;
PreparedStatement statement = null;
try {
- statement = connection.prepareStatement("INSERT INTO " + tablePrefix + "users (user, uuid, lastlogin) VALUES (?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
- statement.setString(1, playerName);
- statement.setString(2, uuid);
- statement.setLong(3, System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
- statement.execute();
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ statement = connection.prepareStatement("DELETE FROM u, e, h, s, c " +
+ "USING " + tablePrefix + "users u " +
+ "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
+ "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
+ "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
+ "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
+ "WHERE u.user = ?");
- int id = readId(playerName);
- writeMissingRows(id);
+ statement.setString(1, playerName);
+
+ success = statement.executeUpdate() != 0;
}
catch (SQLException ex) {
printErrors(ex);
@@ -349,26 +232,399 @@ public final class SQLDatabaseManager implements DatabaseManager {
// Ignore
}
}
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+
+ if (success) {
+ Misc.profileCleanup(playerName);
+ }
+
+ return success;
+ }
+
+ public boolean saveUser(PlayerProfile profile) {
+ boolean success = true;
+ PreparedStatement statement = null;
+ Connection connection = null;
+
+ try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+
+ int id = getUserID(connection, profile.getUniqueId());
+
+ if (id == -1) {
+ newUser(profile.getPlayerName(), profile.getUniqueId().toString());
+ id = getUserID(connection, profile.getUniqueId());
+ if (id == -1) {
+ return false;
+ }
+ }
+
+ statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET lastlogin = UNIX_TIMESTAMP() WHERE uuid = ?");
+ statement.setString(1, profile.getUniqueId().toString());
+ success &= (statement.executeUpdate() != 0);
+ statement.close();
+
+ statement = connection.prepareStatement("UPDATE " + tablePrefix + "skills SET "
+ + " taming = ?, mining = ?, repair = ?, woodcutting = ?"
+ + ", unarmed = ?, herbalism = ?, excavation = ?"
+ + ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
+ + ", fishing = ?, alchemy = ? WHERE user_id = ?");
+ statement.setInt(1, profile.getSkillLevel(SkillType.TAMING));
+ statement.setInt(2, profile.getSkillLevel(SkillType.MINING));
+ statement.setInt(3, profile.getSkillLevel(SkillType.REPAIR));
+ statement.setInt(4, profile.getSkillLevel(SkillType.WOODCUTTING));
+ statement.setInt(5, profile.getSkillLevel(SkillType.UNARMED));
+ statement.setInt(6, profile.getSkillLevel(SkillType.HERBALISM));
+ statement.setInt(7, profile.getSkillLevel(SkillType.EXCAVATION));
+ statement.setInt(8, profile.getSkillLevel(SkillType.ARCHERY));
+ statement.setInt(9, profile.getSkillLevel(SkillType.SWORDS));
+ statement.setInt(10, profile.getSkillLevel(SkillType.AXES));
+ statement.setInt(11, profile.getSkillLevel(SkillType.ACROBATICS));
+ statement.setInt(12, profile.getSkillLevel(SkillType.FISHING));
+ statement.setInt(13, profile.getSkillLevel(SkillType.ALCHEMY));
+ statement.setInt(14, id);
+ success &= (statement.executeUpdate() != 0);
+ statement.close();
+
+ statement = connection.prepareStatement("UPDATE " + tablePrefix + "experience SET "
+ + " taming = ?, mining = ?, repair = ?, woodcutting = ?"
+ + ", unarmed = ?, herbalism = ?, excavation = ?"
+ + ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
+ + ", fishing = ?, alchemy = ? WHERE user_id = ?");
+ statement.setInt(1, profile.getSkillXpLevel(SkillType.TAMING));
+ statement.setInt(2, profile.getSkillXpLevel(SkillType.MINING));
+ statement.setInt(3, profile.getSkillXpLevel(SkillType.REPAIR));
+ statement.setInt(4, profile.getSkillXpLevel(SkillType.WOODCUTTING));
+ statement.setInt(5, profile.getSkillXpLevel(SkillType.UNARMED));
+ statement.setInt(6, profile.getSkillXpLevel(SkillType.HERBALISM));
+ statement.setInt(7, profile.getSkillXpLevel(SkillType.EXCAVATION));
+ statement.setInt(8, profile.getSkillXpLevel(SkillType.ARCHERY));
+ statement.setInt(9, profile.getSkillXpLevel(SkillType.SWORDS));
+ statement.setInt(10, profile.getSkillXpLevel(SkillType.AXES));
+ statement.setInt(11, profile.getSkillXpLevel(SkillType.ACROBATICS));
+ statement.setInt(12, profile.getSkillXpLevel(SkillType.FISHING));
+ statement.setInt(13, profile.getSkillXpLevel(SkillType.ALCHEMY));
+ statement.setInt(14, id);
+ success &= (statement.executeUpdate() != 0);
+ statement.close();
+
+ statement = connection.prepareStatement("UPDATE " + tablePrefix + "cooldowns SET "
+ + " mining = ?, woodcutting = ?, unarmed = ?"
+ + ", herbalism = ?, excavation = ?, swords = ?"
+ + ", axes = ?, blast_mining = ? WHERE user_id = ?");
+ statement.setLong(1, profile.getAbilityDATS(AbilityType.SUPER_BREAKER));
+ statement.setLong(2, profile.getAbilityDATS(AbilityType.TREE_FELLER));
+ statement.setLong(3, profile.getAbilityDATS(AbilityType.BERSERK));
+ statement.setLong(4, profile.getAbilityDATS(AbilityType.GREEN_TERRA));
+ statement.setLong(5, profile.getAbilityDATS(AbilityType.GIGA_DRILL_BREAKER));
+ statement.setLong(6, profile.getAbilityDATS(AbilityType.SERRATED_STRIKES));
+ statement.setLong(7, profile.getAbilityDATS(AbilityType.SKULL_SPLITTER));
+ statement.setLong(8, profile.getAbilityDATS(AbilityType.BLAST_MINING));
+ statement.setInt(9, id);
+ success = (statement.executeUpdate() != 0);
+ statement.close();
+
+ statement = connection.prepareStatement("UPDATE " + tablePrefix + "huds SET mobhealthbar = ? WHERE user_id = ?");
+ statement.setString(1, profile.getMobHealthbarType() == null ? Config.getInstance().getMobHealthbarDefault().name() : profile.getMobHealthbarType().name());
+ statement.setInt(2, id);
+ success = (statement.executeUpdate() != 0);
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+
+ return success;
+ }
+
+ public List readLeaderboard(SkillType skill, int pageNumber, int statsPerPage) {
+ List stats = new ArrayList();
+
+ String query = skill == null ? ALL_QUERY_VERSION : skill.name().toLowerCase();
+ ResultSet resultSet = null;
+ PreparedStatement statement = null;
+ Connection connection = null;
+
+ try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ statement = connection.prepareStatement("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT ?, ?");
+ statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage);
+ statement.setInt(2, statsPerPage);
+ resultSet = statement.executeQuery();
+
+ while (resultSet.next()) {
+ ArrayList column = new ArrayList();
+
+ for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
+ column.add(resultSet.getString(i));
+ }
+
+ stats.add(new PlayerStat(column.get(1), Integer.valueOf(column.get(0))));
+ }
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+
+ return stats;
+ }
+
+ public Map readRank(String playerName) {
+ Map skills = new EnumMap(SkillType.class);
+
+ ResultSet resultSet = null;
+ PreparedStatement statement = null;
+ Connection connection = null;
+
+ try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ for (SkillType skillType : SkillType.NON_CHILD_SKILLS) {
+ 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 = ?)";
+
+ 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";
+
+ resultSet.close();
+ statement.close();
+
+ statement = connection.prepareStatement(sql);
+ resultSet = statement.executeQuery();
+
+ while (resultSet.next()) {
+ if (resultSet.getString("user").equalsIgnoreCase(playerName)) {
+ skills.put(skillType, rank + resultSet.getRow());
+ break;
+ }
+ }
+
+ resultSet.close();
+ statement.close();
+ }
+
+ String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
+ "WHERE " + ALL_QUERY_VERSION + " > 0 " +
+ "AND " + ALL_QUERY_VERSION + " > " +
+ "(SELECT " + ALL_QUERY_VERSION + " " +
+ "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)";
+
+ statement = connection.prepareStatement(sql);
+ statement.setString(1, playerName);
+ resultSet = statement.executeQuery();
+
+ resultSet.next();
+
+ int rank = resultSet.getInt("rank");
+
+ resultSet.close();
+ statement.close();
+
+ sql = "SELECT user, " + ALL_QUERY_VERSION + " " +
+ "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
+ "WHERE " + ALL_QUERY_VERSION + " > 0 " +
+ "AND " + ALL_QUERY_VERSION + " = " +
+ "(SELECT " + ALL_QUERY_VERSION + " " +
+ "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(null, rank + resultSet.getRow());
+ break;
+ }
+ }
+
+ resultSet.close();
+ statement.close();
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+
+ return skills;
+ }
+
+ public void newUser(String playerName, String uuid) {
+ Connection connection = null;
+
+ try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ newUser(connection, playerName, uuid);
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ private void newUser(Connection connection, String playerName, String uuid) {
+ ResultSet resultSet = null;
+ PreparedStatement statement = null;
+
+ try {
+ statement = connection.prepareStatement("INSERT INTO " + tablePrefix + "users (user, uuid, lastlogin) VALUES (?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
+ statement.setString(1, playerName);
+ statement.setString(2, uuid);
+ statement.setLong(3, System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
+ statement.executeUpdate();
+
+ resultSet = statement.getGeneratedKeys();
+
+ if (!resultSet.next()) {
+ return;
+ }
+
+ writeMissingRows(connection, resultSet.getInt(1));
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
}
}
/**
- * This is a fallback method to provide the old way of getting a PlayerProfile
- * in case there is no UUID match found
+ * This is a fallback method to provide the old way of getting a
+ * PlayerProfile in case there is no UUID match found
*/
private PlayerProfile loadPlayerNameProfile(String playerName, String uuid, boolean create, boolean retry) {
- if (!checkConnected()) {
- // return fake profile if not connected
- if (uuid.isEmpty()) {
- return new PlayerProfile(playerName, false);
- }
-
- return new PlayerProfile(playerName, UUID.fromString(uuid), false);
- }
-
PreparedStatement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ int id = getUserID(connection, playerName);
+
+ if (id == -1) {
+ // There is no such user
+ if (create) {
+ newUser(playerName, uuid);
+ return loadPlayerNameProfile(playerName, uuid, false, false);
+ }
+
+ // Return unloaded profile if can't create
+ return new PlayerProfile(playerName, false);
+ }
+ // There is such a user
+ writeMissingRows(connection, id);
+
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, s.alchemy, "
@@ -383,23 +639,29 @@ public final class SQLDatabaseManager implements DatabaseManager {
+ "WHERE u.user = ?");
statement.setString(1, playerName);
- ResultSet result = statement.executeQuery();
+ resultSet = statement.executeQuery();
- if (result.next()) {
+ if (resultSet.next()) {
try {
- PlayerProfile ret = loadFromResult(playerName, result);
- result.close();
+ PlayerProfile ret = loadFromResult(playerName, resultSet);
return ret;
}
catch (SQLException e) {
}
}
- result.close();
}
catch (SQLException ex) {
printErrors(ex);
}
finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
if (statement != null) {
try {
statement.close();
@@ -408,6 +670,14 @@ public final class SQLDatabaseManager implements DatabaseManager {
// Ignore
}
}
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
}
// Problem, nothing was returned
@@ -417,33 +687,17 @@ public final class SQLDatabaseManager implements DatabaseManager {
return new PlayerProfile(playerName, false);
}
- // 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, uuid);
- return loadPlayerNameProfile(playerName, uuid, false, false);
- }
-
- // Return unloaded profile if can't create
- return new PlayerProfile(playerName, false);
- }
- // There is such a user
- writeMissingRows(id);
// Retry, and abort on re-failure
return loadPlayerNameProfile(playerName, uuid, create, false);
}
@Deprecated
public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
- return loadPlayerProfile(playerName, "", create, true);
+ return loadPlayerProfile(playerName, "", false, true);
}
- public PlayerProfile loadPlayerProfile(UUID uuid, boolean create) {
- return loadPlayerProfile("", uuid.toString(), create, true);
+ public PlayerProfile loadPlayerProfile(UUID uuid) {
+ return loadPlayerProfile("", uuid.toString(), false, true);
}
public PlayerProfile loadPlayerProfile(String playerName, UUID uuid, boolean create) {
@@ -451,18 +705,27 @@ public final class SQLDatabaseManager implements DatabaseManager {
}
private PlayerProfile loadPlayerProfile(String playerName, String uuid, boolean create, boolean retry) {
- if (!checkConnected()) {
- // return fake profile if not connected
- if (uuid.isEmpty()) {
- return new PlayerProfile(playerName, false);
- }
-
- return new PlayerProfile(playerName, UUID.fromString(uuid), false);
- }
-
PreparedStatement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ int id = getUserID(connection, playerName);
+
+ if (id == -1) {
+ // There is no such user
+ if (create) {
+ newUser(playerName, uuid);
+ return loadPlayerNameProfile(playerName, uuid, false, false);
+ }
+
+ // Return unloaded profile if can't create
+ return new PlayerProfile(playerName, false);
+ }
+ // There is such a user
+ writeMissingRows(connection, id);
+
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, s.alchemy, "
@@ -474,25 +737,26 @@ public final class SQLDatabaseManager implements DatabaseManager {
+ "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.UUID = ?");
+ + "WHERE u.uuid = ?");
statement.setString(1, uuid);
- ResultSet result = statement.executeQuery();
+ resultSet = statement.executeQuery();
- if (result.next()) {
+ if (resultSet.next()) {
try {
- PlayerProfile profile = loadFromResult(playerName, result);
- result.close();
+ PlayerProfile profile = loadFromResult(playerName, resultSet);
+ resultSet.close();
+ statement.close();
if (!playerName.isEmpty() && !profile.getPlayerName().isEmpty()) {
statement = connection.prepareStatement(
"UPDATE `" + tablePrefix + "users` "
+ "SET user = ? "
- + "WHERE UUID = ?");
+ + "WHERE uuid = ?");
statement.setString(1, playerName);
statement.setString(2, uuid);
- result = statement.executeQuery();
- result.close();
+ statement.executeUpdate();
+ statement.close();
}
return profile;
@@ -500,12 +764,20 @@ public final class SQLDatabaseManager implements DatabaseManager {
catch (SQLException e) {
}
}
- result.close();
+ resultSet.close();
}
catch (SQLException ex) {
printErrors(ex);
}
finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
if (statement != null) {
try {
statement.close();
@@ -514,6 +786,14 @@ public final class SQLDatabaseManager implements DatabaseManager {
// Ignore
}
}
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
}
// Problem, nothing was returned
@@ -523,34 +803,17 @@ public final class SQLDatabaseManager implements DatabaseManager {
return loadPlayerNameProfile(playerName, uuid, create, true);
}
- // 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, uuid);
- return loadPlayerProfile(playerName, uuid, false, false);
- }
-
- // Return unloaded profile if can't create
- return new PlayerProfile(playerName, false);
- }
- // There is such a user
- writeMissingRows(id);
// Retry, and abort on re-failure
return loadPlayerProfile(playerName, uuid, create, false);
}
public void convertUsers(DatabaseManager destination) {
- if (!checkConnected()) {
- return;
- }
-
PreparedStatement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
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, s.alchemy, "
@@ -564,7 +827,6 @@ public final class SQLDatabaseManager implements DatabaseManager {
+ "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) "
+ "WHERE u.user = ?");
List usernames = getStoredUsers();
- ResultSet resultSet;
int convertedUsers = 0;
long startMillis = System.currentTimeMillis();
for (String playerName : usernames) {
@@ -586,6 +848,14 @@ public final class SQLDatabaseManager implements DatabaseManager {
printErrors(e);
}
finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
if (statement != null) {
try {
statement.close();
@@ -594,19 +864,24 @@ public final class SQLDatabaseManager implements DatabaseManager {
// Ignore
}
}
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
}
}
public boolean saveUserUUID(String userName, UUID uuid) {
- if (!checkConnected()) {
- // return false
- return false;
- }
-
PreparedStatement statement = null;
+ Connection connection = null;
try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
statement = connection.prepareStatement(
"UPDATE `" + tablePrefix + "users` SET "
+ " uuid = ? WHERE user = ?");
@@ -628,20 +903,25 @@ public final class SQLDatabaseManager implements DatabaseManager {
// Ignore
}
}
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
}
-
- // Problem, nothing was returned
}
public boolean saveUserUUIDs(Map fetchedUUIDs) {
- if (!checkConnected()) {
- return false;
- }
-
PreparedStatement statement = null;
int count = 0;
+ Connection connection = null;
+
try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET uuid = ? WHERE user = ?");
for (Map.Entry entry : fetchedUUIDs.entrySet()) {
@@ -677,123 +957,58 @@ public final class SQLDatabaseManager implements DatabaseManager {
// Ignore
}
}
- }
- }
-
- /**
- * 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 attempting a database connection each
- * time McMMO needs the database.
- *
- * @return the boolean value for whether or not we are connected
- */
- public 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) {
+ if (connection != null) {
try {
- isValid = connection.isValid(VALID_TIMEOUT);
+ connection.close();
}
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;
+ // Ignore
}
}
}
-
- // 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;
}
public List getStoredUsers() {
ArrayList users = new ArrayList();
- if (checkConnected()) {
- Statement stmt = null;
- try {
- stmt = connection.createStatement();
- ResultSet result = stmt.executeQuery("SELECT user FROM " + tablePrefix + "users");
- while (result.next()) {
- users.add(result.getString("user"));
+ Statement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
+
+ try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ statement = connection.createStatement();
+ resultSet = statement.executeQuery("SELECT user FROM " + tablePrefix + "users");
+ while (resultSet.next()) {
+ users.add(resultSet.getString("user"));
+ }
+ }
+ catch (SQLException e) {
+ printErrors(e);
+ }
+ finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
}
- result.close();
}
- catch (SQLException e) {
- printErrors(e);
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
}
- finally {
- if (stmt != null) {
- try {
- stmt.close();
- }
- catch (SQLException e) {
- // Ignore
- }
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
}
}
}
@@ -801,122 +1016,114 @@ public final class SQLDatabaseManager implements DatabaseManager {
return users;
}
- /**
- * Attempt to connect to the mySQL database.
- */
- private void connect() {
- connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().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", Config.getInstance().getMySQLUserName());
- connectionProperties.put("password", Config.getInstance().getMySQLUserPassword());
- connectionProperties.put("autoReconnect", "false");
- 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().severe("Connection to MySQL failed!");
- printErrors(ex);
- }
- }
- catch (ClassNotFoundException ex) {
- connection = null;
-
- if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
- mcMMO.p.getLogger().severe("MySQL database driver not found!");
- }
- }
- }
-
/**
* Checks that the database structure is present and correct
*/
private void checkStructure() {
- if (!checkConnected()) {
- return;
+
+ Statement statement = null;
+ Connection connection = null;
+
+ try {
+ connection = connectionPool.getConnection(POOL_FETCH_TIMEOUT);
+ statement = connection.createStatement();
+
+ statement.executeUpdate("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` ("
+ + "`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
+ + "`user` varchar(40) NOT NULL,"
+ + "`uuid` varchar(36) NULL DEFAULT NULL,"
+ + "`lastlogin` int(32) unsigned NOT NULL,"
+ + "PRIMARY KEY (`id`),"
+ + "UNIQUE KEY `user` (`user`),"
+ + "UNIQUE KEY `uuid` (`uuid`)) DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;");
+ statement.executeUpdate("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "huds` ("
+ + "`user_id` int(10) unsigned NOT NULL,"
+ + "`mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "',"
+ + "PRIMARY KEY (`user_id`)) "
+ + "DEFAULT CHARSET=latin1;");
+ statement.executeUpdate("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`)) "
+ + "DEFAULT CHARSET=latin1;");
+ statement.executeUpdate("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',"
+ + "`fishing` int(10) unsigned NOT NULL DEFAULT '0',"
+ + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0',"
+ + "PRIMARY KEY (`user_id`)) "
+ + "DEFAULT CHARSET=latin1;");
+ statement.executeUpdate("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',"
+ + "`fishing` int(10) unsigned NOT NULL DEFAULT '0',"
+ + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0',"
+ + "PRIMARY KEY (`user_id`)) "
+ + "DEFAULT CHARSET=latin1;");
+
+ for (UpgradeType updateType : UpgradeType.values()) {
+ checkDatabaseStructure(connection, updateType);
+ }
+
+ mcMMO.p.getLogger().info("Killing orphans");
+ statement.executeUpdate("DELETE FROM `" + tablePrefix + "experience` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "experience`.`user_id` = `u`.`id`)");
+ statement.executeUpdate("DELETE FROM `" + tablePrefix + "huds` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "huds`.`user_id` = `u`.`id`)");
+ statement.executeUpdate("DELETE FROM `" + tablePrefix + "cooldowns` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "cooldowns`.`user_id` = `u`.`id`)");
+ statement.executeUpdate("DELETE FROM `" + tablePrefix + "skills` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "skills`.`user_id` = `u`.`id`)");
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (connection != null) {
+ try {
+ connection.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
}
- write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` ("
- + "`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
- + "`user` varchar(40) NOT NULL,"
- + "`uuid` varchar(36) NOT NULL DEFAULT '',"
- + "`lastlogin` int(32) unsigned NOT NULL,"
- + "PRIMARY KEY (`id`),"
- + "UNIQUE KEY `user` (`user`)) DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;");
- write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "huds` ("
- + "`user_id` int(10) unsigned NOT NULL,"
- + "`mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "',"
- + "PRIMARY KEY (`user_id`)) "
- + "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`)) "
- + "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',"
- + "`fishing` int(10) unsigned NOT NULL DEFAULT '0',"
- + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0',"
- + "PRIMARY KEY (`user_id`)) "
- + "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',"
- + "`fishing` int(10) unsigned NOT NULL DEFAULT '0',"
- + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0',"
- + "PRIMARY KEY (`user_id`)) "
- + "DEFAULT CHARSET=latin1;");
-
- for (UpgradeType updateType : UpgradeType.values()) {
- checkDatabaseStructure(updateType);
- }
-
- mcMMO.p.getLogger().info("Killing orphans");
- write("DELETE FROM `" + tablePrefix + "experience` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "experience`.`user_id` = `u`.`id`)");
- write("DELETE FROM `" + tablePrefix + "huds` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "huds`.`user_id` = `u`.`id`)");
- write("DELETE FROM `" + tablePrefix + "cooldowns` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "cooldowns`.`user_id` = `u`.`id`)");
- write("DELETE FROM `" + tablePrefix + "skills` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "skills`.`user_id` = `u`.`id`)");
}
/**
@@ -924,11 +1131,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
*
* @param upgrade Upgrade to attempt to apply
*/
- private void checkDatabaseStructure(UpgradeType upgrade) {
- if (!checkConnected()) {
- return;
- }
-
+ private void checkDatabaseStructure(Connection connection, UpgradeType upgrade) {
if (!mcMMO.getUpgradeManager().shouldUpgrade(upgrade)) {
mcMMO.p.debug("Skipping " + upgrade.name() + " upgrade (unneeded)");
return;
@@ -994,162 +1197,7 @@ 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.
- */
- private boolean write(String sql) {
- if (!checkConnected()) {
- return false;
- }
-
- PreparedStatement statement = null;
- try {
- statement = connection.prepareStatement(sql);
- statement.executeUpdate();
- return true;
- }
- catch (SQLException ex) {
- if (!sql.contains("DROP COLUMN")) {
- printErrors(ex);
- }
- return false;
- }
- finally {
- if (statement != null) {
- try {
- statement.close();
- }
- catch (SQLException e) {
- // Ignore
- }
- }
- }
- }
-
- /**
- * 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;
-
- if (checkConnected()) {
- 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) {
- // Ignore
- }
- }
- }
- }
-
- return rows;
- }
-
- /**
- * 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>();
-
- if (checkConnected()) {
- PreparedStatement statement = null;
- ResultSet resultSet;
-
- try {
- statement = connection.prepareStatement(sql);
- resultSet = statement.executeQuery();
-
- while (resultSet.next()) {
- ArrayList column = new ArrayList();
-
- 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) {
- // Ignore
- }
- }
- }
- }
-
- return rows;
- }
-
- /**
- * Get the Integer. Only return first row / first field.
- *
- * @param statement SQL query to execute
- *
- * @return the value in the first row / first field
- */
- private int readInt(PreparedStatement statement) {
- int result = -1;
-
- if (checkConnected()) {
- ResultSet resultSet;
-
- try {
- 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) {
- // Ignore
- }
- }
- }
- }
-
- return result;
- }
-
- private void writeMissingRows(int id) {
+ private void writeMissingRows(Connection connection, int id) {
PreparedStatement statement = null;
try {
@@ -1168,8 +1216,9 @@ public final class SQLDatabaseManager implements DatabaseManager {
statement.execute();
statement.close();
- statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "huds (user_id, mobhealthbar) VALUES (? ,'" + Config.getInstance().getMobHealthbarDefault().name() + "')");
+ statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "huds (user_id, mobhealthbar) VALUES (?, ?)");
statement.setInt(1, id);
+ statement.setString(2, Config.getInstance().getMobHealthbarDefault().name());
statement.execute();
statement.close();
}
@@ -1188,181 +1237,21 @@ public final class SQLDatabaseManager implements DatabaseManager {
}
}
- private void processPurge(Collection> usernames) {
- for (ArrayList user : usernames) {
- Misc.profileCleanup(user.get(0));
- }
- }
-
- private boolean saveIntegers(String sql, int... args) {
- PreparedStatement statement = null;
-
- try {
- statement = connection.prepareStatement(sql);
- int i = 1;
-
- for (int arg : args) {
- statement.setInt(i++, arg);
- }
-
- statement.execute();
- return true;
- }
- catch (SQLException ex) {
- printErrors(ex);
- return false;
- }
- finally {
- if (statement != null) {
- try {
- statement.close();
- }
- catch (SQLException e) {
- // Ignore
- }
- }
- }
- }
-
- private boolean saveLongs(String sql, int id, long... args) {
- PreparedStatement statement = null;
-
- try {
- statement = connection.prepareStatement(sql);
- int i = 1;
-
- for (long arg : args) {
- statement.setLong(i++, arg);
- }
-
- statement.setInt(i++, id);
- statement.execute();
- return true;
- }
- catch (SQLException ex) {
- printErrors(ex);
- return false;
- }
- finally {
- if (statement != null) {
- try {
- statement.close();
- }
- catch (SQLException e) {
- // Ignore
- }
- }
- }
- }
-
- /**
- * Retrieve the database id for a player
- *
- * @param playerName The name of the user to retrieve the id for
- *
- * @return the requested id or -1 if not found
- */
- private int readId(String playerName) {
- int id = -1;
-
- try {
- PreparedStatement statement = connection.prepareStatement("SELECT id FROM " + tablePrefix + "users WHERE user = ?");
- statement.setString(1, playerName);
- id = readInt(statement);
- }
- catch (SQLException ex) {
- printErrors(ex);
- }
-
- return id;
- }
-
- private boolean saveUniqueId(int id, String uuid) {
- PreparedStatement statement = null;
-
- try {
- statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET uuid = ? WHERE id = ?");
- statement.setString(1, uuid);
- statement.setInt(2, id);
- statement.execute();
- return true;
- }
- catch (SQLException ex) {
- printErrors(ex);
- return false;
- }
- finally {
- if (statement != null) {
- try {
- statement.close();
- }
- catch (SQLException e) {
- // Ignore
- }
- }
- }
- }
-
- private boolean saveLogin(int id, long login) {
- PreparedStatement statement = null;
-
- try {
- statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET lastlogin = ? WHERE id = ?");
- statement.setLong(1, login);
- statement.setInt(2, id);
- statement.execute();
- return true;
- }
- catch (SQLException ex) {
- printErrors(ex);
- return false;
- }
- finally {
- if (statement != null) {
- try {
- statement.close();
- }
- catch (SQLException e) {
- // Ignore
- }
- }
- }
- }
-
- private boolean saveHuds(int userId, String mobHealthBar) {
- PreparedStatement statement = null;
-
- try {
- statement = connection.prepareStatement("UPDATE " + tablePrefix + "huds SET mobhealthbar = ? WHERE user_id = ?");
- statement.setString(1, mobHealthBar);
- statement.setInt(2, userId);
- statement.execute();
- return true;
- }
- catch (SQLException ex) {
- printErrors(ex);
- return false;
- }
- finally {
- if (statement != null) {
- try {
- statement.close();
- }
- catch (SQLException e) {
- // Ignore
- }
- }
+ private void processPurge(Collection usernames) {
+ for (String user : usernames) {
+ Misc.profileCleanup(user);
}
}
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
+ Map skills = new EnumMap(SkillType.class); // Skill & Level
+ Map skillsXp = new EnumMap(SkillType.class); // Skill & XP
+ Map skillsDATS = new EnumMap(AbilityType.class); // Ability & Cooldown
MobHealthbarType mobHealthbarType;
UUID uuid;
- final int OFFSET_SKILLS = 0; // TODO update these numbers when the query changes (a new skill is added)
+ final int OFFSET_SKILLS = 0; // TODO update these numbers when the query
+ // changes (a new skill is added)
final int OFFSET_XP = 13;
final int OFFSET_DATS = 26;
final int OFFSET_OTHER = 38;
@@ -1426,6 +1315,8 @@ public final class SQLDatabaseManager implements DatabaseManager {
}
private void printErrors(SQLException ex) {
+ StackTraceElement element = ex.getStackTrace()[0];
+ mcMMO.p.getLogger().severe("Location: " + element.getMethodName() + " " + element.getLineNumber());
mcMMO.p.getLogger().severe("SQLException: " + ex.getMessage());
mcMMO.p.getLogger().severe("SQLState: " + ex.getSQLState());
mcMMO.p.getLogger().severe("VendorError: " + ex.getErrorCode());
@@ -1533,7 +1424,8 @@ public final class SQLDatabaseManager implements DatabaseManager {
if (!column_exists) {
mcMMO.p.getLogger().info("Adding UUIDs to mcMMO MySQL user table...");
- statement.executeUpdate("ALTER TABLE `" + tablePrefix + "users` ADD `uuid` varchar(36) NOT NULL DEFAULT ''");
+ statement.executeUpdate("ALTER TABLE `" + tablePrefix + "users` ADD `uuid` varchar(36) NULL DEFAULT NULL");
+ statement.executeUpdate("ALTER TABLE `" + tablePrefix + "users` ADD UNIQUE INDEX `uuid` (`uuid`) USING BTREE");
}
}
catch (SQLException ex) {
@@ -1551,7 +1443,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
}
try {
- resultSet = statement.executeQuery("SELECT `user` FROM `" + tablePrefix + "users` WHERE `uuid` = ''");
+ resultSet = statement.executeQuery("SELECT `user` FROM `" + tablePrefix + "users` WHERE `uuid` IS NULL");
while (resultSet.next()) {
names.add(resultSet.getString("user"));
@@ -1647,4 +1539,110 @@ public final class SQLDatabaseManager implements DatabaseManager {
}
}
}
+
+ private int getUserID(final Connection connection, final String playerName) {
+ Integer id = cachedUserIDsByName.get(playerName.toLowerCase());
+ if (id != null) {
+ return id;
+ }
+
+ ResultSet resultSet = null;
+ PreparedStatement statement = null;
+
+ try {
+ statement = connection.prepareStatement("SELECT id, uuid FROM " + tablePrefix + "users WHERE user = ?");
+ statement.setString(1, playerName);
+ resultSet = statement.executeQuery();
+
+ if (resultSet.next()) {
+ id = resultSet.getInt("id");
+
+ cachedUserIDsByName.put(playerName.toLowerCase(), id);
+
+ try {
+ cachedUserIDs.put(UUID.fromString(resultSet.getString("uuid")), id);
+ }
+ catch (Exception e) {
+
+ }
+
+ return id;
+ }
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ private int getUserID(final Connection connection, final UUID uuid) {
+ if (cachedUserIDs.containsKey(uuid)) {
+ return cachedUserIDs.get(uuid);
+ }
+
+ ResultSet resultSet = null;
+ PreparedStatement statement = null;
+
+ try {
+ statement = connection.prepareStatement("SELECT id, user FROM " + tablePrefix + "users WHERE uuid = ?");
+ statement.setString(1, uuid.toString());
+ resultSet = statement.executeQuery();
+
+ if (resultSet.next()) {
+ int id = resultSet.getInt("id");
+
+ cachedUserIDs.put(uuid, id);
+ cachedUserIDsByName.put(resultSet.getString("user").toLowerCase(), id);
+
+ return id;
+ }
+ }
+ catch (SQLException ex) {
+ printErrors(ex);
+ }
+ finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ if (statement != null) {
+ try {
+ statement.close();
+ }
+ catch (SQLException e) {
+ // Ignore
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void onDisable() {
+ connectionPool.release();
+ }
}
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 148c802d7..060ce77ba 100644
--- a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java
+++ b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java
@@ -7,12 +7,10 @@ import java.util.UUID;
import org.bukkit.GameMode;
import org.bukkit.Location;
-import org.bukkit.Server;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.FixedMetadataValue;
-import org.bukkit.scheduler.BukkitRunnable;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.config.AdvancedConfig;
@@ -93,13 +91,13 @@ public class McMMOPlayer {
private boolean isUsingUnarmed;
private final FixedMetadataValue playerMetadata;
- public McMMOPlayer(Player player) {
+ public McMMOPlayer(Player player, PlayerProfile profile) {
String playerName = player.getName();
UUID uuid = player.getUniqueId();
this.player = player;
playerMetadata = new FixedMetadataValue(mcMMO.p, playerName);
- profile = mcMMO.getDatabaseManager().loadPlayerProfile(playerName, uuid, true);
+ this.profile = profile;
party = PartyManager.getPlayerParty(playerName);
ptpRecord = new PartyTeleportRecord();
@@ -130,70 +128,6 @@ public class McMMOPlayer {
for (ToolType toolType : ToolType.values()) {
toolMode.put(toolType, false);
}
-
- if (!profile.isLoaded()) {
- mcMMO.p.getLogger().warning("Unable to load the PlayerProfile for " + playerName + ". Will retry over the next several seconds.");
- new RetryProfileLoadingTask().runTaskTimerAsynchronously(mcMMO.p, 11L, 31L);
- }
- }
-
- private class RetryProfileLoadingTask extends BukkitRunnable {
- private static final int MAX_TRIES = 5;
- private final String playerName = McMMOPlayer.this.player.getName();
- private final UUID uniqueId = McMMOPlayer.this.player.getUniqueId();
- private int attempt = 0;
-
- // WARNING: ASYNC TASK
- // DO NOT MODIFY THE McMMOPLAYER FROM THIS CODE
- @Override
- public void run() {
- // Quit if they logged out
- if (!player.isOnline()) {
- mcMMO.p.getLogger().info("Aborting profile loading recovery for " + playerName + " - player logged out");
- this.cancel();
- return;
- }
-
- // Send the message that we're doing the recovery
- if (attempt == 0) {
- player.sendMessage(LocaleLoader.getString("Recovery.Notice"));
- }
-
- // Increment attempt counter and try
- attempt++;
- PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(uniqueId, true);
- // If successful, schedule the apply
- if (profile.isLoaded()) {
- new ApplySuccessfulProfile(profile).runTask(mcMMO.p);
- player.sendMessage(LocaleLoader.getString("Recovery.Success"));
- this.cancel();
- return;
- }
-
- // If we've failed five times, give up
- if (attempt >= MAX_TRIES) {
- mcMMO.p.getLogger().severe("Giving up on attempting to load the PlayerProfile for " + playerName);
- mcMMO.p.getServer().broadcast(LocaleLoader.getString("Recovery.AdminFailureNotice", playerName), Server.BROADCAST_CHANNEL_ADMINISTRATIVE);
- player.sendMessage(LocaleLoader.getString("Recovery.Failure").split("\n"));
- this.cancel();
- return;
- }
- }
- }
-
- private class ApplySuccessfulProfile extends BukkitRunnable {
- private final PlayerProfile profile;
-
- private ApplySuccessfulProfile(PlayerProfile profile) {
- this.profile = profile;
- }
-
- // Synchronized task
- // No database access permitted
- @Override
- public void run() {
- McMMOPlayer.this.profile = profile;
- }
}
public AcrobaticsManager getAcrobaticsManager() {
diff --git a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java
index 1eacb7fcb..faadec0fe 100644
--- a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java
+++ b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java
@@ -41,7 +41,7 @@ import com.gmail.nossr50.datatypes.skills.AbilityType;
import com.gmail.nossr50.datatypes.skills.SkillType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.party.ShareHandler;
-import com.gmail.nossr50.runnables.commands.McScoreboardKeepTask;
+import com.gmail.nossr50.runnables.player.PlayerProfileLoadingTask;
import com.gmail.nossr50.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.skills.fishing.FishingManager;
import com.gmail.nossr50.skills.herbalism.HerbalismManager;
@@ -387,9 +387,7 @@ public class PlayerListener implements Listener {
return;
}
- McMMOPlayer mcMMOPlayer = UserManager.addUser(player);
- mcMMOPlayer.actualizeRespawnATS();
- ScoreboardManager.setupPlayer(player);
+ new PlayerProfileLoadingTask(player).runTaskTimerAsynchronously(mcMMO.p, 1, 20); // 1 Tick delay to ensure the player is marked as online before we begin loading
if (Config.getInstance().getMOTDEnabled() && Permissions.motd(player)) {
Motd.displayAll(player);
@@ -403,11 +401,6 @@ public class PlayerListener implements Listener {
player.sendMessage(LocaleLoader.getString("UpdateChecker.Outdated"));
player.sendMessage(LocaleLoader.getString("UpdateChecker.NewAvailable"));
}
-
- if (Config.getInstance().getShowStatsAfterLogin()) {
- ScoreboardManager.enablePlayerStatsScoreboard(player);
- new McScoreboardKeepTask(player).runTaskLater(mcMMO.p, 1 * Misc.TICK_CONVERSION_FACTOR);
- }
}
/**
diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java
index d4ea83366..350cb8ec7 100644
--- a/src/main/java/com/gmail/nossr50/mcMMO.java
+++ b/src/main/java/com/gmail/nossr50/mcMMO.java
@@ -38,6 +38,7 @@ import com.gmail.nossr50.runnables.UpdaterResultAsyncTask;
import com.gmail.nossr50.runnables.backups.CleanBackupsTask;
import com.gmail.nossr50.runnables.database.UserPurgeTask;
import com.gmail.nossr50.runnables.party.PartyAutoKickTask;
+import com.gmail.nossr50.runnables.player.PlayerProfileLoadingTask;
import com.gmail.nossr50.runnables.player.PowerLevelUpdatingTask;
import com.gmail.nossr50.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.skills.alchemy.Alchemy;
@@ -167,8 +168,7 @@ public class mcMMO extends JavaPlugin {
holidayManager = new HolidayManager();
for (Player player : getServer().getOnlinePlayers()) {
- UserManager.addUser(player); // In case of reload add all users back into UserManager
- ScoreboardManager.setupPlayer(player);
+ new PlayerProfileLoadingTask(player).runTaskTimerAsynchronously(mcMMO.p, 1, 20); // 1 Tick delay to ensure the player is marked as online before we begin loading
}
debug("Version " + getDescription().getVersion() + " is enabled!");
@@ -242,6 +242,7 @@ public class mcMMO extends JavaPlugin {
}
}
+ databaseManager.onDisable();
debug("Was disabled."); // How informative!
}
diff --git a/src/main/java/com/gmail/nossr50/runnables/database/SQLDatabaseKeepaliveTask.java b/src/main/java/com/gmail/nossr50/runnables/database/SQLDatabaseKeepaliveTask.java
deleted file mode 100644
index 96d301026..000000000
--- a/src/main/java/com/gmail/nossr50/runnables/database/SQLDatabaseKeepaliveTask.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.gmail.nossr50.runnables.database;
-
-import java.lang.ref.WeakReference;
-
-import org.bukkit.scheduler.BukkitRunnable;
-
-import com.gmail.nossr50.database.SQLDatabaseManager;
-
-/**
- * This task is in charge of sending a MySQL ping over the MySQL connection
- * every hour to prevent the connection from timing out and losing players'
- * data when they join.
- *
- * A WeakReference is used to keep the database instance, because
- * {@link com.gmail.nossr50.commands.database.ConvertDatabaseCommand database
- * conversion} may create a SQLDatabaseManager that will be thrown out. If a
- * normal reference was used, the conversion would cause a combined data and
- * resource leak through this task.
- */
-public class SQLDatabaseKeepaliveTask extends BukkitRunnable {
- WeakReference databaseInstance;
-
- public SQLDatabaseKeepaliveTask(SQLDatabaseManager dbman) {
- databaseInstance = new WeakReference(dbman);
- }
-
- public void run() {
- SQLDatabaseManager dbman = databaseInstance.get();
- if (dbman != null) {
- dbman.checkConnected();
- }
- else {
- // This happens when the database was started for a conversion,
- // or discarded by its creator for any other reason. If this code
- // was not present, we would leak the connection resources.
- this.cancel();
- }
- }
-}
diff --git a/src/main/java/com/gmail/nossr50/runnables/database/SQLReconnectTask.java b/src/main/java/com/gmail/nossr50/runnables/database/SQLReconnectTask.java
deleted file mode 100644
index d60591d3b..000000000
--- a/src/main/java/com/gmail/nossr50/runnables/database/SQLReconnectTask.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.gmail.nossr50.runnables.database;
-
-import org.bukkit.entity.Player;
-import org.bukkit.scheduler.BukkitRunnable;
-
-import com.gmail.nossr50.mcMMO;
-import com.gmail.nossr50.database.SQLDatabaseManager;
-import com.gmail.nossr50.util.player.UserManager;
-
-public class SQLReconnectTask extends BukkitRunnable {
- @Override
- public void run() {
- if (((SQLDatabaseManager) mcMMO.getDatabaseManager()).checkConnected()) {
- UserManager.saveAll(); // Save all profiles
- UserManager.clearAll(); // Clear the profiles
-
- for (Player player : mcMMO.p.getServer().getOnlinePlayers()) {
- UserManager.addUser(player); // Add in new profiles, forcing them to 'load' again from MySQL
- }
- }
- }
-}
diff --git a/src/main/java/com/gmail/nossr50/runnables/database/UUIDFetcherRunnable.java b/src/main/java/com/gmail/nossr50/runnables/database/UUIDFetcherRunnable.java
deleted file mode 100644
index 485e0f855..000000000
--- a/src/main/java/com/gmail/nossr50/runnables/database/UUIDFetcherRunnable.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.gmail.nossr50.runnables.database;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.UUID;
-
-import org.bukkit.scheduler.BukkitRunnable;
-
-import com.gmail.nossr50.mcMMO;
-import com.gmail.nossr50.util.uuid.UUIDFetcher;
-
-public class UUIDFetcherRunnable extends BukkitRunnable {
- private List names;
-
- public UUIDFetcherRunnable(List names) {
- this.names = names;
- }
-
- public UUIDFetcherRunnable(String name) {
- this.names = new ArrayList();
- this.names.add(name);
- }
-
- @Override
- public void run() {
- try {
- Map returns = new UUIDFetcher(this.names).call();
- new CacheReturnedNames(returns).runTask(mcMMO.p);
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private class CacheReturnedNames extends BukkitRunnable {
- private Map returns;
-
- public CacheReturnedNames(Map returns) {
- this.returns = returns;
- }
-
- @Override
- public void run() {
- for (Entry entry : this.returns.entrySet()) {
- mcMMO.getDatabaseManager().saveUserUUID(entry.getKey(), entry.getValue());
- }
- }
- }
-}
diff --git a/src/main/java/com/gmail/nossr50/runnables/player/PlayerProfileLoadingTask.java b/src/main/java/com/gmail/nossr50/runnables/player/PlayerProfileLoadingTask.java
new file mode 100644
index 000000000..a8ed02ffa
--- /dev/null
+++ b/src/main/java/com/gmail/nossr50/runnables/player/PlayerProfileLoadingTask.java
@@ -0,0 +1,108 @@
+package com.gmail.nossr50.runnables.player;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.bukkit.Server;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+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.locale.LocaleLoader;
+import com.gmail.nossr50.runnables.commands.McScoreboardKeepTask;
+import com.gmail.nossr50.util.Misc;
+import com.gmail.nossr50.util.player.UserManager;
+import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
+
+public class PlayerProfileLoadingTask extends BukkitRunnable {
+ private static final int MAX_TRIES = 5;
+ private final Player player;
+ private int attempt = 0;
+ private ReentrantLock lock = new ReentrantLock();
+ private boolean cancelled = false;
+
+ public PlayerProfileLoadingTask(Player player) {
+ this.player = player;
+ }
+
+ // WARNING: ASYNC TASK
+ // DO NOT MODIFY THE McMMOPLAYER FROM THIS CODE
+ @Override
+ public void run() {
+ lock.lock();
+
+ if (this.cancelled) {
+ return;
+ }
+
+ // Quit if they logged out
+ if (!player.isOnline()) {
+ mcMMO.p.getLogger().info("Aborting profile loading recovery for " + player.getName() + " - player logged out");
+ this.cancel();
+ cancelled = true;
+ lock.unlock();
+ return;
+ }
+
+ // Increment attempt counter and try
+ attempt++;
+
+ PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(player.getName(), player.getUniqueId(), true);
+ // If successful, schedule the apply
+ if (profile.isLoaded()) {
+ new ApplySuccessfulProfile(profile).runTask(mcMMO.p);
+ this.cancel();
+ cancelled = true;
+ lock.unlock();
+ return;
+ }
+
+ // If we've failed five times, give up
+ if (attempt >= MAX_TRIES) {
+ mcMMO.p.getLogger().severe("Giving up on attempting to load the PlayerProfile for " + player.getName());
+ mcMMO.p.getServer().broadcast(LocaleLoader.getString("Profile.Loading.AdminFailureNotice", player.getName()), Server.BROADCAST_CHANNEL_ADMINISTRATIVE);
+ player.sendMessage(LocaleLoader.getString("Profile.Loading.Failure").split("\n"));
+ this.cancel();
+ cancelled = true;
+ lock.unlock();
+ return;
+ }
+ lock.unlock();
+ }
+
+ private class ApplySuccessfulProfile extends BukkitRunnable {
+ private final PlayerProfile profile;
+
+ private ApplySuccessfulProfile(PlayerProfile profile) {
+ this.profile = profile;
+ }
+
+ // Synchronized task
+ // No database access permitted
+ @Override
+ public void run() {
+ if (!player.isOnline()) {
+ mcMMO.p.getLogger().info("Aborting profile loading recovery for " + player.getName() + " - player logged out");
+ return;
+ }
+
+ McMMOPlayer mcMMOPlayer = new McMMOPlayer(player, profile);
+ UserManager.track(mcMMOPlayer);
+ mcMMOPlayer.actualizeRespawnATS();
+ ScoreboardManager.setupPlayer(player);
+
+ if (Config.getInstance().getShowProfileLoadedMessage()) {
+ player.sendMessage(LocaleLoader.getString("Profile.Loading.Success"));
+ }
+
+ if (Config.getInstance().getShowStatsAfterLogin()) {
+ ScoreboardManager.enablePlayerStatsScoreboard(player);
+ new McScoreboardKeepTask(player).runTaskLater(mcMMO.p, 1 * Misc.TICK_CONVERSION_FACTOR);
+ }
+ }
+ }
+}
+
+
diff --git a/src/main/java/com/gmail/nossr50/util/Misc.java b/src/main/java/com/gmail/nossr50/util/Misc.java
index 1196fa63f..7c3e63d02 100644
--- a/src/main/java/com/gmail/nossr50/util/Misc.java
+++ b/src/main/java/com/gmail/nossr50/util/Misc.java
@@ -13,6 +13,7 @@ import org.bukkit.inventory.ItemStack;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.events.items.McMMOItemSpawnEvent;
+import com.gmail.nossr50.runnables.player.PlayerProfileLoadingTask;
import com.gmail.nossr50.util.player.UserManager;
public final class Misc {
@@ -112,7 +113,7 @@ public final class Misc {
if (player != null) {
UserManager.remove(player);
- UserManager.addUser(player);
+ new PlayerProfileLoadingTask(player).runTaskTimerAsynchronously(mcMMO.p, 1, 20); // 1 Tick delay to ensure the player is marked as online before we begin loading
}
}
diff --git a/src/main/java/com/gmail/nossr50/util/player/UserManager.java b/src/main/java/com/gmail/nossr50/util/player/UserManager.java
index 10923a2eb..61e704818 100644
--- a/src/main/java/com/gmail/nossr50/util/player/UserManager.java
+++ b/src/main/java/com/gmail/nossr50/util/player/UserManager.java
@@ -18,16 +18,12 @@ public final class UserManager {
private UserManager() {}
/**
- * Add a new user.
+ * Track a new user.
*
- * @param player The player to create a user record for
- * @return the player's {@link McMMOPlayer} object
+ * @param mcMMOPlayer the player profile to start tracking
*/
- public static McMMOPlayer addUser(Player player) {
- McMMOPlayer mcMMOPlayer = new McMMOPlayer(player);
- player.setMetadata(mcMMO.playerDataKey, new FixedMetadataValue(mcMMO.p, mcMMOPlayer));
-
- return mcMMOPlayer;
+ public static void track(McMMOPlayer mcMMOPlayer) {
+ mcMMOPlayer.getPlayer().setMetadata(mcMMO.playerDataKey, new FixedMetadataValue(mcMMO.p, mcMMOPlayer));
}
/**
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 4b0826838..aee39c6f2 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -10,6 +10,8 @@
General:
Locale: en_US
MOTD_Enabled: true
+ # Send a message to the player when his profile was successfully loaded
+ Show_Profile_Loaded: false
# Amount of time (in minutes) to wait between saves of player information
Save_Interval: 10
# Allow mcMMO to report on basic anonymous usage
@@ -122,6 +124,12 @@ MySQL:
User_Password: UserPassword
Name: DataBaseName
TablePrefix: mcmmo_
+ # This setting is the max simultaneous mysql connections allowed at a time, needs to be
+ # high enough to support multiple player logins in quick succession
+ MaxConnections: 30
+ # This setting is the max size of the pool of cached connections that we hold available
+ # at any given time
+ MaxPoolSize: 20
Server:
Port: 3306
Address: localhost
diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties
index 34f36eece..7b894fa84 100644
--- a/src/main/resources/locale/locale_en_US.properties
+++ b/src/main/resources/locale/locale_en_US.properties
@@ -960,7 +960,6 @@ Scoreboard.Misc.Cooldown=[[LIGHT_PURPLE]]Cooldown
Scoreboard.Misc.Overall=[[GOLD]]Overall
#DATABASE RECOVERY
-Recovery.Notice=[[RED]]Notice: mcMMO was [[DARK_RED]]unable to load your data.[[RED]] Retrying 5 times...
-Recovery.Success=[[GREEN]]Success! Your mcMMO data was loaded.
-Recovery.Failure=[[RED]]mcMMO still cannot load your data. You may want to [[AQUA]]contact the server owner.\n[[YELLOW]]You can still play on the server, but you will have [[BOLD]]no mcMMO levels[[YELLOW]] and any XP you get [[BOLD]]will not be saved[[YELLOW]].
-Recovery.AdminFailureNotice=[[DARK_RED]][A][[RED]] mcMMO was unable to load the player data for [[YELLOW]]{0}[[RED]]. [[LIGHT_PURPLE]]Please inspect your database setup.
+Profile.Loading.Success=[[GREEN]]Your mcMMO profile has been loaded.
+Profile.Loading.Failure=[[RED]]mcMMO still cannot load your data. You may want to [[AQUA]]contact the server owner.\n[[YELLOW]]You can still play on the server, but you will have [[BOLD]]no mcMMO levels[[YELLOW]] and any XP you get [[BOLD]]will not be saved[[YELLOW]].
+Profile.Loading.AdminFailureNotice=[[DARK_RED]][A][[RED]] mcMMO was unable to load the player data for [[YELLOW]]{0}[[RED]]. [[LIGHT_PURPLE]]Please inspect your database setup.