12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256 |
- package com.gmail.nossr50.database;
- import com.gmail.nossr50.api.exceptions.InvalidSkillException;
- import com.gmail.nossr50.datatypes.database.DatabaseType;
- import com.gmail.nossr50.datatypes.database.PlayerStat;
- 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.util.Misc;
- import com.gmail.nossr50.util.skills.SkillTools;
- import org.bukkit.OfflinePlayer;
- import org.bukkit.entity.Player;
- import org.jetbrains.annotations.NotNull;
- import org.jetbrains.annotations.Nullable;
- import java.io.*;
- import java.time.LocalDateTime;
- import java.time.format.DateTimeFormatter;
- import java.util.*;
- import java.util.logging.Logger;
- public final class FlatFileDatabaseManager implements DatabaseManager {
- public static final String IGNORED = "IGNORED";
- public static final String LEGACY_INVALID_OLD_USERNAME = "_INVALID_OLD_USERNAME_'";
- private final @NotNull EnumMap<PrimarySkillType, List<PlayerStat>> playerStatHash = new EnumMap<PrimarySkillType, List<PlayerStat>>(PrimarySkillType.class);
- private final @NotNull List<PlayerStat> powerLevels = new ArrayList<>();
- private long lastUpdate = 0;
- private final @NotNull String usersFilePath;
- private final @NotNull Logger logger;
- private final long purgeTime;
- private final int startingLevel;
- private final boolean testing;
- private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
- private final @NotNull File usersFile;
- private static final Object fileWritingLock = new Object();
- public static final int USERNAME_INDEX = 0;
- public static final int SKILLS_MINING = 1;
- public static final int EXP_MINING = 4;
- public static final int SKILLS_WOODCUTTING = 5;
- public static final int EXP_WOODCUTTING = 6;
- public static final int SKILLS_REPAIR = 7;
- public static final int SKILLS_UNARMED = 8;
- public static final int SKILLS_HERBALISM = 9;
- public static final int SKILLS_EXCAVATION = 10;
- public static final int SKILLS_ARCHERY = 11;
- public static final int SKILLS_SWORDS = 12;
- public static final int SKILLS_AXES = 13;
- public static final int SKILLS_ACROBATICS = 14;
- public static final int EXP_REPAIR = 15;
- public static final int EXP_UNARMED = 16;
- public static final int EXP_HERBALISM = 17;
- public static final int EXP_EXCAVATION = 18;
- public static final int EXP_ARCHERY = 19;
- public static final int EXP_SWORDS = 20;
- public static final int EXP_AXES = 21;
- public static final int EXP_ACROBATICS = 22;
- public static final int SKILLS_TAMING = 24;
- public static final int EXP_TAMING = 25;
- public static final int COOLDOWN_BERSERK = 26;
- public static final int COOLDOWN_GIGA_DRILL_BREAKER = 27;
- public static final int COOLDOWN_TREE_FELLER = 28;
- public static final int COOLDOWN_GREEN_TERRA = 29;
- public static final int COOLDOWN_SERRATED_STRIKES = 30;
- public static final int COOLDOWN_SKULL_SPLITTER = 31;
- public static final int COOLDOWN_SUPER_BREAKER = 32;
- public static final int SKILLS_FISHING = 34;
- public static final int EXP_FISHING = 35;
- public static final int COOLDOWN_BLAST_MINING = 36;
- public static final int LEGACY_LAST_LOGIN = 37;
- public static final int HEALTHBAR = 38;
- public static final int SKILLS_ALCHEMY = 39;
- public static final int EXP_ALCHEMY = 40;
- public static final int UUID_INDEX = 41;
- public static final int SCOREBOARD_TIPS = 42;
- public static final int COOLDOWN_CHIMAERA_WING = 43;
- public static final int OVERHAUL_LAST_LOGIN = 44;
- public static final int DATA_ENTRY_COUNT = OVERHAUL_LAST_LOGIN + 1; //Update this everytime new data is added
- protected FlatFileDatabaseManager(@NotNull File usersFile, @NotNull Logger logger, long purgeTime, int startingLevel, boolean testing) {
- this.usersFile = usersFile;
- this.usersFilePath = usersFile.getPath();
- this.logger = logger;
- this.purgeTime = purgeTime;
- this.startingLevel = startingLevel;
- this.testing = testing;
- if(!testing) {
- List<FlatFileDataFlag> flatFileDataFlags = checkFileHealthAndStructure();
- if(flatFileDataFlags != null) {
- if(flatFileDataFlags.size() > 0) {
- logger.info("Detected "+flatFileDataFlags.size() + " data entries which need correction.");
- }
- }
- updateLeaderboards();
- }
- }
- protected FlatFileDatabaseManager(@NotNull String usersFilePath, @NotNull Logger logger, long purgeTime, int startingLevel) {
- this(new File(usersFilePath), logger, purgeTime, startingLevel, false);
- }
- public int purgePowerlessUsers() {
- int purgedUsers = 0;
- logger.info("Purging powerless users...");
- BufferedReader in = null;
- FileWriter out = null;
- // This code is O(n) instead of O(n²)
- synchronized (fileWritingLock) {
- try {
- in = new BufferedReader(new FileReader(usersFilePath));
- StringBuilder writer = new StringBuilder();
- String line;
- while ((line = in.readLine()) != null) {
- String[] character = line.split(":");
- Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(character);
- boolean powerless = true;
- for (int skill : skills.values()) {
- if (skill != 0) {
- powerless = false;
- break;
- }
- }
- // If they're still around, rewrite them to the file.
- if (!powerless) {
- writer.append(line).append("\r\n");
- }
- else {
- purgedUsers++;
- }
- }
- // Write the new file
- out = new FileWriter(usersFilePath);
- out.write(writer.toString());
- }
- catch (IOException e) {
- logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
- }
- finally {
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- if (out != null) {
- try {
- out.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- logger.info("Purged " + purgedUsers + " users from the database.");
- return purgedUsers;
- }
- //TODO: Test this
- public void purgeOldUsers() {
- int removedPlayers = 0;
- long currentTime = System.currentTimeMillis();
- logger.info("Purging old users...");
- BufferedReader in = null;
- FileWriter out = null;
- // This code is O(n) instead of O(n²)
- synchronized (fileWritingLock) {
- try {
- in = new BufferedReader(new FileReader(usersFilePath));
- StringBuilder writer = new StringBuilder();
- String line;
- while ((line = in.readLine()) != null) {
- String[] character = line.split(":");
- String name = character[USERNAME_INDEX];
- long lastPlayed = 0;
- boolean rewrite = false;
- try {
- lastPlayed = Long.parseLong(character[OVERHAUL_LAST_LOGIN]);
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- if (lastPlayed == -1) {
- OfflinePlayer player = mcMMO.p.getServer().getOfflinePlayer(name);
- if(player.getLastPlayed() != 0) {
- lastPlayed = player.getLastPlayed();
- rewrite = true;
- }
- }
- if (lastPlayed != -1 && lastPlayed != 0 && currentTime - lastPlayed > purgeTime) {
- removedPlayers++;
- } else {
- if (rewrite) {
- // Rewrite their data with a valid time
- character[OVERHAUL_LAST_LOGIN] = Long.toString(lastPlayed);
- String newLine = org.apache.commons.lang.StringUtils.join(character, ":");
- writer.append(newLine).append("\r\n");
- } else {
- writer.append(line).append("\r\n");
- }
- }
- }
- // Write the new file
- out = new FileWriter(usersFilePath);
- out.write(writer.toString());
- if(testing) {
- System.out.println(writer.toString());
- }
- }
- catch (IOException e) {
- logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
- }
- finally {
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- if (out != null) {
- try {
- out.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- logger.info("Purged " + removedPlayers + " users from the database.");
- }
- public boolean removeUser(String playerName, UUID uuid) {
- //NOTE: UUID is unused for FlatFile for this interface implementation
- boolean worked = false;
- BufferedReader in = null;
- FileWriter out = null;
- synchronized (fileWritingLock) {
- try {
- in = new BufferedReader(new FileReader(usersFilePath));
- StringBuilder writer = new StringBuilder();
- String line;
- while ((line = in.readLine()) != null) {
- // Write out the same file but when we get to the player we want to remove, we skip his line.
- if (!worked && line.split(":")[USERNAME_INDEX].equalsIgnoreCase(playerName)) {
- logger.info("User found, removing...");
- worked = true;
- continue; // Skip the player
- }
- writer.append(line).append("\r\n");
- }
- out = new FileWriter(usersFilePath); // Write out the new file
- out.write(writer.toString());
- }
- catch (Exception e) {
- logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
- }
- finally {
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- if (out != null) {
- try {
- out.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- Misc.profileCleanup(playerName);
- return worked;
- }
- @Override
- public void cleanupUser(UUID uuid) {
- //Not used in FlatFile
- }
- public boolean saveUser(@NotNull PlayerProfile profile) {
- String playerName = profile.getPlayerName();
- UUID uuid = profile.getUniqueId();
- BufferedReader in = null;
- FileWriter out = null;
- boolean corruptDataFound = false;
- synchronized (fileWritingLock) {
- try {
- // Open the file
- in = new BufferedReader(new FileReader(usersFilePath));
- StringBuilder writer = new StringBuilder();
- String line;
- boolean wroteUser = false;
- if(testing) {
- System.out.println("-- saveUser bufferedreader feed --");
- }
- // While not at the end of the file
- while ((line = in.readLine()) != null) {
- if(testing) {
- System.out.println(line);
- }
- if(line.startsWith("#")) {
- writer.append(line).append("\r\n");
- continue;
- }
- //Check for incomplete or corrupted data
- if(!line.contains(":")) {
- if(!corruptDataFound) {
- logger.severe("mcMMO found some unexpected or corrupted data in mcmmo.users and is removing it, it is possible some data has been lost.");
- corruptDataFound = true;
- }
- continue;
- }
- String[] splitData = line.split(":");
- //This would be rare, but check the splitData for having enough entries to contain a UUID
- if(splitData.length < UUID_INDEX) { //UUID have been in mcMMO DB for a very long time so any user without
- if(!corruptDataFound) {
- logger.severe("mcMMO found some unexpected or corrupted data in mcmmo.users and is removing it, it is possible some data has been lost.");
- corruptDataFound = true;
- }
- continue;
- }
- if (!(uuid != null
- && splitData[UUID_INDEX].equalsIgnoreCase(uuid.toString()))
- && !splitData[USERNAME_INDEX].equalsIgnoreCase(playerName)) {
- writer.append(line).append("\r\n"); //Not the user so write it to file and move on
- } else {
- //User found
- writeUserToLine(profile, playerName, uuid, writer);
- wroteUser = true;
- }
- }
- /*
- * If we couldn't find the user in the DB we need to add him
- */
- if(!wroteUser) {
- writeUserToLine(profile, playerName, uuid, writer);
- }
- if(testing) {
- System.out.println("-- saveUser (FileWriter contents before save) --");
- System.out.println(writer.toString());
- }
- // Write the new file
- out = new FileWriter(usersFilePath);
- out.write(writer.toString());
- return true;
- }
- catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- finally {
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- if (out != null) {
- try {
- out.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- }
- public void writeUserToLine(@NotNull PlayerProfile profile, @NotNull String playerName, @Nullable UUID uuid, @NotNull Appendable appendable) throws IOException {
- appendable.append(playerName).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.MINING))).append(":");
- appendable.append(IGNORED).append(":");
- appendable.append(IGNORED).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.MINING))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.WOODCUTTING))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.WOODCUTTING))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.REPAIR))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.UNARMED))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.HERBALISM))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.EXCAVATION))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ARCHERY))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.SWORDS))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.AXES))).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ACROBATICS))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.REPAIR))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.UNARMED))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.HERBALISM))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.EXCAVATION))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ARCHERY))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.SWORDS))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.AXES))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ACROBATICS))).append(":");
- appendable.append(IGNORED).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.TAMING))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.TAMING))).append(":");
- appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.BERSERK))).append(":");
- appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.GIGA_DRILL_BREAKER))).append(":");
- appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.TREE_FELLER))).append(":");
- appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.GREEN_TERRA))).append(":");
- appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SERRATED_STRIKES))).append(":");
- appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SKULL_SPLITTER))).append(":");
- appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SUPER_BREAKER))).append(":");
- appendable.append(IGNORED).append(":");
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.FISHING))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.FISHING))).append(":");
- appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.BLAST_MINING))).append(":");
- appendable.append(IGNORED).append(":"); //Legacy last login
- appendable.append(IGNORED).append(":"); //mob health bar
- appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ALCHEMY))).append(":");
- appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ALCHEMY))).append(":");
- appendable.append(uuid != null ? uuid.toString() : "NULL").append(":");
- appendable.append(String.valueOf(profile.getScoreboardTipsShown())).append(":");
- appendable.append(String.valueOf(profile.getUniqueData(UniqueDataType.CHIMAERA_WING_DATS))).append(":");
- appendable.append(String.valueOf(profile.getLastLogin())).append(":"); //overhaul last login
- appendable.append("\r\n");
- }
- public @NotNull List<PlayerStat> readLeaderboard(@Nullable PrimarySkillType primarySkillType, int pageNumber, int statsPerPage) throws InvalidSkillException {
- //Fix for a plugin that people are using that is throwing SQL errors
- if(primarySkillType != null && SkillTools.isChildSkill(primarySkillType)) {
- logger.severe("A plugin hooking into mcMMO is being naughty with our database commands, update all plugins that hook into mcMMO and contact their devs!");
- throw new InvalidSkillException("A plugin hooking into mcMMO that you are using is attempting to read leaderboard skills for child skills, child skills do not have leaderboards! This is NOT an mcMMO error!");
- }
- updateLeaderboards();
- List<PlayerStat> statsList = primarySkillType == null ? powerLevels : playerStatHash.get(primarySkillType);
- int fromIndex = (Math.max(pageNumber, 1) - 1) * statsPerPage;
- return statsList.subList(Math.min(fromIndex, statsList.size()), Math.min(fromIndex + statsPerPage, statsList.size()));
- }
- public Map<PrimarySkillType, Integer> readRank(String playerName) {
- updateLeaderboards();
- Map<PrimarySkillType, Integer> skills = new EnumMap<PrimarySkillType, Integer>(PrimarySkillType.class);
- for (PrimarySkillType skill : SkillTools.NON_CHILD_SKILLS) {
- skills.put(skill, getPlayerRank(playerName, playerStatHash.get(skill)));
- }
- skills.put(null, getPlayerRank(playerName, powerLevels));
- return skills;
- }
- public @NotNull PlayerProfile newUser(@NotNull Player player) {
- return new PlayerProfile(player.getName(), player.getUniqueId(), true, startingLevel);
- }
- public @NotNull PlayerProfile newUser(@NotNull String playerName, @NotNull UUID uuid) {
- PlayerProfile playerProfile = new PlayerProfile(playerName, uuid, true, startingLevel);
- synchronized (fileWritingLock) {
- try(BufferedReader bufferedReader = new BufferedReader(new FileReader(usersFilePath))) {
- StringBuilder stringBuilder = new StringBuilder();
- String line;
- //Build up the file
- while((line = bufferedReader.readLine()) != null) {
- stringBuilder.append(line).append("\r\n");
- }
- try (FileWriter fileWriter = new FileWriter(usersFile)) {
- writeUserToLine(playerProfile, playerName, uuid, stringBuilder);
- fileWriter.write(stringBuilder.toString());
- } catch (Exception e) {
- e.printStackTrace();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return playerProfile;
- }
- public @NotNull PlayerProfile loadPlayerProfile(@NotNull OfflinePlayer offlinePlayer) {
- return loadPlayerByUUID(offlinePlayer.getUniqueId(), offlinePlayer.getName(), offlinePlayer.isOnline());
- }
- public @NotNull PlayerProfile loadPlayerProfile(@NotNull String playerName) {
- return loadPlayerByName(playerName);
- }
- public @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid, @Nullable String playerName) {
- return loadPlayerByUUID(uuid, playerName, false);
- }
- public @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid) {
- return loadPlayerByUUID(uuid, null, false);
- }
- /**
- * Find and load a player by UUID
- *
- * @param uuid target uuid
- * @param playerName target player name
- * @param replaceName name to replace if the found name differs
- * @return a profile with the targets data or an unloaded profile if no data was found
- * @deprecated only use this if you know what you are doing, replacing the name can cause havoc
- */
- @Deprecated
- public @NotNull PlayerProfile loadPlayerByUUID(@NotNull UUID uuid, @Nullable String playerName, boolean replaceName) {
- BufferedReader in = null;
- synchronized (fileWritingLock) {
- try {
- // Open the user file
- in = new BufferedReader(new FileReader(usersFilePath));
- String line;
- while ((line = in.readLine()) != null) {
- // Find if the line contains the player we want.
- String[] rawSplitData = line.split(":");
- /* Don't read corrupt data */
- if(rawSplitData.length < (UUID_INDEX + 1)) {
- continue;
- }
- /* Does this entry have a UUID? */
- if (rawSplitData[UUID_INDEX].equalsIgnoreCase("NULL")
- || rawSplitData[UUID_INDEX].isEmpty()
- || rawSplitData[UUID_INDEX].equalsIgnoreCase("")) {
- continue; //No UUID entry found for this data in the DB, go to next entry
- }
- // Compare provided UUID to DB
- if (!rawSplitData[UUID_INDEX].equalsIgnoreCase(uuid.toString())) {
- continue; //Doesn't match, go to the next entry
- }
- /*
- * UUID Matched!
- * Making it this far means the current data line is considered a match
- */
- /* Check for nickname changes and update since we are here anyways */
- if(playerName != null) {
- if(replaceName) {
- logger.info("A users name is being updated, this can happen from either a call to our API or they simply changed their name");
- if (!rawSplitData[USERNAME_INDEX].equalsIgnoreCase(playerName)) {
- //logger.info("Name updated for player: " + rawSplitData[USERNAME_INDEX] + " => " + playerName);
- rawSplitData[USERNAME_INDEX] = playerName;
- }
- }
- }
- return loadFromLine(rawSplitData);
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // I have no idea why it's necessary to inline tryClose() here, but it removes
- // a resource leak warning, and I'm trusting the compiler on this one.
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- /*
- * No match was found in the file
- */
- return grabUnloadedProfile(uuid, playerName); //Create an empty new profile and return
- }
- private @NotNull PlayerProfile loadPlayerByName(@NotNull String playerName) {
- BufferedReader in = null;
- synchronized (fileWritingLock) {
- try {
- // Open the user file
- in = new BufferedReader(new FileReader(usersFilePath));
- String line;
- while ((line = in.readLine()) != null) {
- if(line.startsWith("#")) {
- continue;
- }
- // Find if the line contains the player we want.
- String[] rawSplitData = line.split(":");
- /* Don't read corrupt data */
- if(rawSplitData.length < (USERNAME_INDEX + 1)) {
- continue;
- }
- //If we couldn't find anyone
- if(playerName.equalsIgnoreCase(rawSplitData[USERNAME_INDEX])) {
- return loadFromLine(rawSplitData);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // I have no idea why it's necessary to inline tryClose() here, but it removes
- // a resource leak warning, and I'm trusting the compiler on this one.
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- //Return a new blank profile
- return new PlayerProfile(playerName, new UUID(0, 0), startingLevel);
- }
- private @NotNull PlayerProfile grabUnloadedProfile(@NotNull UUID uuid, @Nullable String playerName) {
- if(playerName == null) {
- playerName = ""; //No name for you boy!
- }
- return new PlayerProfile(playerName, uuid, 0);
- }
- public void convertUsers(DatabaseManager destination) {
- BufferedReader in = null;
- int convertedUsers = 0;
- long startMillis = System.currentTimeMillis();
- synchronized (fileWritingLock) {
- try {
- // Open the user file
- in = new BufferedReader(new FileReader(usersFilePath));
- String line;
- while ((line = in.readLine()) != null) {
- if(line.startsWith("#")) {
- continue;
- }
- String[] character = line.split(":");
- try {
- destination.saveUser(loadFromLine(character));
- } catch (Exception e) {
- e.printStackTrace();
- }
- convertedUsers++;
- Misc.printProgress(convertedUsers, progressInterval, startMillis);
- }
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- finally {
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- }
- public boolean saveUserUUID(String userName, UUID uuid) {
- boolean worked = false;
- int i = 0;
- BufferedReader in = null;
- FileWriter out = null;
- synchronized (fileWritingLock) {
- try {
- in = new BufferedReader(new FileReader(usersFilePath));
- StringBuilder writer = new StringBuilder();
- String line;
- while ((line = in.readLine()) != null) {
- String[] character = line.split(":");
- if (!worked && character[USERNAME_INDEX].equalsIgnoreCase(userName)) {
- if (character.length < 42) {
- logger.severe("Could not update UUID for " + userName + "!");
- logger.severe("Database entry is invalid.");
- continue;
- }
- line = line.replace(character[UUID_INDEX], uuid.toString());
- worked = true;
- }
- i++;
- writer.append(line).append("\r\n");
- }
- out = new FileWriter(usersFilePath); // Write out the new file
- out.write(writer.toString());
- }
- catch (Exception e) {
- logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
- }
- finally {
- logger.info(i + " entries written while saving UUID for " + userName);
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- if (out != null) {
- try {
- out.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- return worked;
- }
- public boolean saveUserUUIDs(Map<String, UUID> fetchedUUIDs) {
- BufferedReader in = null;
- FileWriter out = null;
- int i = 0;
- synchronized (fileWritingLock) {
- try {
- in = new BufferedReader(new FileReader(usersFilePath));
- StringBuilder writer = new StringBuilder();
- String line;
- while (((line = in.readLine()) != null)) {
- String[] character = line.split(":");
- if (!fetchedUUIDs.isEmpty() && fetchedUUIDs.containsKey(character[USERNAME_INDEX])) {
- if (character.length < 42) {
- logger.severe("Could not update UUID for " + character[USERNAME_INDEX] + "!");
- logger.severe("Database entry is invalid.");
- continue;
- }
- character[UUID_INDEX] = fetchedUUIDs.remove(character[USERNAME_INDEX]).toString();
- line = org.apache.commons.lang.StringUtils.join(character, ":") + ":";
- }
- i++;
- writer.append(line).append("\r\n");
- }
- out = new FileWriter(usersFilePath); // Write out the new file
- out.write(writer.toString());
- }
- catch (Exception e) {
- logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
- }
- finally {
- logger.info(i + " entries written while saving UUID batch");
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- if (out != null) {
- try {
- out.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- return true;
- }
- public List<String> getStoredUsers() {
- ArrayList<String> users = new ArrayList<>();
- BufferedReader in = null;
- synchronized (fileWritingLock) {
- try {
- // Open the user file
- in = new BufferedReader(new FileReader(usersFilePath));
- String line;
- while ((line = in.readLine()) != null) {
- String[] character = line.split(":");
- users.add(character[USERNAME_INDEX]);
- }
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- finally {
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- return users;
- }
- /**
- * Update the leader boards.
- */
- public void updateLeaderboards() {
- // Only update FFS leaderboards every 10 minutes.. this puts a lot of strain on the server (depending on the size of the database) and should not be done frequently
- if (System.currentTimeMillis() < lastUpdate + UPDATE_WAIT_TIME) {
- return;
- }
- lastUpdate = System.currentTimeMillis(); // Log when the last update was run
- powerLevels.clear(); // Clear old values from the power levels
- // Initialize lists
- List<PlayerStat> mining = new ArrayList<>();
- List<PlayerStat> woodcutting = new ArrayList<>();
- List<PlayerStat> herbalism = new ArrayList<>();
- List<PlayerStat> excavation = new ArrayList<>();
- List<PlayerStat> acrobatics = new ArrayList<>();
- List<PlayerStat> repair = new ArrayList<>();
- List<PlayerStat> swords = new ArrayList<>();
- List<PlayerStat> axes = new ArrayList<>();
- List<PlayerStat> archery = new ArrayList<>();
- List<PlayerStat> unarmed = new ArrayList<>();
- List<PlayerStat> taming = new ArrayList<>();
- List<PlayerStat> fishing = new ArrayList<>();
- List<PlayerStat> alchemy = new ArrayList<>();
- BufferedReader in = null;
- String playerName = null;
- // Read from the FlatFile database and fill our arrays with information
- synchronized (fileWritingLock) {
- try {
- in = new BufferedReader(new FileReader(usersFilePath));
- String line;
- while ((line = in.readLine()) != null) {
- String[] data = line.split(":");
- playerName = data[USERNAME_INDEX];
- int powerLevel = 0;
- Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(data);
- powerLevel += putStat(acrobatics, playerName, skills.get(PrimarySkillType.ACROBATICS));
- powerLevel += putStat(alchemy, playerName, skills.get(PrimarySkillType.ALCHEMY));
- powerLevel += putStat(archery, playerName, skills.get(PrimarySkillType.ARCHERY));
- powerLevel += putStat(axes, playerName, skills.get(PrimarySkillType.AXES));
- powerLevel += putStat(excavation, playerName, skills.get(PrimarySkillType.EXCAVATION));
- powerLevel += putStat(fishing, playerName, skills.get(PrimarySkillType.FISHING));
- powerLevel += putStat(herbalism, playerName, skills.get(PrimarySkillType.HERBALISM));
- powerLevel += putStat(mining, playerName, skills.get(PrimarySkillType.MINING));
- powerLevel += putStat(repair, playerName, skills.get(PrimarySkillType.REPAIR));
- powerLevel += putStat(swords, playerName, skills.get(PrimarySkillType.SWORDS));
- powerLevel += putStat(taming, playerName, skills.get(PrimarySkillType.TAMING));
- powerLevel += putStat(unarmed, playerName, skills.get(PrimarySkillType.UNARMED));
- powerLevel += putStat(woodcutting, playerName, skills.get(PrimarySkillType.WOODCUTTING));
- putStat(powerLevels, playerName, powerLevel);
- }
- }
- catch (Exception e) {
- logger.severe("Exception while reading " + usersFilePath + " during user " + playerName + " (Are you sure you formatted it correctly?) " + e);
- }
- finally {
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- SkillComparator c = new SkillComparator();
- mining.sort(c);
- woodcutting.sort(c);
- repair.sort(c);
- unarmed.sort(c);
- herbalism.sort(c);
- excavation.sort(c);
- archery.sort(c);
- swords.sort(c);
- axes.sort(c);
- acrobatics.sort(c);
- taming.sort(c);
- fishing.sort(c);
- alchemy.sort(c);
- powerLevels.sort(c);
- playerStatHash.put(PrimarySkillType.MINING, mining);
- playerStatHash.put(PrimarySkillType.WOODCUTTING, woodcutting);
- playerStatHash.put(PrimarySkillType.REPAIR, repair);
- playerStatHash.put(PrimarySkillType.UNARMED, unarmed);
- playerStatHash.put(PrimarySkillType.HERBALISM, herbalism);
- playerStatHash.put(PrimarySkillType.EXCAVATION, excavation);
- playerStatHash.put(PrimarySkillType.ARCHERY, archery);
- playerStatHash.put(PrimarySkillType.SWORDS, swords);
- playerStatHash.put(PrimarySkillType.AXES, axes);
- playerStatHash.put(PrimarySkillType.ACROBATICS, acrobatics);
- playerStatHash.put(PrimarySkillType.TAMING, taming);
- playerStatHash.put(PrimarySkillType.FISHING, fishing);
- playerStatHash.put(PrimarySkillType.ALCHEMY, alchemy);
- }
- private void initEmptyDB() {
- BufferedWriter bufferedWriter = null;
- synchronized (fileWritingLock) {
- try {
- // Open the file to write the player
- bufferedWriter = new BufferedWriter(new FileWriter(usersFilePath, true));
- DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
- LocalDateTime localDateTime = LocalDateTime.now();
- bufferedWriter.append("# mcMMO Database created on ").append(localDateTime.format(dateTimeFormatter)).append("\r\n"); //Empty file
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (bufferedWriter != null) {
- try {
- bufferedWriter.close();
- }
- catch (IOException e) {
- // Ignore
- }
- }
- }
- }
- }
- public @Nullable List<FlatFileDataFlag> checkFileHealthAndStructure() {
- ArrayList<FlatFileDataFlag> flagsFound = null;
- logger.info("(" + usersFile.getPath() + ") Validating database file..");
- FlatFileDataProcessor dataProcessor = null;
- if(!usersFile.exists()) {
- initEmptyDB();
- }
- if (usersFile.exists()) {
- BufferedReader bufferedReader = null;
- FileWriter fileWriter = null;
- synchronized (fileWritingLock) {
- dataProcessor = new FlatFileDataProcessor(logger);
- try {
- String currentLine;
- String dbCommentDate = null;
- bufferedReader = new BufferedReader(new FileReader(usersFilePath));
- //Analyze the data
- while ((currentLine = bufferedReader.readLine()) != null) {
- //Commented lines
- if(currentLine.startsWith("#") && dbCommentDate == null) { //The first commented line in the file is likely to be our note about when the file was created
- dbCommentDate = currentLine;
- continue;
- }
- if(currentLine.isEmpty())
- continue;
- //TODO: We are never passing empty lines, should we remove the flag for them?
- dataProcessor.processData(currentLine);
- }
- //Only update the file if needed
- if(dataProcessor.getFlatFileDataFlags().size() > 0) {
- flagsFound = new ArrayList<>(dataProcessor.getFlatFileDataFlags());
- logger.info("Saving the updated and or repaired FlatFile Database...");
- fileWriter = new FileWriter(usersFilePath);
- //Write data to file
- if(dbCommentDate != null)
- fileWriter.write(dbCommentDate + "\r\n");
- fileWriter.write(dataProcessor.processDataForSave().toString());
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- closeResources(bufferedReader, fileWriter);
- }
- }
- }
- if(flagsFound == null || flagsFound.size() == 0) {
- return null;
- } else {
- return flagsFound;
- }
- }
- private void closeResources(BufferedReader bufferedReader, FileWriter fileWriter) {
- if(bufferedReader != null) {
- try {
- bufferedReader.close();
- }
- catch (IOException e) {
- e.printStackTrace();
- }
- }
- if (fileWriter != null) {
- try {
- fileWriter.close();
- }
- catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- private Integer getPlayerRank(String playerName, List<PlayerStat> statsList) {
- if (statsList == null) {
- return null;
- }
- int currentPos = 1;
- for (PlayerStat stat : statsList) {
- if (stat.name.equalsIgnoreCase(playerName)) {
- return currentPos;
- }
- currentPos++;
- }
- return null;
- }
- private int putStat(List<PlayerStat> statList, String playerName, int statValue) {
- statList.add(new PlayerStat(playerName, statValue));
- return statValue;
- }
- private static class SkillComparator implements Comparator<PlayerStat> {
- @Override
- public int compare(PlayerStat o1, PlayerStat o2) {
- return (o2.statVal - o1.statVal);
- }
- }
- private PlayerProfile loadFromLine(@NotNull String[] character) {
- Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(character); // Skill levels
- Map<PrimarySkillType, Float> skillsXp = new EnumMap<>(PrimarySkillType.class); // Skill & XP
- Map<SuperAbilityType, Integer> skillsDATS = new EnumMap<>(SuperAbilityType.class); // Ability & Cooldown
- Map<UniqueDataType, Integer> uniquePlayerDataMap = new EnumMap<>(UniqueDataType.class);
- int scoreboardTipsShown;
- long lastLogin;
- String username = character[USERNAME_INDEX];
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.TAMING, EXP_TAMING, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.MINING, EXP_MINING, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.REPAIR, EXP_REPAIR, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.WOODCUTTING, EXP_WOODCUTTING, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.UNARMED, EXP_UNARMED, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.HERBALISM, EXP_HERBALISM, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.EXCAVATION, EXP_EXCAVATION, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.ARCHERY, EXP_ARCHERY, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.SWORDS, EXP_SWORDS, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.AXES, EXP_AXES, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.ACROBATICS, EXP_ACROBATICS, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.FISHING, EXP_FISHING, username);
- tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.ALCHEMY, EXP_ALCHEMY, username);
- // Taming - Unused
- tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.SUPER_BREAKER, COOLDOWN_SUPER_BREAKER, username);
- // Repair - Unused
- tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.TREE_FELLER, COOLDOWN_TREE_FELLER, username);
- tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.BERSERK, COOLDOWN_BERSERK, username);
- tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.GREEN_TERRA, COOLDOWN_GREEN_TERRA, username);
- tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.GIGA_DRILL_BREAKER, COOLDOWN_GIGA_DRILL_BREAKER, username);
- // Archery - Unused
- tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.SERRATED_STRIKES, COOLDOWN_SERRATED_STRIKES, username);
- tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.SKULL_SPLITTER, COOLDOWN_SKULL_SPLITTER, username);
- // Acrobatics - Unused
- tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.BLAST_MINING, COOLDOWN_BLAST_MINING, username);
- UUID uuid;
- try {
- uuid = UUID.fromString(character[UUID_INDEX]);
- }
- catch (Exception e) {
- uuid = null;
- }
- try {
- scoreboardTipsShown = Integer.parseInt(character[SCOREBOARD_TIPS]);
- }
- catch (Exception e) {
- scoreboardTipsShown = 0;
- }
- try {
- uniquePlayerDataMap.put(UniqueDataType.CHIMAERA_WING_DATS, Integer.valueOf(character[COOLDOWN_CHIMAERA_WING]));
- }
- catch (Exception e) {
- uniquePlayerDataMap.put(UniqueDataType.CHIMAERA_WING_DATS, 0);
- }
- try {
- lastLogin = Long.parseLong(character[OVERHAUL_LAST_LOGIN]);
- } catch (Exception e) {
- lastLogin = -1;
- }
- return new PlayerProfile(username, uuid, skills, skillsXp, skillsDATS, scoreboardTipsShown, uniquePlayerDataMap, lastLogin);
- }
- private void tryLoadSkillCooldownFromRawData(@NotNull Map<SuperAbilityType, Integer> cooldownMap, @NotNull String[] character, @NotNull SuperAbilityType superAbilityType, int cooldownSuperBreaker, @NotNull String userName) {
- try {
- cooldownMap.put(superAbilityType, Integer.valueOf(character[cooldownSuperBreaker]));
- } catch (NumberFormatException e) {
- logger.severe("Data corruption when trying to load the value for skill "+superAbilityType+" for player named " + userName+ " setting value to zero");
- e.printStackTrace();
- }
- }
- private void tryLoadSkillFloatValuesFromRawData(@NotNull Map<PrimarySkillType, Float> skillMap, @NotNull String[] character, @NotNull PrimarySkillType primarySkillType, int index, @NotNull String userName) {
- try {
- float valueFromString = Integer.parseInt(character[index]);
- skillMap.put(primarySkillType, valueFromString);
- } catch (NumberFormatException e) {
- skillMap.put(primarySkillType, 0F);
- logger.severe("Data corruption when trying to load the value for skill "+primarySkillType+" for player named " + userName+ " setting value to zero");
- e.printStackTrace();
- }
- }
- private void tryLoadSkillIntValuesFromRawData(@NotNull Map<PrimarySkillType, Integer> skillMap, @NotNull String[] character, @NotNull PrimarySkillType primarySkillType, int index, @NotNull String userName) {
- try {
- int valueFromString = Integer.parseInt(character[index]);
- skillMap.put(primarySkillType, valueFromString);
- } catch (NumberFormatException e) {
- skillMap.put(primarySkillType, 0);
- logger.severe("Data corruption when trying to load the value for skill "+primarySkillType+" for player named " + userName+ " setting value to zero");
- e.printStackTrace();
- }
- }
- private @NotNull Map<PrimarySkillType, Integer> getSkillMapFromLine(@NotNull String[] character) {
- EnumMap<PrimarySkillType, Integer> skills = new EnumMap<>(PrimarySkillType.class); // Skill & Level
- String username = character[USERNAME_INDEX];
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.ACROBATICS, SKILLS_ACROBATICS, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.TAMING, SKILLS_TAMING, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.MINING, SKILLS_MINING, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.REPAIR, SKILLS_REPAIR, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.WOODCUTTING, SKILLS_WOODCUTTING, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.UNARMED, SKILLS_UNARMED, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.HERBALISM, SKILLS_HERBALISM, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.EXCAVATION, SKILLS_EXCAVATION, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.ARCHERY, SKILLS_ARCHERY, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.SWORDS, SKILLS_SWORDS, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.AXES, SKILLS_AXES, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.FISHING, SKILLS_FISHING, username);
- tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.ALCHEMY, SKILLS_ALCHEMY, username);
- return skills;
- }
- public DatabaseType getDatabaseType() {
- return DatabaseType.FLATFILE;
- }
- public @NotNull File getUsersFile() {
- return usersFile;
- }
- @Override
- public void onDisable() { }
- }
|