浏览代码

Update SQL Queries + SQL DB Schema

nossr50 4 年之前
父节点
当前提交
17137f3a08

+ 9 - 3
src/main/java/com/gmail/nossr50/commands/database/ConvertDatabaseCommand.java

@@ -52,12 +52,18 @@ public class ConvertDatabaseCommand implements CommandExecutor {
             mcMMO.getUserManager().saveAllSync();
             mcMMO.getUserManager().clearAll();
 
+            if(oldDatabase == null) {
+                sender.sendMessage("Could not load the other database, failed to convert users.");
+                return true;
+            }
+
             for (Player player : mcMMO.p.getServer().getOnlinePlayers()) {
                 PlayerProfile profile = oldDatabase.loadPlayerProfile(player.getUniqueId());
 
-                if (profile.isLoaded()) {
-                    mcMMO.getDatabaseManager().saveUser(profile);
-                }
+                if(profile == null)
+                    continue;
+
+                mcMMO.getUserManager().saveUserImmediately(profile.getPersistentPlayerData(), true);
 
                 new PlayerProfileLoadingTask(player).runTaskLaterAsynchronously(mcMMO.p, 1); // 1 Tick delay to ensure the player is marked as online before we begin loading
             }

+ 412 - 214
src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java

@@ -1,22 +1,24 @@
 package com.gmail.nossr50.database;
 
-import com.gmail.nossr50.config.AdvancedConfig;
 import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.datatypes.MobHealthbarType;
+import com.gmail.nossr50.datatypes.MobHealthBarType;
 import com.gmail.nossr50.datatypes.database.DatabaseType;
 import com.gmail.nossr50.datatypes.database.PlayerStat;
 import com.gmail.nossr50.datatypes.database.UpgradeType;
-import com.gmail.nossr50.datatypes.player.PlayerProfile;
-import com.gmail.nossr50.datatypes.player.UniqueDataType;
+import com.gmail.nossr50.datatypes.player.*;
 import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
 import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.runnables.database.UUIDUpdateAsyncTask;
 import com.gmail.nossr50.util.Misc;
-import com.gmail.nossr50.util.experience.ExperienceBarManager;
+import com.gmail.nossr50.util.experience.MMOExperienceBarManager;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import org.apache.tomcat.jdbc.pool.DataSource;
 import org.apache.tomcat.jdbc.pool.PoolProperties;
+import org.bukkit.entity.Player;
 import org.bukkit.scheduler.BukkitRunnable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.sql.*;
 import java.util.*;
@@ -127,7 +129,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
                     + "taming = 0 AND mining = 0 AND woodcutting = 0 AND repair = 0 "
                     + "AND unarmed = 0 AND herbalism = 0 AND excavation = 0 AND "
                     + "archery = 0 AND swords = 0 AND axes = 0 AND acrobatics = 0 "
-                    + "AND fishing = 0 AND alchemy = 0;");
+                    + "AND fishing = 0 AND alchemy = 0 AND tridents = 0 AND crossbows = 0;");
 
             statement.executeUpdate("DELETE FROM `" + tablePrefix + "experience` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "skills` `s` WHERE `" + tablePrefix + "experience`.`user_id` = `s`.`user_id`)");
             statement.executeUpdate("DELETE FROM `" + tablePrefix + "huds` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "skills` `s` WHERE `" + tablePrefix + "huds`.`user_id` = `s`.`user_id`)");
@@ -218,7 +220,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
         cachedUserIDs.remove(uuid);
     }
 
-    public boolean saveUser(PlayerProfile profile) {
+    public boolean saveUser(MMODataSnapshot dataSnapshot) {
         boolean success = true;
         PreparedStatement statement = null;
         Connection connection = null;
@@ -226,22 +228,22 @@ public final class SQLDatabaseManager implements DatabaseManager {
         try {
             connection = getConnection(PoolIdentifier.SAVE);
 
-            int id = getUserID(connection, profile.getPlayerName(), profile.getUniqueId());
+            int id = getUserID(connection, dataSnapshot.getPlayerName(), dataSnapshot.getPlayerUUID());
 
-            if (id == -1) {
-                id = newUser(connection, profile.getPlayerName(), profile.getUniqueId());
-                if (id == -1) {
-                    mcMMO.p.getLogger().severe("Failed to create new account for " + profile.getPlayerName());
-                    return false;
-                }
-            }
+//            if (id == -1) {
+//                id = newUser(connection, dataSnapshot.getPlayerName(), dataSnapshot.getPlayerUUID());
+//                if (id == -1) {
+//                    mcMMO.p.getLogger().severe("Failed to create new account for " + dataSnapshot.getPlayerName());
+//                    return false;
+//                }
+//            }
 
             statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET lastlogin = UNIX_TIMESTAMP() WHERE id = ?");
             statement.setInt(1, id);
             success &= (statement.executeUpdate() != 0);
             statement.close();
             if (!success) {
-                mcMMO.p.getLogger().severe("Failed to update last login for " + profile.getPlayerName());
+                mcMMO.p.getLogger().severe("Failed to update last login for " + dataSnapshot.getPlayerName());
                 return false;
             }
 
@@ -249,29 +251,31 @@ public final class SQLDatabaseManager implements DatabaseManager {
                     + " taming = ?, mining = ?, repair = ?, woodcutting = ?"
                     + ", unarmed = ?, herbalism = ?, excavation = ?"
                     + ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
-                    + ", fishing = ?, alchemy = ?, total = ? WHERE user_id = ?");
-            statement.setInt(1, profile.getSkillLevel(PrimarySkillType.TAMING));
-            statement.setInt(2, profile.getSkillLevel(PrimarySkillType.MINING));
-            statement.setInt(3, profile.getSkillLevel(PrimarySkillType.REPAIR));
-            statement.setInt(4, profile.getSkillLevel(PrimarySkillType.WOODCUTTING));
-            statement.setInt(5, profile.getSkillLevel(PrimarySkillType.UNARMED));
-            statement.setInt(6, profile.getSkillLevel(PrimarySkillType.HERBALISM));
-            statement.setInt(7, profile.getSkillLevel(PrimarySkillType.EXCAVATION));
-            statement.setInt(8, profile.getSkillLevel(PrimarySkillType.ARCHERY));
-            statement.setInt(9, profile.getSkillLevel(PrimarySkillType.SWORDS));
-            statement.setInt(10, profile.getSkillLevel(PrimarySkillType.AXES));
-            statement.setInt(11, profile.getSkillLevel(PrimarySkillType.ACROBATICS));
-            statement.setInt(12, profile.getSkillLevel(PrimarySkillType.FISHING));
-            statement.setInt(13, profile.getSkillLevel(PrimarySkillType.ALCHEMY));
+                    + ", fishing = ?, alchemy = ?, tridents = ?, crossbows = ?, total = ? WHERE user_id = ?");
+            statement.setInt(1, dataSnapshot.getSkillLevel(PrimarySkillType.TAMING));
+            statement.setInt(2, dataSnapshot.getSkillLevel(PrimarySkillType.MINING));
+            statement.setInt(3, dataSnapshot.getSkillLevel(PrimarySkillType.REPAIR));
+            statement.setInt(4, dataSnapshot.getSkillLevel(PrimarySkillType.WOODCUTTING));
+            statement.setInt(5, dataSnapshot.getSkillLevel(PrimarySkillType.UNARMED));
+            statement.setInt(6, dataSnapshot.getSkillLevel(PrimarySkillType.HERBALISM));
+            statement.setInt(7, dataSnapshot.getSkillLevel(PrimarySkillType.EXCAVATION));
+            statement.setInt(8, dataSnapshot.getSkillLevel(PrimarySkillType.ARCHERY));
+            statement.setInt(9, dataSnapshot.getSkillLevel(PrimarySkillType.SWORDS));
+            statement.setInt(10, dataSnapshot.getSkillLevel(PrimarySkillType.AXES));
+            statement.setInt(11, dataSnapshot.getSkillLevel(PrimarySkillType.ACROBATICS));
+            statement.setInt(12, dataSnapshot.getSkillLevel(PrimarySkillType.FISHING));
+            statement.setInt(13, dataSnapshot.getSkillLevel(PrimarySkillType.ALCHEMY));
+            statement.setInt(14, dataSnapshot.getSkillLevel(PrimarySkillType.TRIDENTS));
+            statement.setInt(15, dataSnapshot.getSkillLevel(PrimarySkillType.CROSSBOWS));
             int total = 0;
             for (PrimarySkillType primarySkillType : PrimarySkillType.NON_CHILD_SKILLS)
-                total += profile.getSkillLevel(primarySkillType);
-            statement.setInt(14, total);
-            statement.setInt(15, id);
+                total += dataSnapshot.getSkillLevel(primarySkillType);
+            statement.setInt(16, total);
+            statement.setInt(17, id);
             success &= (statement.executeUpdate() != 0);
             statement.close();
             if (!success) {
-                mcMMO.p.getLogger().severe("Failed to update skills for " + profile.getPlayerName());
+                mcMMO.p.getLogger().severe("Failed to update skills for " + dataSnapshot.getPlayerName());
                 return false;
             }
 
@@ -279,59 +283,112 @@ public final class SQLDatabaseManager implements DatabaseManager {
                     + " taming = ?, mining = ?, repair = ?, woodcutting = ?"
                     + ", unarmed = ?, herbalism = ?, excavation = ?"
                     + ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
-                    + ", fishing = ?, alchemy = ? WHERE user_id = ?");
-            statement.setInt(1, profile.getSkillXpLevel(PrimarySkillType.TAMING));
-            statement.setInt(2, profile.getSkillXpLevel(PrimarySkillType.MINING));
-            statement.setInt(3, profile.getSkillXpLevel(PrimarySkillType.REPAIR));
-            statement.setInt(4, profile.getSkillXpLevel(PrimarySkillType.WOODCUTTING));
-            statement.setInt(5, profile.getSkillXpLevel(PrimarySkillType.UNARMED));
-            statement.setInt(6, profile.getSkillXpLevel(PrimarySkillType.HERBALISM));
-            statement.setInt(7, profile.getSkillXpLevel(PrimarySkillType.EXCAVATION));
-            statement.setInt(8, profile.getSkillXpLevel(PrimarySkillType.ARCHERY));
-            statement.setInt(9, profile.getSkillXpLevel(PrimarySkillType.SWORDS));
-            statement.setInt(10, profile.getSkillXpLevel(PrimarySkillType.AXES));
-            statement.setInt(11, profile.getSkillXpLevel(PrimarySkillType.ACROBATICS));
-            statement.setInt(12, profile.getSkillXpLevel(PrimarySkillType.FISHING));
-            statement.setInt(13, profile.getSkillXpLevel(PrimarySkillType.ALCHEMY));
-            statement.setInt(14, id);
+                    + ", fishing = ?, alchemy = ?, tridents = ?, crossbows = ?, WHERE user_id = ?");
+            statement.setInt(1, dataSnapshot.getSkillXpLevel(PrimarySkillType.TAMING));
+            statement.setInt(2, dataSnapshot.getSkillXpLevel(PrimarySkillType.MINING));
+            statement.setInt(3, dataSnapshot.getSkillXpLevel(PrimarySkillType.REPAIR));
+            statement.setInt(4, dataSnapshot.getSkillXpLevel(PrimarySkillType.WOODCUTTING));
+            statement.setInt(5, dataSnapshot.getSkillXpLevel(PrimarySkillType.UNARMED));
+            statement.setInt(6, dataSnapshot.getSkillXpLevel(PrimarySkillType.HERBALISM));
+            statement.setInt(7, dataSnapshot.getSkillXpLevel(PrimarySkillType.EXCAVATION));
+            statement.setInt(8, dataSnapshot.getSkillXpLevel(PrimarySkillType.ARCHERY));
+            statement.setInt(9, dataSnapshot.getSkillXpLevel(PrimarySkillType.SWORDS));
+            statement.setInt(10, dataSnapshot.getSkillXpLevel(PrimarySkillType.AXES));
+            statement.setInt(11, dataSnapshot.getSkillXpLevel(PrimarySkillType.ACROBATICS));
+            statement.setInt(12, dataSnapshot.getSkillXpLevel(PrimarySkillType.FISHING));
+            statement.setInt(13, dataSnapshot.getSkillXpLevel(PrimarySkillType.ALCHEMY));
+            statement.setInt(14, dataSnapshot.getSkillXpLevel(PrimarySkillType.TRIDENTS));
+            statement.setInt(15, dataSnapshot.getSkillXpLevel(PrimarySkillType.CROSSBOWS));
+            statement.setInt(16, id);
             success &= (statement.executeUpdate() != 0);
             statement.close();
             if (!success) {
-                mcMMO.p.getLogger().severe("Failed to update experience for " + profile.getPlayerName());
+                mcMMO.p.getLogger().severe("Failed to update experience for " + dataSnapshot.getPlayerName());
                 return false;
             }
 
             statement = connection.prepareStatement("UPDATE " + tablePrefix + "cooldowns SET "
                     + "  mining = ?, woodcutting = ?, unarmed = ?"
                     + ", herbalism = ?, excavation = ?, swords = ?"
-                    + ", axes = ?, blast_mining = ?, chimaera_wing = ? WHERE user_id = ?");
-            statement.setLong(1, profile.getAbilityDATS(SuperAbilityType.SUPER_BREAKER));
-            statement.setLong(2, profile.getAbilityDATS(SuperAbilityType.TREE_FELLER));
-            statement.setLong(3, profile.getAbilityDATS(SuperAbilityType.BERSERK));
-            statement.setLong(4, profile.getAbilityDATS(SuperAbilityType.GREEN_TERRA));
-            statement.setLong(5, profile.getAbilityDATS(SuperAbilityType.GIGA_DRILL_BREAKER));
-            statement.setLong(6, profile.getAbilityDATS(SuperAbilityType.SERRATED_STRIKES));
-            statement.setLong(7, profile.getAbilityDATS(SuperAbilityType.SKULL_SPLITTER));
-            statement.setLong(8, profile.getAbilityDATS(SuperAbilityType.BLAST_MINING));
-            statement.setLong(9, profile.getUniqueData(UniqueDataType.CHIMAERA_WING_DATS));
-            statement.setInt(10, id);
+                    + ", axes = ?, blast_mining = ?, chimaera_wing = ?, archery = ?, tridents = ?, crossbows = ? WHERE user_id = ?");
+            statement.setLong(1, dataSnapshot.getAbilityDATS(SuperAbilityType.SUPER_BREAKER));
+            statement.setLong(2, dataSnapshot.getAbilityDATS(SuperAbilityType.TREE_FELLER));
+            statement.setLong(3, dataSnapshot.getAbilityDATS(SuperAbilityType.BERSERK));
+            statement.setLong(4, dataSnapshot.getAbilityDATS(SuperAbilityType.GREEN_TERRA));
+            statement.setLong(5, dataSnapshot.getAbilityDATS(SuperAbilityType.GIGA_DRILL_BREAKER));
+            statement.setLong(6, dataSnapshot.getAbilityDATS(SuperAbilityType.SERRATED_STRIKES));
+            statement.setLong(7, dataSnapshot.getAbilityDATS(SuperAbilityType.SKULL_SPLITTER));
+            statement.setLong(8, dataSnapshot.getAbilityDATS(SuperAbilityType.BLAST_MINING));
+            statement.setLong(9, dataSnapshot.getUniqueData(UniqueDataType.CHIMAERA_WING_DATS));
+            statement.setLong(10, dataSnapshot.getAbilityDATS(SuperAbilityType.ARCHERY_SUPER));
+            statement.setLong(11, dataSnapshot.getAbilityDATS(SuperAbilityType.TRIDENT_SUPER));
+            statement.setLong(12, dataSnapshot.getAbilityDATS(SuperAbilityType.SUPER_SHOTGUN));
+            statement.setInt(13, id);
             success = (statement.executeUpdate() != 0);
             statement.close();
             if (!success) {
-                mcMMO.p.getLogger().severe("Failed to update cooldowns for " + profile.getPlayerName());
+                mcMMO.p.getLogger().severe("Failed to update cooldowns for " + dataSnapshot.getPlayerName());
                 return false;
             }
 
             statement = connection.prepareStatement("UPDATE " + tablePrefix + "huds SET mobhealthbar = ?, scoreboardtips = ? WHERE user_id = ?");
-            statement.setString(1, profile.getMobHealthbarType() == null ? Config.getInstance().getMobHealthbarDefault().name() : profile.getMobHealthbarType().name());
-            statement.setInt(2, profile.getScoreboardTipsShown());
+            statement.setString(1, dataSnapshot.getMobHealthBarType() == null ? Config.getInstance().getMobHealthbarDefault().name() : dataSnapshot.getMobHealthBarType().name());
+            statement.setInt(2, dataSnapshot.getScoreboardTipsShown());
             statement.setInt(3, id);
             success = (statement.executeUpdate() != 0);
             statement.close();
             if (!success) {
-                mcMMO.p.getLogger().severe("Failed to update hud settings for " + profile.getPlayerName());
+                mcMMO.p.getLogger().severe("Failed to update hud settings for " + dataSnapshot.getPlayerName());
                 return false;
             }
+
+            //XP BAR STUFF
+
+            statement = connection.prepareStatement("UPDATE " + tablePrefix + "xpbar SET "
+                    + " view_taming = ?, view_mining = ?, view_repair = ?, view_woodcutting = ?"
+                    + ", view_unarmed = ?, view_herbalism = ?, view_excavation = ?"
+                    + ", view_archery = ?, view_swords = ?, view_axes = ?, view_acrobatics = ?"
+                    + ", view_fishing = ?, view_alchemy = ?, view_salvage = ?, view_smelting = ?, view_tridents = ?, view_crossbows = ? WHERE user_id = ?");
+            statement.setString(1, dataSnapshot.getBarStateMap().get(PrimarySkillType.TAMING).toString());
+            statement.setString(2, dataSnapshot.getBarStateMap().get(PrimarySkillType.MINING).toString());
+            statement.setString(3, dataSnapshot.getBarStateMap().get(PrimarySkillType.REPAIR).toString());
+            statement.setString(4, dataSnapshot.getBarStateMap().get(PrimarySkillType.WOODCUTTING).toString());
+            statement.setString(5, dataSnapshot.getBarStateMap().get(PrimarySkillType.UNARMED).toString());
+            statement.setString(6, dataSnapshot.getBarStateMap().get(PrimarySkillType.HERBALISM).toString());
+            statement.setString(7, dataSnapshot.getBarStateMap().get(PrimarySkillType.EXCAVATION).toString());
+            statement.setString(8, dataSnapshot.getBarStateMap().get(PrimarySkillType.ARCHERY).toString());
+            statement.setString(9, dataSnapshot.getBarStateMap().get(PrimarySkillType.SWORDS).toString());
+            statement.setString(10, dataSnapshot.getBarStateMap().get(PrimarySkillType.AXES).toString());
+            statement.setString(11, dataSnapshot.getBarStateMap().get(PrimarySkillType.ACROBATICS).toString());
+            statement.setString(12, dataSnapshot.getBarStateMap().get(PrimarySkillType.FISHING).toString());
+            statement.setString(13, dataSnapshot.getBarStateMap().get(PrimarySkillType.ALCHEMY).toString());
+            statement.setString(14, dataSnapshot.getBarStateMap().get(PrimarySkillType.SALVAGE).toString());
+            statement.setString(15, dataSnapshot.getBarStateMap().get(PrimarySkillType.SMELTING).toString());
+            statement.setString(16, dataSnapshot.getBarStateMap().get(PrimarySkillType.TRIDENTS).toString());
+            statement.setString(17, dataSnapshot.getBarStateMap().get(PrimarySkillType.CROSSBOWS).toString());
+            statement.setInt(18, id);
+            success &= (statement.executeUpdate() != 0);
+            statement.close();
+            if (!success) {
+                mcMMO.p.getLogger().severe("Failed to update XP bar views for " + dataSnapshot.getPlayerName());
+                return false;
+            }
+
+
+            //TOGGLES
+
+            statement = connection.prepareStatement("UPDATE " + tablePrefix + "toggle SET "
+                    + " chatspy = ?, rankless = ? WHERE user_id = ?");
+            statement.setBoolean(1, dataSnapshot.getPartyChatSpying());
+            statement.setBoolean(2, dataSnapshot.isLeaderBoardExcluded());
+            statement.setInt(3, id);
+            success &= (statement.executeUpdate() != 0);
+            statement.close();
+            if (!success) {
+                mcMMO.p.getLogger().severe("Failed to update user toggles for " + dataSnapshot.getPlayerName());
+                return false;
+            }
+
         }
         catch (SQLException ex) {
             printErrors(ex);
@@ -354,7 +411,9 @@ public final class SQLDatabaseManager implements DatabaseManager {
 
         try {
             connection = getConnection(PoolIdentifier.MISC);
-            statement = connection.prepareStatement("SELECT " + query + ", user FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 AND NOT user = '\\_INVALID\\_OLD\\_USERNAME\\_' ORDER BY " + query + " DESC, user LIMIT ?, ?");
+            statement = connection.prepareStatement("SELECT " + query + ", user FROM " + tablePrefix + "users JOIN " + tablePrefix
+                    + "skills ON (user_id = id) WHERE " + query + " > 0 AND NOT user = '\\_INVALID\\_OLD\\_USERNAME\\_' ORDER BY "
+                    + query + " DESC, user LIMIT ?, ?");
             statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage);
             statement.setInt(2, statsPerPage);
             resultSet = statement.executeQuery();
@@ -530,55 +589,51 @@ public final class SQLDatabaseManager implements DatabaseManager {
         return -1;
     }
 
-    @Deprecated
-    public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
-        return loadPlayerProfile(playerName, null, false, true);
+//    @Deprecated
+//    public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
+//        return loadPlayerProfile(playerName, null);
+//    }
+
+    public @Nullable PlayerProfile loadPlayerProfile(UUID uuid) {
+        return loadPlayerProfile(null, "", uuid);
+    }
+
+    public @Nullable PlayerProfile loadPlayerProfile(String playerName) {
+        return loadPlayerProfile(null, playerName, null);
     }
 
-    public PlayerProfile loadPlayerProfile(UUID uuid) {
-        return loadPlayerProfile("", uuid, false, true);
+    public @Nullable PlayerProfile loadPlayerProfile(String playerName, UUID uuid, boolean create) {
+        return loadPlayerProfile(null, playerName, uuid);
     }
 
-    public PlayerProfile loadPlayerProfile(String playerName, UUID uuid, boolean create) {
-        return loadPlayerProfile(playerName, uuid, create, true);
+    public @NotNull PlayerProfile loadPlayerProfile(Player player) {
+        return loadPlayerProfile(player, player.getName(), player.getUniqueId());
     }
 
-    private PlayerProfile loadPlayerProfile(String playerName, UUID uuid, boolean create, boolean retry) {
+    private @Nullable PlayerProfile loadPlayerProfile(@Nullable Player player, @NotNull String playerName, @Nullable UUID playerUUID) {
         PreparedStatement statement = null;
         Connection connection = null;
         ResultSet resultSet = null;
 
         try {
             connection = getConnection(PoolIdentifier.LOAD);
-            int id = getUserID(connection, playerName, uuid);
+            int id = getUserID(connection, playerName, playerUUID);
 
             if (id == -1) {
                 // There is no such user
-                if (create) {
-                    id = newUser(connection, playerName, uuid);
-                    create = false;
+                if (player != null) {
+                    id = newUser(connection, playerName, playerUUID);
                     if (id == -1) {
-                        return new PlayerProfile(playerName, false);
+                        return null;
                     }
                 } else {
-                    return new PlayerProfile(playerName, false);
+                    return null;
                 }
             }
             // 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, "
-                            + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, "
-                            + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, c.chimaera_wing, "
-                            + "h.mobhealthbar, h.scoreboardtips, u.uuid, u.user "
-                            + "FROM " + tablePrefix + "users u "
-                            + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) "
-                            + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) "
-                            + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) "
-                            + "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) "
-                            + "WHERE u.id = ?");
+            statement = getUserData(connection);
             statement.setInt(1, id);
 
             resultSet = statement.executeQuery();
@@ -590,7 +645,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
                     resultSet.close();
                     statement.close();
 
-                    if (!playerName.isEmpty() && !playerName.equalsIgnoreCase(name) && uuid != null) {
+                    if (!playerName.isEmpty() && !playerName.equalsIgnoreCase(name) && playerUUID != null) {
                         statement = connection.prepareStatement(
                                 "UPDATE `" + tablePrefix + "users` "
                                         + "SET user = ? "
@@ -604,7 +659,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
                                         + "SET user = ?, uuid = ? "
                                         + "WHERE id = ?");
                         statement.setString(1, playerName);
-                        statement.setString(2, uuid.toString());
+                        statement.setString(2, playerUUID.toString());
                         statement.setInt(3, id);
                         statement.executeUpdate();
                         statement.close();
@@ -627,15 +682,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
             tryClose(connection);
         }
 
-        // Problem, nothing was returned
-
-        // return unloaded profile
-        if (!retry) {
-            return new PlayerProfile(playerName, false);
-        }
-
-        // Retry, and abort on re-failure
-        return loadPlayerProfile(playerName, uuid, create, false);
+        return null;
     }
 
     public void convertUsers(DatabaseManager destination) {
@@ -645,18 +692,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
 
         try {
             connection = getConnection(PoolIdentifier.MISC);
-            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, "
-                            + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, "
-                            + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, c.chimaera_wing, "
-                            + "h.mobhealthbar, h.scoreboardtips, u.uuid "
-                            + "FROM " + tablePrefix + "users u "
-                            + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) "
-                            + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) "
-                            + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) "
-                            + "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) "
-                            + "WHERE u.user = ?");
+            statement = getUserData(connection);
             List<String> usernames = getStoredUsers();
             int convertedUsers = 0;
             long startMillis = System.currentTimeMillis();
@@ -665,7 +701,15 @@ public final class SQLDatabaseManager implements DatabaseManager {
                 try {
                     resultSet = statement.executeQuery();
                     resultSet.next();
-                    destination.saveUser(loadFromResult(playerName, resultSet));
+                    PlayerProfile playerProfile = loadFromResult(playerName, resultSet);
+
+                    if(playerProfile == null)
+                        continue;
+
+                    PersistentPlayerData persistentPlayerData = playerProfile.getPersistentPlayerData();
+
+                    MMODataSnapshot mmoDataSnapshot = mcMMO.getUserManager().createPlayerDataSnapshot(persistentPlayerData);
+                    destination.saveUser(mmoDataSnapshot);
                     resultSet.close();
                 }
                 catch (SQLException e) {
@@ -687,6 +731,26 @@ public final class SQLDatabaseManager implements DatabaseManager {
 
     }
 
+    private @NotNull PreparedStatement getUserData(@NotNull Connection connection) throws SQLException {
+        return 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, s.tridents, s.crossbows, "
+                        + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, e.tridents, e.crossbows, "
+                        + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, c.chimaera_wing, c.tridents, c.crossbows, "
+                        + "h.mobhealthbar, h.scoreboardtips, u.uuid "
+                        + "x.view_taming, x.view_mining, x.view_repair, x.view_woodcutting, x.view_unarmed, x.view_herbalism, x.view_excavation, x.view_archery, x.view_swords, x.view_axes, x.view_acrobatics, x.view_salvage, x.view_smelting, x.view_tridents, x.view_crossbows, "
+                        + "t.chatspy, t.rankless, "
+                        + "FROM " + tablePrefix + "users u "
+                        + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) "
+                        + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) "
+                        + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) "
+                        + "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) "
+                        + "JOIN " + tablePrefix + "xpbar x ON (u.id = x.user_id) "
+                        + "JOIN " + tablePrefix + "toggle t ON (u.id = t.user_id) "
+                        + "WHERE u.user = ?");
+    }
+
+
     public boolean saveUserUUID(String userName, UUID uuid) {
         PreparedStatement statement = null;
         Connection connection = null;
@@ -711,45 +775,45 @@ public final class SQLDatabaseManager implements DatabaseManager {
         }
     }
 
-    public boolean saveUserUUIDs(Map<String, UUID> fetchedUUIDs) {
-        PreparedStatement statement = null;
-        int count = 0;
-
-        Connection connection = null;
-
-        try {
-            connection = getConnection(PoolIdentifier.MISC);
-            statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET uuid = ? WHERE user = ?");
-
-            for (Map.Entry<String, UUID> entry : fetchedUUIDs.entrySet()) {
-                statement.setString(1, entry.getValue().toString());
-                statement.setString(2, entry.getKey());
-
-                statement.addBatch();
-
-                count++;
-
-                if ((count % 500) == 0) {
-                    statement.executeBatch();
-                    count = 0;
-                }
-            }
-
-            if (count != 0) {
-                statement.executeBatch();
-            }
-
-            return true;
-        }
-        catch (SQLException ex) {
-            printErrors(ex);
-            return false;
-        }
-        finally {
-            tryClose(statement);
-            tryClose(connection);
-        }
-    }
+//    public boolean saveUserUUIDs(Map<String, UUID> fetchedUUIDs) {
+//        PreparedStatement statement = null;
+//        int count = 0;
+//
+//        Connection connection = null;
+//
+//        try {
+//            connection = getConnection(PoolIdentifier.MISC);
+//            statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET uuid = ? WHERE user = ?");
+//
+//            for (Map.Entry<String, UUID> entry : fetchedUUIDs.entrySet()) {
+//                statement.setString(1, entry.getValue().toString());
+//                statement.setString(2, entry.getKey());
+//
+//                statement.addBatch();
+//
+//                count++;
+//
+//                if ((count % 500) == 0) {
+//                    statement.executeBatch();
+//                    count = 0;
+//                }
+//            }
+//
+//            if (count != 0) {
+//                statement.executeBatch();
+//            }
+//
+//            return true;
+//        }
+//        catch (SQLException ex) {
+//            printErrors(ex);
+//            return false;
+//        }
+//        finally {
+//            tryClose(statement);
+//            tryClose(connection);
+//        }
+//    }
 
     public List<String> getStoredUsers() {
         ArrayList<String> users = new ArrayList<>();
@@ -843,6 +907,8 @@ public final class SQLDatabaseManager implements DatabaseManager {
                         + "`acrobatics` int(32) unsigned NOT NULL DEFAULT '0',"
                         + "`blast_mining` int(32) unsigned NOT NULL DEFAULT '0',"
                         + "`chimaera_wing` int(32) unsigned NOT NULL DEFAULT '0',"
+                        + "`tridents` int(32) unsigned NOT NULL DEFAULT '0',"
+                        + "`crossbows` int(32) unsigned NOT NULL DEFAULT '0',"
                         + "PRIMARY KEY (`user_id`)) "
                         + "DEFAULT CHARSET=latin1;");
                 tryClose(createStatement);
@@ -852,25 +918,25 @@ public final class SQLDatabaseManager implements DatabaseManager {
             statement.setString(2, tablePrefix + "skills");
             resultSet = statement.executeQuery();
             if (!resultSet.next()) {
-                String startingLevel = "'" + AdvancedConfig.getInstance().getStartingLevel() + "'";
-                String totalLevel = "'" + (AdvancedConfig.getInstance().getStartingLevel() * (PrimarySkillType.values().length - PrimarySkillType.CHILD_SKILLS.size())) + "'";
                 createStatement = connection.createStatement();
                 createStatement.executeUpdate("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "skills` ("
                         + "`user_id` int(10) unsigned NOT NULL,"
-                        + "`taming` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`mining` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`woodcutting` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`repair` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`unarmed` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`herbalism` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`excavation` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`archery` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`swords` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`axes` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`acrobatics` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`fishing` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`alchemy` int(10) unsigned NOT NULL DEFAULT "+startingLevel+","
-                        + "`total` int(10) unsigned NOT NULL DEFAULT "+totalLevel+","
+                        + "`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',"
+                        + "`tridents` int(10) unsigned NOT NULL DEFAULT '0',"
+                        + "`crossbows` int(10) unsigned NOT NULL DEFAULT '0',"
+                        + "`total` int(10) unsigned NOT NULL DEFAULT '0',"
                         + "PRIMARY KEY (`user_id`)) "
                         + "DEFAULT CHARSET=latin1;");
                 tryClose(createStatement);
@@ -896,6 +962,55 @@ public final class SQLDatabaseManager implements DatabaseManager {
                         + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0',"
                         + "`fishing` int(10) unsigned NOT NULL DEFAULT '0',"
                         + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0',"
+                        + "`tridents` int(10) unsigned NOT NULL DEFAULT '0',"
+                        + "`crossbows` int(10) unsigned NOT NULL DEFAULT '0',"
+                        + "PRIMARY KEY (`user_id`)) "
+                        + "DEFAULT CHARSET=latin1;");
+                tryClose(createStatement);
+            }
+            tryClose(resultSet);
+            tryClose(statement);
+
+            //Toggle Table
+            statement.setString(1, Config.getInstance().getMySQLDatabaseName());
+            statement.setString(2, tablePrefix + "toggle");
+            resultSet = statement.executeQuery();
+            if (!resultSet.next()) {
+                createStatement = connection.createStatement();
+                createStatement.executeUpdate("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "toggle` ("
+                        + "`user_id` int(10) unsigned NOT NULL,"
+                        + "`chatspy` bit NOT NULL DEFAULT '0',"
+                        + "`rankless` bit NOT NULL DEFAULT '0',"
+                        + "PRIMARY KEY (`user_id`)) "
+                        + "DEFAULT CHARSET=latin1;");
+                tryClose(createStatement);
+            }
+            tryClose(resultSet);
+            tryClose(statement);
+
+            //XP Bar Table
+            statement.setString(1, Config.getInstance().getMySQLDatabaseName());
+            statement.setString(2, tablePrefix + "xpbar");
+            resultSet = statement.executeQuery();
+            if (!resultSet.next()) {
+                createStatement = connection.createStatement();
+                createStatement.executeUpdate("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "xpbar` ("
+                        + "`user_id` int(10) unsigned NOT NULL,"
+                        + "`view_taming` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_mining` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_woodcutting` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_repair` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_unarmed` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_herbalism` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_excavation` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_archery` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_swords` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_axes` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_acrobatics` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_salvage` varchar(40) NOT NULL DEFAULT 'DISABLED',"
+                        + "`view_smelting` varchar(40) NOT NULL DEFAULT 'DISABLED',"
+                        + "`view_tridents` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+                        + "`view_crossbows` varchar(40) NOT NULL DEFAULT 'NORMAL',"
                         + "PRIMARY KEY (`user_id`)) "
                         + "DEFAULT CHARSET=latin1;");
                 tryClose(createStatement);
@@ -924,6 +1039,8 @@ public final class SQLDatabaseManager implements DatabaseManager {
             createStatement.executeUpdate("DELETE FROM `" + tablePrefix + "huds` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "huds`.`user_id` = `u`.`id`)");
             createStatement.executeUpdate("DELETE FROM `" + tablePrefix + "cooldowns` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "cooldowns`.`user_id` = `u`.`id`)");
             createStatement.executeUpdate("DELETE FROM `" + tablePrefix + "skills` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "skills`.`user_id` = `u`.`id`)");
+            createStatement.executeUpdate("DELETE FROM `" + tablePrefix + "toggle` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "skills`.`user_id` = `u`.`id`)");
+            createStatement.executeUpdate("DELETE FROM `" + tablePrefix + "xpbar` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "skills`.`user_id` = `u`.`id`)");
         }
         catch (SQLException ex) {
             printErrors(ex);
@@ -1004,7 +1121,6 @@ public final class SQLDatabaseManager implements DatabaseManager {
                 case ADD_UUIDS:
                     checkUpgradeAddUUIDs(statement);
                     return;
-
                 case ADD_SCOREBOARD_TIPS:
                     checkUpgradeAddScoreboardTips(statement);
                     return;
@@ -1019,7 +1135,9 @@ public final class SQLDatabaseManager implements DatabaseManager {
                 case ADD_UNIQUE_PLAYER_DATA:
                     checkUpgradeAddUniqueChimaeraWing(statement);
                     break;
-
+                case ADD_SQL_2_2:
+                    checkUpgradeAddTridentsAndCrossbowsSQL(statement);
+                    break;
                 default:
                     break;
 
@@ -1069,22 +1187,26 @@ public final class SQLDatabaseManager implements DatabaseManager {
         }
     }
 
-    private PlayerProfile loadFromResult(String playerName, ResultSet result) throws SQLException {
-        Map<PrimarySkillType, Integer> skills = new EnumMap<PrimarySkillType, Integer>(PrimarySkillType.class); // Skill & Level
-        Map<PrimarySkillType, Float> skillsXp = new EnumMap<PrimarySkillType, Float>(PrimarySkillType.class); // Skill & XP
-        Map<SuperAbilityType, Integer> skillsDATS = new EnumMap<SuperAbilityType, Integer>(SuperAbilityType.class); // Ability & Cooldown
-        Map<UniqueDataType, Integer> uniqueData = new EnumMap<UniqueDataType, Integer>(UniqueDataType.class); //Chimaera wing cooldown and other misc info
-        Map<PrimarySkillType, ExperienceBarManager.BarState> xpBarStateMap = new EnumMap<PrimarySkillType, ExperienceBarManager.BarState>(PrimarySkillType.class);
+    private @Nullable PlayerProfile loadFromResult(String playerName, ResultSet result) throws SQLException {
+        PersistentPlayerDataBuilder persistentPlayerDataBuilder = new PersistentPlayerDataBuilder();
+        EnumMap<PrimarySkillType, Integer> skills = new EnumMap<PrimarySkillType, Integer>(PrimarySkillType.class); // Skill & Level
+        EnumMap<PrimarySkillType, Float> skillsXp = new EnumMap<PrimarySkillType, Float>(PrimarySkillType.class); // Skill & XP
+        EnumMap<SuperAbilityType, Integer> skillsDATS = new EnumMap<SuperAbilityType, Integer>(SuperAbilityType.class); // Ability & Cooldown
+        EnumMap<UniqueDataType, Integer> uniqueData = new EnumMap<UniqueDataType, Integer>(UniqueDataType.class); //Chimaera wing cooldown and other misc info
+        EnumMap<PrimarySkillType, MMOExperienceBarManager.BarState> xpBarStateMap = new EnumMap<PrimarySkillType, MMOExperienceBarManager.BarState>(PrimarySkillType.class);
 
-        MobHealthbarType mobHealthbarType;
+        MobHealthBarType mobHealthbarType;
         UUID uuid;
         int scoreboardTipsShown;
 
+        final int parentSkills = 15, allSkills = 17, cooldownCount = 17, toggleCount = 2, otherCount = 4;
         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 = 39;
+        final int OFFSET_EXPERIENCE = OFFSET_SKILLS + parentSkills;
+        final int OFFSET_COOLDOWNS = OFFSET_EXPERIENCE + parentSkills;
+        final int OFFSET_OTHER = OFFSET_COOLDOWNS + cooldownCount;
+        final int OFFSET_XPBAR = OFFSET_OTHER + otherCount;
+        final int OFFSET_TOGGLE = OFFSET_XPBAR + allSkills;
 
         skills.put(PrimarySkillType.TAMING, result.getInt(OFFSET_SKILLS + 1));
         skills.put(PrimarySkillType.MINING, result.getInt(OFFSET_SKILLS + 2));
@@ -1099,38 +1221,46 @@ public final class SQLDatabaseManager implements DatabaseManager {
         skills.put(PrimarySkillType.ACROBATICS, result.getInt(OFFSET_SKILLS + 11));
         skills.put(PrimarySkillType.FISHING, result.getInt(OFFSET_SKILLS + 12));
         skills.put(PrimarySkillType.ALCHEMY, result.getInt(OFFSET_SKILLS + 13));
-
-        skillsXp.put(PrimarySkillType.TAMING, result.getFloat(OFFSET_XP + 1));
-        skillsXp.put(PrimarySkillType.MINING, result.getFloat(OFFSET_XP + 2));
-        skillsXp.put(PrimarySkillType.REPAIR, result.getFloat(OFFSET_XP + 3));
-        skillsXp.put(PrimarySkillType.WOODCUTTING, result.getFloat(OFFSET_XP + 4));
-        skillsXp.put(PrimarySkillType.UNARMED, result.getFloat(OFFSET_XP + 5));
-        skillsXp.put(PrimarySkillType.HERBALISM, result.getFloat(OFFSET_XP + 6));
-        skillsXp.put(PrimarySkillType.EXCAVATION, result.getFloat(OFFSET_XP + 7));
-        skillsXp.put(PrimarySkillType.ARCHERY, result.getFloat(OFFSET_XP + 8));
-        skillsXp.put(PrimarySkillType.SWORDS, result.getFloat(OFFSET_XP + 9));
-        skillsXp.put(PrimarySkillType.AXES, result.getFloat(OFFSET_XP + 10));
-        skillsXp.put(PrimarySkillType.ACROBATICS, result.getFloat(OFFSET_XP + 11));
-        skillsXp.put(PrimarySkillType.FISHING, result.getFloat(OFFSET_XP + 12));
-        skillsXp.put(PrimarySkillType.ALCHEMY, result.getFloat(OFFSET_XP + 13));
-
-        // Taming - Unused - result.getInt(OFFSET_DATS + 1)
-        skillsDATS.put(SuperAbilityType.SUPER_BREAKER, result.getInt(OFFSET_DATS + 2));
-        // Repair - Unused - result.getInt(OFFSET_DATS + 3)
-        skillsDATS.put(SuperAbilityType.TREE_FELLER, result.getInt(OFFSET_DATS + 4));
-        skillsDATS.put(SuperAbilityType.BERSERK, result.getInt(OFFSET_DATS + 5));
-        skillsDATS.put(SuperAbilityType.GREEN_TERRA, result.getInt(OFFSET_DATS + 6));
-        skillsDATS.put(SuperAbilityType.GIGA_DRILL_BREAKER, result.getInt(OFFSET_DATS + 7));
-        // Archery - Unused - result.getInt(OFFSET_DATS + 8)
-        skillsDATS.put(SuperAbilityType.SERRATED_STRIKES, result.getInt(OFFSET_DATS + 9));
-        skillsDATS.put(SuperAbilityType.SKULL_SPLITTER, result.getInt(OFFSET_DATS + 10));
-        // Acrobatics - Unused - result.getInt(OFFSET_DATS + 11)
-        skillsDATS.put(SuperAbilityType.BLAST_MINING, result.getInt(OFFSET_DATS + 12));
-        uniqueData.put(UniqueDataType.CHIMAERA_WING_DATS, result.getInt(OFFSET_DATS + 13));
+        skills.put(PrimarySkillType.TRIDENTS, result.getInt(OFFSET_SKILLS + 14));
+        skills.put(PrimarySkillType.CROSSBOWS, result.getInt(OFFSET_SKILLS + 15));
+
+        skillsXp.put(PrimarySkillType.TAMING, result.getFloat(OFFSET_EXPERIENCE + 1));
+        skillsXp.put(PrimarySkillType.MINING, result.getFloat(OFFSET_EXPERIENCE + 2));
+        skillsXp.put(PrimarySkillType.REPAIR, result.getFloat(OFFSET_EXPERIENCE + 3));
+        skillsXp.put(PrimarySkillType.WOODCUTTING, result.getFloat(OFFSET_EXPERIENCE + 4));
+        skillsXp.put(PrimarySkillType.UNARMED, result.getFloat(OFFSET_EXPERIENCE + 5));
+        skillsXp.put(PrimarySkillType.HERBALISM, result.getFloat(OFFSET_EXPERIENCE + 6));
+        skillsXp.put(PrimarySkillType.EXCAVATION, result.getFloat(OFFSET_EXPERIENCE + 7));
+        skillsXp.put(PrimarySkillType.ARCHERY, result.getFloat(OFFSET_EXPERIENCE + 8));
+        skillsXp.put(PrimarySkillType.SWORDS, result.getFloat(OFFSET_EXPERIENCE + 9));
+        skillsXp.put(PrimarySkillType.AXES, result.getFloat(OFFSET_EXPERIENCE + 10));
+        skillsXp.put(PrimarySkillType.ACROBATICS, result.getFloat(OFFSET_EXPERIENCE + 11));
+        skillsXp.put(PrimarySkillType.FISHING, result.getFloat(OFFSET_EXPERIENCE + 12));
+        skillsXp.put(PrimarySkillType.ALCHEMY, result.getFloat(OFFSET_EXPERIENCE + 13));
+        skillsXp.put(PrimarySkillType.TRIDENTS, result.getFloat(OFFSET_EXPERIENCE + 14));
+        skillsXp.put(PrimarySkillType.CROSSBOWS, result.getFloat(OFFSET_EXPERIENCE + 15));
+
+        // Taming - Unused - result.getInt(OFFSET_COOLDOWNS + 1)
+        skillsDATS.put(SuperAbilityType.SUPER_BREAKER, result.getInt(OFFSET_COOLDOWNS + 2));
+        // Repair - Unused - result.getInt(OFFSET_COOLDOWNS + 3)
+        skillsDATS.put(SuperAbilityType.TREE_FELLER, result.getInt(OFFSET_COOLDOWNS + 4));
+        skillsDATS.put(SuperAbilityType.BERSERK, result.getInt(OFFSET_COOLDOWNS + 5));
+        skillsDATS.put(SuperAbilityType.GREEN_TERRA, result.getInt(OFFSET_COOLDOWNS + 6));
+        skillsDATS.put(SuperAbilityType.GIGA_DRILL_BREAKER, result.getInt(OFFSET_COOLDOWNS + 7));
+        skillsDATS.put(SuperAbilityType.ARCHERY_SUPER, result.getInt(OFFSET_COOLDOWNS + 8));
+        skillsDATS.put(SuperAbilityType.SERRATED_STRIKES, result.getInt(OFFSET_COOLDOWNS + 9));
+        skillsDATS.put(SuperAbilityType.SKULL_SPLITTER, result.getInt(OFFSET_COOLDOWNS + 10));
+        // Acrobatics - Unused - result.getInt(OFFSET_COOLDOWNS + 11)
+        skillsDATS.put(SuperAbilityType.BLAST_MINING, result.getInt(OFFSET_COOLDOWNS + 12));
+        uniqueData.put(UniqueDataType.CHIMAERA_WING_DATS, result.getInt(OFFSET_COOLDOWNS + 13));
+        skillsDATS.put(SuperAbilityType.TRIDENT_SUPER, result.getInt(OFFSET_COOLDOWNS + 14));
+        skillsDATS.put(SuperAbilityType.SUPER_SHOTGUN, result.getInt(OFFSET_COOLDOWNS + 15));
+
+        //
 
 
         try {
-            mobHealthbarType = MobHealthbarType.valueOf(result.getString(OFFSET_OTHER + 1));
+            mobHealthbarType = MobHealthBarType.valueOf(result.getString(OFFSET_OTHER + 1));
         }
         catch (Exception e) {
             mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
@@ -1147,10 +1277,56 @@ public final class SQLDatabaseManager implements DatabaseManager {
             uuid = UUID.fromString(result.getString(OFFSET_OTHER + 3));
         }
         catch (Exception e) {
-            uuid = null;
+            return null;
+        }
+
+        //XPBAR
+        xpBarStateMap.put(PrimarySkillType.TAMING, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 1)));
+        xpBarStateMap.put(PrimarySkillType.MINING, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 2)));
+        xpBarStateMap.put(PrimarySkillType.REPAIR, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 3)));
+        xpBarStateMap.put(PrimarySkillType.WOODCUTTING, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 4)));
+        xpBarStateMap.put(PrimarySkillType.UNARMED, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 5)));
+        xpBarStateMap.put(PrimarySkillType.HERBALISM, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 6)));
+        xpBarStateMap.put(PrimarySkillType.EXCAVATION, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 7)));
+        xpBarStateMap.put(PrimarySkillType.ARCHERY, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 8)));
+        xpBarStateMap.put(PrimarySkillType.SWORDS, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 9)));
+        xpBarStateMap.put(PrimarySkillType.AXES, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 10)));
+        xpBarStateMap.put(PrimarySkillType.ACROBATICS, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 11)));
+        xpBarStateMap.put(PrimarySkillType.FISHING, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 12)));
+        xpBarStateMap.put(PrimarySkillType.ALCHEMY, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 13)));
+        xpBarStateMap.put(PrimarySkillType.SALVAGE, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 14)));
+        xpBarStateMap.put(PrimarySkillType.SMELTING, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 15)));
+        xpBarStateMap.put(PrimarySkillType.TRIDENTS, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 16)));
+        xpBarStateMap.put(PrimarySkillType.CROSSBOWS, SkillUtils.asBarState(result.getString(OFFSET_XPBAR + 17)));
+
+        //TOGGLE
+        boolean chatSpy = result.getBoolean(OFFSET_TOGGLE+1);
+        boolean rankLess = result.getBoolean(OFFSET_TOGGLE+2);
+
+        //Build
+        persistentPlayerDataBuilder.setSkillLevelValues(skills)
+                .setSkillExperienceValues(skillsXp)
+                .setAbilityDeactivationTimestamps(skillsDATS)
+                .setUniquePlayerData(uniqueData)
+                .setBarStateMap(xpBarStateMap)
+                .setPlayerUUID(uuid)
+                .setPlayerName(playerName)
+                .setPartyChatSpying(chatSpy)
+                .setLastLogin(0) //TODO: Program this in properly
+                .setScoreboardTipsShown(scoreboardTipsShown)
+                .setMobHealthBarType(mobHealthbarType)
+                .setLeaderBoardExemption(rankLess);
+
+        PersistentPlayerData persistentPlayerData;
+
+        try {
+            persistentPlayerData = persistentPlayerDataBuilder.build();
+            return new PlayerProfile(persistentPlayerData);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
         }
 
-        return new PlayerProfile(playerName, uuid, skills, skillsXp, skillsDATS, mobHealthbarType, scoreboardTipsShown, uniqueData, xpBarStateMap);
     }
 
     private void printErrors(SQLException ex) {
@@ -1191,6 +1367,23 @@ public final class SQLDatabaseManager implements DatabaseManager {
         }
     }
 
+    private void checkUpgradeAddTridentsAndCrossbowsSQL(final Statement statement) throws SQLException {
+        try {
+            statement.executeQuery("SELECT 'tridents' FROM `" + tablePrefix +"cooldowns` LIMIT 1");
+        } catch (SQLException ex) {
+            mcMMO.p.getLogger().info("Updating SQL DB tables for 2.2 Update....");
+
+            statement.executeUpdate("ALTER TABLE `" + tablePrefix + "cooldowns` ADD `tridents` int(10) NOT NULL DEFAULT '0'");
+            statement.executeUpdate("ALTER TABLE `" + tablePrefix + "cooldowns` ADD `crossbows` int(10) NOT NULL DEFAULT '0'");
+
+            statement.executeUpdate("ALTER TABLE `" + tablePrefix + "skills` ADD `tridents` int(10) NOT NULL DEFAULT '0'");
+            statement.executeUpdate("ALTER TABLE `" + tablePrefix + "skills` ADD `crossbows` int(10) NOT NULL DEFAULT '0'");
+
+            statement.executeUpdate("ALTER TABLE `" + tablePrefix + "experience` ADD `tridents` int(10) NOT NULL DEFAULT '0'");
+            statement.executeUpdate("ALTER TABLE `" + tablePrefix + "experience` ADD `crossbows` int(10) NOT NULL DEFAULT '0'");
+        }
+    }
+
     private void checkUpgradeAddAlchemy(final Statement statement) throws SQLException {
         try {
             statement.executeQuery("SELECT `alchemy` FROM `" + tablePrefix + "skills` LIMIT 1");
@@ -1548,4 +1741,9 @@ public final class SQLDatabaseManager implements DatabaseManager {
             tryClose(connection);
         }
     }
+
+    @Override
+    public void removeCache(UUID uuid) {
+        cachedUserIDs.remove(uuid);
+    }
 }

+ 1 - 0
src/main/java/com/gmail/nossr50/datatypes/database/UpgradeType.java

@@ -14,6 +14,7 @@ public enum UpgradeType {
     DROP_NAME_UNIQUENESS,
     ADD_SKILL_TOTAL,
     ADD_UNIQUE_PLAYER_DATA,
+    ADD_SQL_2_2,
     FIX_SPELLING_NETHERITE_SALVAGE,
     FIX_SPELLING_NETHERITE_REPAIR,
     FIX_NETHERITE_SALVAGE_QUANTITIES

+ 7 - 0
src/main/java/com/gmail/nossr50/datatypes/player/MMODataSnapshot.java

@@ -29,6 +29,7 @@ public class MMODataSnapshot {
 
     /* Special Flags */
     private final @NotNull Boolean partyChatSpying;
+    private final @NotNull Boolean leaderBoardExclusion;
 
     /* Scoreboards */
     private final @NotNull Integer scoreboardTipsShown;
@@ -48,6 +49,8 @@ public class MMODataSnapshot {
         barStateMap = ImmutableMap.copyOf(persistentPlayerData.getBarStateMap());
 
         partyChatSpying = persistentPlayerData.isPartyChatSpying();
+        leaderBoardExclusion = persistentPlayerData.isLeaderBoardExcluded();
+
         scoreboardTipsShown = persistentPlayerData.getScoreboardTipsShown();
     }
 
@@ -110,4 +113,8 @@ public class MMODataSnapshot {
     public long getUniqueData(UniqueDataType uniqueDataType) {
         return uniquePlayerData.getOrDefault(uniqueDataType, 0);
     }
+
+    public Boolean isLeaderBoardExcluded() {
+        return leaderBoardExclusion;
+    }
 }

+ 8 - 0
src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java

@@ -133,4 +133,12 @@ public class PlayerProfile {
     public @NotNull CooldownManager getCooldownManager() {
         return cooldownManager;
     }
+
+    /**
+     * Whether or not this player is excluded from leaderboards
+     * @return true if the player is excluded from leaderboards
+     */
+    public boolean isLeaderBoardExcluded() {
+        return persistentPlayerData.isLeaderBoardExcluded();
+    }
 }

+ 0 - 1
src/main/java/com/gmail/nossr50/listeners/SelfListener.java

@@ -10,7 +10,6 @@ import com.gmail.nossr50.events.experience.McMMOPlayerXpGainEvent;
 import com.gmail.nossr50.events.skills.abilities.McMMOPlayerAbilityActivateEvent;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.util.player.PlayerLevelUtils;
-import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
 import com.gmail.nossr50.util.skills.RankUtils;
 import com.gmail.nossr50.worldguard.WorldGuardManager;

+ 1 - 1
src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java

@@ -15,8 +15,8 @@ import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.util.ItemUtils;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.StringUtils;
-import com.gmail.nossr50.util.experience.MMOExperienceBarManager;
 import com.gmail.nossr50.util.compat.layers.persistentdata.AbstractPersistentDataLayer;
+import com.gmail.nossr50.util.experience.MMOExperienceBarManager;
 import com.gmail.nossr50.util.player.NotificationManager;
 import org.bukkit.Bukkit;
 import org.bukkit.Location;

+ 1 - 0
src/main/resources/upgrades.yml

@@ -10,3 +10,4 @@ Upgrades_Finished:
     FIX_SPELLING_NETHERITE_SALVAGE: false
     FIX_SPELLING_NETHERITE_REPAIR: false
     FIX_NETHERITE_SALVAGE_QUANTITIES: false
+    ADD_SQL_2_2: false