Browse Source

Add a newUser test

nossr50 4 years ago
parent
commit
d9e195f63a

+ 2 - 0
Changelog.txt

@@ -5,6 +5,8 @@ Version 2.1.189
     Rewrote how FlatFileDatabase verifies data integrity
     (API) Added com.gmail.nossr50.database.DatabaseManager.loadPlayerProfile(org.bukkit.OfflinePlayer)
     (API) Deprecated com.gmail.nossr50.database.DatabaseManager.loadPlayerProfile(java.util.UUID, java.lang.String)
+    (API) Removed com.gmail.nossr50.database.DatabaseManager.newUser(java.lang.String, java.util.UUID)
+    (API) Added
     Added unit tests for FlatFileDatabaseManager (see notes)
     Fixed a bug where FlatFileDatabaseManager didn't properly upgrade older database entries to the newest schema
     The setting to disable the mcMMO user block tracker has been moved from our "hidden config" to persistent_data.yml

+ 6 - 0
pom.xml

@@ -220,6 +220,12 @@
         <!-- ... -->
     </repositories>
     <dependencies>
+        <dependency>
+            <groupId>com.github.seeseemelk</groupId>
+            <artifactId>MockBukkit-v1.16</artifactId>
+            <version>0.25.0</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>co.aikar</groupId>
             <artifactId>acf-bukkit</artifactId> <!-- Don't forget to replace this -->

+ 6 - 4
src/main/java/com/gmail/nossr50/database/DatabaseManager.java

@@ -76,11 +76,11 @@ public interface DatabaseManager {
 
     /**
      * Add a new user to the database.
-     *
-     * @param playerName The name of the player to be added to the database
+     *  @param playerName The name of the player to be added to the database
      * @param uuid The uuid of the player to be added to the database
+     * @return
      */
-    void newUser(String playerName, UUID uuid);
+    @NotNull PlayerProfile newUser(String playerName, UUID uuid);
 
     @NotNull PlayerProfile newUser(@NotNull Player player);
 
@@ -101,11 +101,13 @@ public interface DatabaseManager {
      * Load a player from the database.
      * @param uuid The uuid of the player to load from the database
      * @return The player's data, or an unloaded PlayerProfile if not found
-     * @deprecated Use {@link DatabaseManager#loadPlayerProfile(org.bukkit.OfflinePlayer)} if possible
+     * @deprecated Use {@link DatabaseManager#loadPlayerProfile(org.bukkit.OfflinePlayer)} or {@link DatabaseManager#loadPlayerProfile(java.util.UUID)} if possible
      */
     @Deprecated
     @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid, @Nullable String playerName);
 
+    @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid);
+
     /**
      * Get all users currently stored in the database.
      *

+ 75 - 127
src/main/java/com/gmail/nossr50/database/FlatFileDatabaseManager.java

@@ -31,7 +31,7 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
     private final @NotNull Logger logger;
     private final long purgeTime;
     private final int startingLevel;
-    private boolean testing;
+    private final boolean testing;
 
     private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
     private final @NotNull File usersFile;
@@ -97,6 +97,8 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
                     logger.info("Detected "+flatFileDataFlags.size() + " data entries which need correction.");
                 }
             }
+
+            updateLeaderboards();
         }
     }
 
@@ -193,29 +195,31 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
                     String name = character[USERNAME_INDEX];
                     long lastPlayed = 0;
                     boolean rewrite = false;
+
                     try {
-                        lastPlayed = Long.parseLong(character[37]) * Misc.TIME_CONVERSION_FACTOR;
-                    }
-                    catch (NumberFormatException e) {
+                        lastPlayed = Long.parseLong(character[OVERHAUL_LAST_LOGIN]);
+                    } catch (NumberFormatException e) {
                         e.printStackTrace();
                     }
-                    if (lastPlayed == 0) {
+
+                    if (lastPlayed == -1) {
                         OfflinePlayer player = mcMMO.p.getServer().getOfflinePlayer(name);
-                        lastPlayed = player.getLastPlayed();
-                        rewrite = true;
+
+                        if(player.getLastPlayed() != 0) {
+                            lastPlayed = player.getLastPlayed();
+                            rewrite = true;
+                        }
                     }
 
                     if (currentTime - lastPlayed > purgeTime) {
                         removedPlayers++;
-                    }
-                    else {
+                    } else {
                         if (rewrite) {
                             // Rewrite their data with a valid time
-                            character[37] = Long.toString(lastPlayed);
+                            character[OVERHAUL_LAST_LOGIN] = Long.toString(lastPlayed);
                             String newLine = org.apache.commons.lang.StringUtils.join(character, ":");
                             writer.append(newLine).append("\r\n");
-                        }
-                        else {
+                        } else {
                             writer.append(line).append("\r\n");
                         }
                     }
@@ -421,51 +425,52 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
         }
     }
 
-    private void writeUserToLine(PlayerProfile profile, @NotNull String playerName, @Nullable UUID uuid, StringBuilder writer) {
+    public void writeUserToLine(@NotNull PlayerProfile profile, @NotNull String playerName, @Nullable UUID uuid, @NotNull Appendable writer) throws IOException {
         writer.append(playerName).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.MINING)).append(":");
-        writer.append(":");
-        writer.append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.MINING)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.WOODCUTTING)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.WOODCUTTING)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.REPAIR)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.UNARMED)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.HERBALISM)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.EXCAVATION)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.ARCHERY)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.SWORDS)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.AXES)).append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.ACROBATICS)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.REPAIR)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.UNARMED)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.HERBALISM)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.EXCAVATION)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.ARCHERY)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.SWORDS)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.AXES)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.ACROBATICS)).append(":");
-        writer.append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.TAMING)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.TAMING)).append(":");
-        writer.append((int) profile.getAbilityDATS(SuperAbilityType.BERSERK)).append(":");
-        writer.append((int) profile.getAbilityDATS(SuperAbilityType.GIGA_DRILL_BREAKER)).append(":");
-        writer.append((int) profile.getAbilityDATS(SuperAbilityType.TREE_FELLER)).append(":");
-        writer.append((int) profile.getAbilityDATS(SuperAbilityType.GREEN_TERRA)).append(":");
-        writer.append((int) profile.getAbilityDATS(SuperAbilityType.SERRATED_STRIKES)).append(":");
-        writer.append((int) profile.getAbilityDATS(SuperAbilityType.SKULL_SPLITTER)).append(":");
-        writer.append((int) profile.getAbilityDATS(SuperAbilityType.SUPER_BREAKER)).append(":");
-        writer.append(":");
-        writer.append(profile.getSkillLevel(PrimarySkillType.FISHING)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.FISHING)).append(":");
-        writer.append((int) profile.getAbilityDATS(SuperAbilityType.BLAST_MINING)).append(":");
-        writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.MINING))).append(":");
+        writer.append(IGNORED).append(":");
+        writer.append(IGNORED).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.MINING))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.WOODCUTTING))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.WOODCUTTING))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.REPAIR))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.UNARMED))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.HERBALISM))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.EXCAVATION))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ARCHERY))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.SWORDS))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.AXES))).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ACROBATICS))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.REPAIR))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.UNARMED))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.HERBALISM))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.EXCAVATION))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ARCHERY))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.SWORDS))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.AXES))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ACROBATICS))).append(":");
+        writer.append(IGNORED).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.TAMING))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.TAMING))).append(":");
+        writer.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.BERSERK))).append(":");
+        writer.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.GIGA_DRILL_BREAKER))).append(":");
+        writer.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.TREE_FELLER))).append(":");
+        writer.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.GREEN_TERRA))).append(":");
+        writer.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SERRATED_STRIKES))).append(":");
+        writer.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SKULL_SPLITTER))).append(":");
+        writer.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SUPER_BREAKER))).append(":");
+        writer.append(IGNORED).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.FISHING))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.FISHING))).append(":");
+        writer.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.BLAST_MINING))).append(":");
+        writer.append(IGNORED).append(":"); //Legacy last login
         writer.append(IGNORED).append(":"); //mob health bar
-        writer.append(profile.getSkillLevel(PrimarySkillType.ALCHEMY)).append(":");
-        writer.append(profile.getSkillXpLevel(PrimarySkillType.ALCHEMY)).append(":");
+        writer.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ALCHEMY))).append(":");
+        writer.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ALCHEMY))).append(":");
         writer.append(uuid != null ? uuid.toString() : "NULL").append(":");
-        writer.append(profile.getScoreboardTipsShown()).append(":");
-        writer.append(profile.getUniqueData(UniqueDataType.CHIMAERA_WING_DATS)).append(":");
+        writer.append(String.valueOf(profile.getScoreboardTipsShown())).append(":");
+        writer.append(String.valueOf(profile.getUniqueData(UniqueDataType.CHIMAERA_WING_DATS))).append(":");
+        writer.append(String.valueOf(profile.getLastLogin())).append(":"); //overhaul last login
         writer.append("\r\n");
     }
 
@@ -498,81 +503,21 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
     }
 
     public @NotNull PlayerProfile newUser(@NotNull Player player) {
-        newUser(player.getName(), player.getUniqueId());
-        return new PlayerProfile(player.getName(), player.getUniqueId(), true);
+        return new PlayerProfile(player.getName(), player.getUniqueId(), true, startingLevel);
     }
 
-    public void newUser(String playerName, UUID uuid) {
-        BufferedWriter out = null;
+    public @NotNull PlayerProfile newUser(@NotNull String playerName, @NotNull UUID uuid) {
+        PlayerProfile playerProfile = new PlayerProfile(playerName, uuid, true, startingLevel);
+
         synchronized (fileWritingLock) {
-            try {
-                // Open the file to write the player
-                out = new BufferedWriter(new FileWriter(usersFilePath, true));
-
-                String startingLevelStr = startingLevel + ":";
-
-                // Add the player to the end
-                out.append(playerName).append(":");
-                out.append(startingLevelStr); // Mining
-                out.append(":");
-                out.append(":");
-                out.append("0:"); // Xp
-                out.append(startingLevelStr); // Woodcutting
-                out.append("0:"); // WoodCuttingXp
-                out.append(startingLevelStr); // Repair
-                out.append(startingLevelStr); // Unarmed
-                out.append(startingLevelStr); // Herbalism
-                out.append(startingLevelStr); // Excavation
-                out.append(startingLevelStr); // Archery
-                out.append(startingLevelStr); // Swords
-                out.append(startingLevelStr); // Axes
-                out.append(startingLevelStr); // Acrobatics
-                out.append("0:"); // RepairXp
-                out.append("0:"); // UnarmedXp
-                out.append("0:"); // HerbalismXp
-                out.append("0:"); // ExcavationXp
-                out.append("0:"); // ArcheryXp
-                out.append("0:"); // SwordsXp
-                out.append("0:"); // AxesXp
-                out.append("0:"); // AcrobaticsXp
-                out.append(":");
-                out.append(startingLevelStr); // Taming
-                out.append("0:"); // TamingXp
-                out.append("0:"); // DATS
-                out.append("0:"); // DATS
-                out.append("0:"); // DATS
-                out.append("0:"); // DATS
-                out.append("0:"); // DATS
-                out.append("0:"); // DATS
-                out.append("0:"); // DATS
-                out.append(":");
-                out.append(startingLevelStr); // Fishing
-                out.append("0:"); // FishingXp
-                out.append("0:"); // Blast Mining
-                out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
-                out.append(IGNORED).append(":"); // Mob Healthbar HUD
-                out.append(startingLevelStr); // Alchemy
-                out.append("0:"); // AlchemyXp
-                out.append(uuid != null ? uuid.toString() : "NULL").append(":"); // UUID
-                out.append("0:"); // Scoreboard tips shown
-                // Add more in the same format as the line above
-
-                out.newLine();
-            }
-            catch (Exception e) {
+            try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(usersFilePath, true))) {
+                writeUserToLine(playerProfile, playerName, uuid, bufferedWriter);
+            } catch (Exception e) {
                 e.printStackTrace();
             }
-            finally {
-                if (out != null) {
-                    try {
-                        out.close();
-                    }
-                    catch (IOException e) {
-                        // Ignore
-                    }
-                }
-            }
         }
+
+        return playerProfile;
     }
 
     public @NotNull PlayerProfile loadPlayerProfile(@NotNull OfflinePlayer offlinePlayer) {
@@ -587,6 +532,10 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
         return loadPlayerByUUID(uuid, playerName, false);
     }
 
+    public @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid) {
+        return loadPlayerByUUID(uuid, null, false);
+    }
+
     private @NotNull PlayerProfile loadPlayerByUUID(@NotNull UUID uuid, @Nullable String playerName, boolean isOnline) {
         BufferedReader in = null;
 
@@ -700,7 +649,7 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
         }
 
         //Return a new blank profile
-        return new PlayerProfile(playerName, null);
+        return new PlayerProfile(playerName, new UUID(0, 0), startingLevel);
     }
 
     private @NotNull PlayerProfile grabUnloadedProfile(@NotNull UUID uuid, @Nullable String playerName) {
@@ -708,7 +657,7 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
             playerName = ""; //No name for you boy!
         }
 
-        return new PlayerProfile(playerName, uuid);
+        return new PlayerProfile(playerName, uuid, 0);
     }
 
     public void convertUsers(DatabaseManager destination) {
@@ -731,8 +680,7 @@ public final class FlatFileDatabaseManager implements DatabaseManager {
 
                     try {
                         destination.saveUser(loadFromLine(character));
-                    }
-                    catch (Exception e) {
+                    } catch (Exception e) {
                         e.printStackTrace();
                     }
                     convertedUsers++;

+ 17 - 10
src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java

@@ -1,6 +1,7 @@
 package com.gmail.nossr50.database;
 
 import com.gmail.nossr50.api.exceptions.InvalidSkillException;
+import com.gmail.nossr50.config.AdvancedConfig;
 import com.gmail.nossr50.datatypes.MobHealthbarType;
 import com.gmail.nossr50.datatypes.database.DatabaseType;
 import com.gmail.nossr50.datatypes.database.PlayerStat;
@@ -491,19 +492,19 @@ public final class SQLDatabaseManager implements DatabaseManager {
         return skills;
     }
 
-    public void newUser(String playerName, UUID uuid) {
+    public @NotNull PlayerProfile newUser(String playerName, UUID uuid) {
         Connection connection = null;
 
         try {
             connection = getConnection(PoolIdentifier.MISC);
             newUser(connection, playerName, uuid);
-        }
-        catch (SQLException ex) {
+        } catch (SQLException ex) {
             printErrors(ex);
-        }
-        finally {
+        } finally {
             tryClose(connection);
         }
+
+        return new PlayerProfile(playerName, uuid, true, mcMMO.p.getAdvancedConfig().getStartingLevel());
     }
 
     @Override
@@ -513,7 +514,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
             int id = newUser(connection, player.getName(), player.getUniqueId());
 
             if (id == -1) {
-                return new PlayerProfile(player.getName(), player.getUniqueId(), false);
+                return new PlayerProfile(player.getName(), player.getUniqueId(), false, mcMMO.p.getAdvancedConfig().getStartingLevel());
             } else {
                 return loadPlayerProfile(player.getUniqueId(), player.getName());
             }
@@ -521,7 +522,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
             e.printStackTrace();
         }
 
-        return new PlayerProfile(player.getName(), player.getUniqueId(), false);
+        return new PlayerProfile(player.getName(), player.getUniqueId(), false, mcMMO.p.getAdvancedConfig().getStartingLevel());
     }
 
     private int newUser(Connection connection, String playerName, UUID uuid) {
@@ -567,7 +568,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
             return loadPlayerFromDB(null, playerName);
         } catch (RuntimeException e) {
             e.printStackTrace();
-            return new PlayerProfile(playerName, false);
+            return new PlayerProfile(playerName, false, mcMMO.p.getAdvancedConfig().getStartingLevel());
         }
     }
 
@@ -575,6 +576,12 @@ public final class SQLDatabaseManager implements DatabaseManager {
         return loadPlayerFromDB(uuid, playerName);
     }
 
+    @Override
+    public @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid) {
+        return loadPlayerFromDB(uuid, null);
+    }
+
+
     private PlayerProfile loadPlayerFromDB(@Nullable UUID uuid, @Nullable String playerName) throws RuntimeException {
         if(uuid == null && playerName == null) {
             throw new RuntimeException("Error looking up player, both UUID and playerName are null and one must not be.");
@@ -590,7 +597,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
 
             if (id == -1) {
             // There is no such user
-                return new PlayerProfile(playerName, false);
+                return new PlayerProfile(playerName, mcMMO.p.getAdvancedConfig().getStartingLevel());
             }
             // There is such a user
             writeMissingRows(connection, id);
@@ -659,7 +666,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
         }
 
         //Return empty profile
-        return new PlayerProfile(playerName, false);
+        return new PlayerProfile(playerName, mcMMO.p.getAdvancedConfig().getStartingLevel());
     }
 
     public void convertUsers(DatabaseManager destination) {

+ 14 - 10
src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java

@@ -43,11 +43,13 @@ public class PlayerProfile {
     private final Map<PrimarySkillType, Float> rollingSkillsXp = new EnumMap<PrimarySkillType, Float>(PrimarySkillType.class);
 
     @Deprecated
-    public PlayerProfile(String playerName) {
-        this(playerName, null);
+    //TODO: Add deprecated constructor w/o startinglevel
+    public PlayerProfile(String playerName, int startingLevel) {
+        this(playerName, null, startingLevel);
     }
 
-    public PlayerProfile(String playerName, UUID uuid) {
+    //TODO: Add deprecated constructor w/o startinglevel
+    public PlayerProfile(String playerName, UUID uuid, int startingLevel) {
         this.uuid = uuid;
         this.playerName = playerName;
 
@@ -58,9 +60,7 @@ public class PlayerProfile {
         }
 
         for (PrimarySkillType primarySkillType : SkillTools.NON_CHILD_SKILLS) {
-            int startingLvl = mcMMO.p != null ? mcMMO.p.getAdvancedConfig().getStartingLevel() : 0; //TODO: Setup the mock since this was to avoid setting up a mock in a test
-
-            skills.put(primarySkillType, startingLvl);
+            skills.put(primarySkillType, startingLevel);
             skillsXp.put(primarySkillType, 0F);
         }
 
@@ -70,13 +70,13 @@ public class PlayerProfile {
     }
 
     @Deprecated
-    public PlayerProfile(@NotNull String playerName, boolean isLoaded) {
-        this(playerName);
+    public PlayerProfile(@NotNull String playerName, boolean isLoaded, int startingLvl) {
+        this(playerName, startingLvl);
         this.loaded = isLoaded;
     }
 
-    public PlayerProfile(@NotNull String playerName, UUID uuid, boolean isLoaded) {
-        this(playerName, uuid);
+    public PlayerProfile(@NotNull String playerName, UUID uuid, boolean isLoaded, int startingLvl) {
+        this(playerName, uuid, startingLvl);
         this.loaded = isLoaded;
     }
 
@@ -167,6 +167,10 @@ public class PlayerProfile {
             return lastLogin;
     }
 
+    public void updateLastLogin() {
+        this.lastLogin = System.currentTimeMillis();
+    }
+
     public String getPlayerName() {
         return playerName;
     }

+ 9 - 0
src/main/java/com/gmail/nossr50/mcMMO.java

@@ -59,8 +59,10 @@ import org.bukkit.Bukkit;
 import org.bukkit.entity.Player;
 import org.bukkit.event.HandlerList;
 import org.bukkit.metadata.FixedMetadataValue;
+import org.bukkit.plugin.PluginDescriptionFile;
 import org.bukkit.plugin.PluginManager;
 import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.plugin.java.JavaPluginLoader;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -168,6 +170,13 @@ public class mcMMO extends JavaPlugin {
         p = this;
     }
 
+
+    protected mcMMO(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file)
+    {
+        super(loader, description, dataFolder, file);
+    }
+
+
     /**
      * Things to be run when the plugin is enabled.
      */

+ 2 - 0
src/main/java/com/gmail/nossr50/runnables/player/PlayerProfileLoadingTask.java

@@ -92,6 +92,8 @@ public class PlayerProfileLoadingTask extends BukkitRunnable {
                 return;
             }
 
+            mcMMOPlayer.getProfile().updateLastLogin();
+
             mcMMOPlayer.setupPartyData();
             UserManager.track(mcMMOPlayer);
             mcMMOPlayer.actualizeRespawnATS();

+ 1 - 1
src/main/java/com/gmail/nossr50/util/commands/CommandUtils.java

@@ -86,7 +86,7 @@ public final class CommandUtils {
             return true;
         }
 
-        PlayerProfile profile = new PlayerProfile(playerName, false);
+        PlayerProfile profile = new PlayerProfile(playerName, false, 0);
 
         if (unloadedProfile(sender, profile)) {
             return false;

+ 59 - 2
src/test/java/com/gmail/nossr50/database/FlatFileDatabaseManagerTest.java

@@ -6,14 +6,20 @@ import com.gmail.nossr50.datatypes.player.PlayerProfile;
 import com.gmail.nossr50.datatypes.player.UniqueDataType;
 import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
 import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
+import com.gmail.nossr50.mcMMO;
+import com.gmail.nossr50.runnables.player.PlayerProfileLoadingTask;
 import com.gmail.nossr50.util.skills.SkillTools;
 import com.google.common.io.Files;
+import org.bukkit.entity.Player;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
 import org.powermock.modules.junit4.PowerMockRunner;
 
 import java.io.*;
@@ -26,9 +32,10 @@ import static org.junit.Assert.*;
 
 
 //TODO: Test update leaderboards
-@RunWith(PowerMockRunner.class)
 public class FlatFileDatabaseManagerTest {
 
+    public mcMMO plugin;
+
     public static final @NotNull String TEST_FILE_NAME = "test.mcmmo.users";
     public static final @NotNull String BAD_FILE_LINE_ONE = "mrfloris:2420:::0:2452:0:1983:1937:1790:3042:1138:3102:2408:3411:0:0:0:0:0:0:0:0::642:0:1617583171:0:1617165043:0:1617583004:1617563189:1616785408::2184:0:0:1617852413:HEARTS:415:0:631e3896-da2a-4077-974b-d047859d76bc:5:1600906906:";
     public static final @NotNull String BAD_DATA_FILE_LINE_TWENTY_THREE = "nossr51:baddata:::baddata:baddata:640:baddata:1000:1000:1000:baddata:baddata:baddata:baddata:16:0:500:20273:0:0:0:0::1000:0:0:baddata:1593543012:0:0:0:0::1000:0:0:baddata:IGNORED:1000:0:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1:0:";
@@ -137,7 +144,7 @@ public class FlatFileDatabaseManagerTest {
         //Make a Profile to save and check to see if it worked
         UUID uuid = UUID.fromString("588fe472-1c82-4c4e-9aa1-7eefccb277e3");
         String playerName = "nossr50";
-        PlayerProfile testProfile = new PlayerProfile(playerName, uuid);
+        PlayerProfile testProfile = new PlayerProfile(playerName, uuid, 0);
         //The above profile should be "zero" initialized
 
         //Save the zero version and see if it looks correct
@@ -208,6 +215,56 @@ public class FlatFileDatabaseManagerTest {
         testHealthyDataProfileValues(playerName, uuid, profile);
     }
 
+    @Test
+    public void testNewUser() {
+        //We will test that new user values line up with our expectations
+        UUID uuid = new UUID(0, 1);
+        String playerName = "nossr50";
+
+        int newUserTestStartingLvl = 1337;
+        db = new FlatFileDatabaseManager(new File(tempDir.getPath() + File.separator + TEST_FILE_NAME), logger, PURGE_TIME, newUserTestStartingLvl, true);
+        db.checkFileHealthAndStructure();
+
+        PlayerProfile playerProfile = db.newUser(playerName, uuid);
+
+        assertTrue(playerProfile.isLoaded());
+        assertEquals(playerName, playerProfile.getPlayerName());
+        assertEquals(uuid, playerProfile.getUniqueId());
+
+        PlayerProfile retrievedFromDisk = db.loadPlayerProfile(uuid);
+        assertTrue(retrievedFromDisk.isLoaded());
+        assertEquals(playerName, retrievedFromDisk.getPlayerName());
+        assertEquals(uuid, retrievedFromDisk.getUniqueId());
+
+        //Checking a new user for being "zero" initialized
+        checkNewUserValues(playerProfile, newUserTestStartingLvl);
+        checkNewUserValues(retrievedFromDisk, newUserTestStartingLvl);
+
+        //TODO: Should we do any dupe checking? Probably not needed as it would be caught on the next load
+        db.newUser("disco", new UUID(3, 3));
+        db.newUser("dingus", new UUID(3, 4));
+        db.newUser("duped_dingus", new UUID(3, 4));
+    }
+
+    private void checkNewUserValues(@NotNull PlayerProfile playerProfile, int startingLevel) {
+        //Checking a new user for being zero initialized
+        for(PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+            if(SkillTools.isChildSkill(primarySkillType))
+                continue;
+
+            assertEquals(startingLevel, playerProfile.getSkillLevel(primarySkillType));
+            assertEquals(0, playerProfile.getSkillXpLevelRaw(primarySkillType), 0);
+        }
+
+        for(SuperAbilityType superAbilityType : SuperAbilityType.values()) {
+            assertEquals(0, playerProfile.getAbilityDATS(superAbilityType));
+        }
+
+        assertTrue(playerProfile.getLastLogin() > 0);
+        assertEquals(playerProfile.getChimaerWingDATS(), 0);
+        assertEquals(playerProfile.getScoreboardTipsShown(), 0);
+    }
+
     @Test
     public void testLoadByUUID() {
         File dbFile = prepareDatabaseTestResource(DB_HEALTHY);