FlatFileDatabaseManager.java 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256
  1. package com.gmail.nossr50.database;
  2. import com.gmail.nossr50.api.exceptions.InvalidSkillException;
  3. import com.gmail.nossr50.datatypes.database.DatabaseType;
  4. import com.gmail.nossr50.datatypes.database.PlayerStat;
  5. import com.gmail.nossr50.datatypes.player.PlayerProfile;
  6. import com.gmail.nossr50.datatypes.player.UniqueDataType;
  7. import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
  8. import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
  9. import com.gmail.nossr50.mcMMO;
  10. import com.gmail.nossr50.util.Misc;
  11. import com.gmail.nossr50.util.skills.SkillTools;
  12. import org.bukkit.OfflinePlayer;
  13. import org.bukkit.entity.Player;
  14. import org.jetbrains.annotations.NotNull;
  15. import org.jetbrains.annotations.Nullable;
  16. import java.io.*;
  17. import java.time.LocalDateTime;
  18. import java.time.format.DateTimeFormatter;
  19. import java.util.*;
  20. import java.util.logging.Logger;
  21. public final class FlatFileDatabaseManager implements DatabaseManager {
  22. public static final String IGNORED = "IGNORED";
  23. public static final String LEGACY_INVALID_OLD_USERNAME = "_INVALID_OLD_USERNAME_'";
  24. private final @NotNull EnumMap<PrimarySkillType, List<PlayerStat>> playerStatHash = new EnumMap<PrimarySkillType, List<PlayerStat>>(PrimarySkillType.class);
  25. private final @NotNull List<PlayerStat> powerLevels = new ArrayList<>();
  26. private long lastUpdate = 0;
  27. private final @NotNull String usersFilePath;
  28. private final @NotNull Logger logger;
  29. private final long purgeTime;
  30. private final int startingLevel;
  31. private final boolean testing;
  32. private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
  33. private final @NotNull File usersFile;
  34. private static final Object fileWritingLock = new Object();
  35. public static final int USERNAME_INDEX = 0;
  36. public static final int SKILLS_MINING = 1;
  37. public static final int EXP_MINING = 4;
  38. public static final int SKILLS_WOODCUTTING = 5;
  39. public static final int EXP_WOODCUTTING = 6;
  40. public static final int SKILLS_REPAIR = 7;
  41. public static final int SKILLS_UNARMED = 8;
  42. public static final int SKILLS_HERBALISM = 9;
  43. public static final int SKILLS_EXCAVATION = 10;
  44. public static final int SKILLS_ARCHERY = 11;
  45. public static final int SKILLS_SWORDS = 12;
  46. public static final int SKILLS_AXES = 13;
  47. public static final int SKILLS_ACROBATICS = 14;
  48. public static final int EXP_REPAIR = 15;
  49. public static final int EXP_UNARMED = 16;
  50. public static final int EXP_HERBALISM = 17;
  51. public static final int EXP_EXCAVATION = 18;
  52. public static final int EXP_ARCHERY = 19;
  53. public static final int EXP_SWORDS = 20;
  54. public static final int EXP_AXES = 21;
  55. public static final int EXP_ACROBATICS = 22;
  56. public static final int SKILLS_TAMING = 24;
  57. public static final int EXP_TAMING = 25;
  58. public static final int COOLDOWN_BERSERK = 26;
  59. public static final int COOLDOWN_GIGA_DRILL_BREAKER = 27;
  60. public static final int COOLDOWN_TREE_FELLER = 28;
  61. public static final int COOLDOWN_GREEN_TERRA = 29;
  62. public static final int COOLDOWN_SERRATED_STRIKES = 30;
  63. public static final int COOLDOWN_SKULL_SPLITTER = 31;
  64. public static final int COOLDOWN_SUPER_BREAKER = 32;
  65. public static final int SKILLS_FISHING = 34;
  66. public static final int EXP_FISHING = 35;
  67. public static final int COOLDOWN_BLAST_MINING = 36;
  68. public static final int LEGACY_LAST_LOGIN = 37;
  69. public static final int HEALTHBAR = 38;
  70. public static final int SKILLS_ALCHEMY = 39;
  71. public static final int EXP_ALCHEMY = 40;
  72. public static final int UUID_INDEX = 41;
  73. public static final int SCOREBOARD_TIPS = 42;
  74. public static final int COOLDOWN_CHIMAERA_WING = 43;
  75. public static final int OVERHAUL_LAST_LOGIN = 44;
  76. public static final int DATA_ENTRY_COUNT = OVERHAUL_LAST_LOGIN + 1; //Update this everytime new data is added
  77. protected FlatFileDatabaseManager(@NotNull File usersFile, @NotNull Logger logger, long purgeTime, int startingLevel, boolean testing) {
  78. this.usersFile = usersFile;
  79. this.usersFilePath = usersFile.getPath();
  80. this.logger = logger;
  81. this.purgeTime = purgeTime;
  82. this.startingLevel = startingLevel;
  83. this.testing = testing;
  84. if(!testing) {
  85. List<FlatFileDataFlag> flatFileDataFlags = checkFileHealthAndStructure();
  86. if(flatFileDataFlags != null) {
  87. if(flatFileDataFlags.size() > 0) {
  88. logger.info("Detected "+flatFileDataFlags.size() + " data entries which need correction.");
  89. }
  90. }
  91. updateLeaderboards();
  92. }
  93. }
  94. protected FlatFileDatabaseManager(@NotNull String usersFilePath, @NotNull Logger logger, long purgeTime, int startingLevel) {
  95. this(new File(usersFilePath), logger, purgeTime, startingLevel, false);
  96. }
  97. public int purgePowerlessUsers() {
  98. int purgedUsers = 0;
  99. logger.info("Purging powerless users...");
  100. BufferedReader in = null;
  101. FileWriter out = null;
  102. // This code is O(n) instead of O(n²)
  103. synchronized (fileWritingLock) {
  104. try {
  105. in = new BufferedReader(new FileReader(usersFilePath));
  106. StringBuilder writer = new StringBuilder();
  107. String line;
  108. while ((line = in.readLine()) != null) {
  109. String[] character = line.split(":");
  110. Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(character);
  111. boolean powerless = true;
  112. for (int skill : skills.values()) {
  113. if (skill != 0) {
  114. powerless = false;
  115. break;
  116. }
  117. }
  118. // If they're still around, rewrite them to the file.
  119. if (!powerless) {
  120. writer.append(line).append("\r\n");
  121. }
  122. else {
  123. purgedUsers++;
  124. }
  125. }
  126. // Write the new file
  127. out = new FileWriter(usersFilePath);
  128. out.write(writer.toString());
  129. }
  130. catch (IOException e) {
  131. logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
  132. }
  133. finally {
  134. if (in != null) {
  135. try {
  136. in.close();
  137. }
  138. catch (IOException e) {
  139. // Ignore
  140. }
  141. }
  142. if (out != null) {
  143. try {
  144. out.close();
  145. }
  146. catch (IOException e) {
  147. // Ignore
  148. }
  149. }
  150. }
  151. }
  152. logger.info("Purged " + purgedUsers + " users from the database.");
  153. return purgedUsers;
  154. }
  155. //TODO: Test this
  156. public void purgeOldUsers() {
  157. int removedPlayers = 0;
  158. long currentTime = System.currentTimeMillis();
  159. logger.info("Purging old users...");
  160. BufferedReader in = null;
  161. FileWriter out = null;
  162. // This code is O(n) instead of O(n²)
  163. synchronized (fileWritingLock) {
  164. try {
  165. in = new BufferedReader(new FileReader(usersFilePath));
  166. StringBuilder writer = new StringBuilder();
  167. String line;
  168. while ((line = in.readLine()) != null) {
  169. String[] character = line.split(":");
  170. String name = character[USERNAME_INDEX];
  171. long lastPlayed = 0;
  172. boolean rewrite = false;
  173. try {
  174. lastPlayed = Long.parseLong(character[OVERHAUL_LAST_LOGIN]);
  175. } catch (NumberFormatException e) {
  176. e.printStackTrace();
  177. }
  178. if (lastPlayed == -1) {
  179. OfflinePlayer player = mcMMO.p.getServer().getOfflinePlayer(name);
  180. if(player.getLastPlayed() != 0) {
  181. lastPlayed = player.getLastPlayed();
  182. rewrite = true;
  183. }
  184. }
  185. if (lastPlayed != -1 && lastPlayed != 0 && currentTime - lastPlayed > purgeTime) {
  186. removedPlayers++;
  187. } else {
  188. if (rewrite) {
  189. // Rewrite their data with a valid time
  190. character[OVERHAUL_LAST_LOGIN] = Long.toString(lastPlayed);
  191. String newLine = org.apache.commons.lang.StringUtils.join(character, ":");
  192. writer.append(newLine).append("\r\n");
  193. } else {
  194. writer.append(line).append("\r\n");
  195. }
  196. }
  197. }
  198. // Write the new file
  199. out = new FileWriter(usersFilePath);
  200. out.write(writer.toString());
  201. if(testing) {
  202. System.out.println(writer.toString());
  203. }
  204. }
  205. catch (IOException e) {
  206. logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
  207. }
  208. finally {
  209. if (in != null) {
  210. try {
  211. in.close();
  212. }
  213. catch (IOException e) {
  214. // Ignore
  215. }
  216. }
  217. if (out != null) {
  218. try {
  219. out.close();
  220. }
  221. catch (IOException e) {
  222. // Ignore
  223. }
  224. }
  225. }
  226. }
  227. logger.info("Purged " + removedPlayers + " users from the database.");
  228. }
  229. public boolean removeUser(String playerName, UUID uuid) {
  230. //NOTE: UUID is unused for FlatFile for this interface implementation
  231. boolean worked = false;
  232. BufferedReader in = null;
  233. FileWriter out = null;
  234. synchronized (fileWritingLock) {
  235. try {
  236. in = new BufferedReader(new FileReader(usersFilePath));
  237. StringBuilder writer = new StringBuilder();
  238. String line;
  239. while ((line = in.readLine()) != null) {
  240. // Write out the same file but when we get to the player we want to remove, we skip his line.
  241. if (!worked && line.split(":")[USERNAME_INDEX].equalsIgnoreCase(playerName)) {
  242. logger.info("User found, removing...");
  243. worked = true;
  244. continue; // Skip the player
  245. }
  246. writer.append(line).append("\r\n");
  247. }
  248. out = new FileWriter(usersFilePath); // Write out the new file
  249. out.write(writer.toString());
  250. }
  251. catch (Exception e) {
  252. logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
  253. }
  254. finally {
  255. if (in != null) {
  256. try {
  257. in.close();
  258. }
  259. catch (IOException e) {
  260. // Ignore
  261. }
  262. }
  263. if (out != null) {
  264. try {
  265. out.close();
  266. }
  267. catch (IOException e) {
  268. // Ignore
  269. }
  270. }
  271. }
  272. }
  273. Misc.profileCleanup(playerName);
  274. return worked;
  275. }
  276. @Override
  277. public void cleanupUser(UUID uuid) {
  278. //Not used in FlatFile
  279. }
  280. public boolean saveUser(@NotNull PlayerProfile profile) {
  281. String playerName = profile.getPlayerName();
  282. UUID uuid = profile.getUniqueId();
  283. BufferedReader in = null;
  284. FileWriter out = null;
  285. boolean corruptDataFound = false;
  286. synchronized (fileWritingLock) {
  287. try {
  288. // Open the file
  289. in = new BufferedReader(new FileReader(usersFilePath));
  290. StringBuilder writer = new StringBuilder();
  291. String line;
  292. boolean wroteUser = false;
  293. if(testing) {
  294. System.out.println("-- saveUser bufferedreader feed --");
  295. }
  296. // While not at the end of the file
  297. while ((line = in.readLine()) != null) {
  298. if(testing) {
  299. System.out.println(line);
  300. }
  301. if(line.startsWith("#")) {
  302. writer.append(line).append("\r\n");
  303. continue;
  304. }
  305. //Check for incomplete or corrupted data
  306. if(!line.contains(":")) {
  307. if(!corruptDataFound) {
  308. logger.severe("mcMMO found some unexpected or corrupted data in mcmmo.users and is removing it, it is possible some data has been lost.");
  309. corruptDataFound = true;
  310. }
  311. continue;
  312. }
  313. String[] splitData = line.split(":");
  314. //This would be rare, but check the splitData for having enough entries to contain a UUID
  315. if(splitData.length < UUID_INDEX) { //UUID have been in mcMMO DB for a very long time so any user without
  316. if(!corruptDataFound) {
  317. logger.severe("mcMMO found some unexpected or corrupted data in mcmmo.users and is removing it, it is possible some data has been lost.");
  318. corruptDataFound = true;
  319. }
  320. continue;
  321. }
  322. if (!(uuid != null
  323. && splitData[UUID_INDEX].equalsIgnoreCase(uuid.toString()))
  324. && !splitData[USERNAME_INDEX].equalsIgnoreCase(playerName)) {
  325. writer.append(line).append("\r\n"); //Not the user so write it to file and move on
  326. } else {
  327. //User found
  328. writeUserToLine(profile, playerName, uuid, writer);
  329. wroteUser = true;
  330. }
  331. }
  332. /*
  333. * If we couldn't find the user in the DB we need to add him
  334. */
  335. if(!wroteUser) {
  336. writeUserToLine(profile, playerName, uuid, writer);
  337. }
  338. if(testing) {
  339. System.out.println("-- saveUser (FileWriter contents before save) --");
  340. System.out.println(writer.toString());
  341. }
  342. // Write the new file
  343. out = new FileWriter(usersFilePath);
  344. out.write(writer.toString());
  345. return true;
  346. }
  347. catch (Exception e) {
  348. e.printStackTrace();
  349. return false;
  350. }
  351. finally {
  352. if (in != null) {
  353. try {
  354. in.close();
  355. }
  356. catch (IOException e) {
  357. // Ignore
  358. }
  359. }
  360. if (out != null) {
  361. try {
  362. out.close();
  363. }
  364. catch (IOException e) {
  365. // Ignore
  366. }
  367. }
  368. }
  369. }
  370. }
  371. public void writeUserToLine(@NotNull PlayerProfile profile, @NotNull String playerName, @Nullable UUID uuid, @NotNull Appendable appendable) throws IOException {
  372. appendable.append(playerName).append(":");
  373. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.MINING))).append(":");
  374. appendable.append(IGNORED).append(":");
  375. appendable.append(IGNORED).append(":");
  376. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.MINING))).append(":");
  377. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.WOODCUTTING))).append(":");
  378. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.WOODCUTTING))).append(":");
  379. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.REPAIR))).append(":");
  380. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.UNARMED))).append(":");
  381. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.HERBALISM))).append(":");
  382. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.EXCAVATION))).append(":");
  383. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ARCHERY))).append(":");
  384. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.SWORDS))).append(":");
  385. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.AXES))).append(":");
  386. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ACROBATICS))).append(":");
  387. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.REPAIR))).append(":");
  388. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.UNARMED))).append(":");
  389. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.HERBALISM))).append(":");
  390. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.EXCAVATION))).append(":");
  391. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ARCHERY))).append(":");
  392. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.SWORDS))).append(":");
  393. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.AXES))).append(":");
  394. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ACROBATICS))).append(":");
  395. appendable.append(IGNORED).append(":");
  396. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.TAMING))).append(":");
  397. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.TAMING))).append(":");
  398. appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.BERSERK))).append(":");
  399. appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.GIGA_DRILL_BREAKER))).append(":");
  400. appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.TREE_FELLER))).append(":");
  401. appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.GREEN_TERRA))).append(":");
  402. appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SERRATED_STRIKES))).append(":");
  403. appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SKULL_SPLITTER))).append(":");
  404. appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.SUPER_BREAKER))).append(":");
  405. appendable.append(IGNORED).append(":");
  406. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.FISHING))).append(":");
  407. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.FISHING))).append(":");
  408. appendable.append(String.valueOf(profile.getAbilityDATS(SuperAbilityType.BLAST_MINING))).append(":");
  409. appendable.append(IGNORED).append(":"); //Legacy last login
  410. appendable.append(IGNORED).append(":"); //mob health bar
  411. appendable.append(String.valueOf(profile.getSkillLevel(PrimarySkillType.ALCHEMY))).append(":");
  412. appendable.append(String.valueOf(profile.getSkillXpLevel(PrimarySkillType.ALCHEMY))).append(":");
  413. appendable.append(uuid != null ? uuid.toString() : "NULL").append(":");
  414. appendable.append(String.valueOf(profile.getScoreboardTipsShown())).append(":");
  415. appendable.append(String.valueOf(profile.getUniqueData(UniqueDataType.CHIMAERA_WING_DATS))).append(":");
  416. appendable.append(String.valueOf(profile.getLastLogin())).append(":"); //overhaul last login
  417. appendable.append("\r\n");
  418. }
  419. public @NotNull List<PlayerStat> readLeaderboard(@Nullable PrimarySkillType primarySkillType, int pageNumber, int statsPerPage) throws InvalidSkillException {
  420. //Fix for a plugin that people are using that is throwing SQL errors
  421. if(primarySkillType != null && SkillTools.isChildSkill(primarySkillType)) {
  422. 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!");
  423. 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!");
  424. }
  425. updateLeaderboards();
  426. List<PlayerStat> statsList = primarySkillType == null ? powerLevels : playerStatHash.get(primarySkillType);
  427. int fromIndex = (Math.max(pageNumber, 1) - 1) * statsPerPage;
  428. return statsList.subList(Math.min(fromIndex, statsList.size()), Math.min(fromIndex + statsPerPage, statsList.size()));
  429. }
  430. public Map<PrimarySkillType, Integer> readRank(String playerName) {
  431. updateLeaderboards();
  432. Map<PrimarySkillType, Integer> skills = new EnumMap<PrimarySkillType, Integer>(PrimarySkillType.class);
  433. for (PrimarySkillType skill : SkillTools.NON_CHILD_SKILLS) {
  434. skills.put(skill, getPlayerRank(playerName, playerStatHash.get(skill)));
  435. }
  436. skills.put(null, getPlayerRank(playerName, powerLevels));
  437. return skills;
  438. }
  439. public @NotNull PlayerProfile newUser(@NotNull Player player) {
  440. return new PlayerProfile(player.getName(), player.getUniqueId(), true, startingLevel);
  441. }
  442. public @NotNull PlayerProfile newUser(@NotNull String playerName, @NotNull UUID uuid) {
  443. PlayerProfile playerProfile = new PlayerProfile(playerName, uuid, true, startingLevel);
  444. synchronized (fileWritingLock) {
  445. try(BufferedReader bufferedReader = new BufferedReader(new FileReader(usersFilePath))) {
  446. StringBuilder stringBuilder = new StringBuilder();
  447. String line;
  448. //Build up the file
  449. while((line = bufferedReader.readLine()) != null) {
  450. stringBuilder.append(line).append("\r\n");
  451. }
  452. try (FileWriter fileWriter = new FileWriter(usersFile)) {
  453. writeUserToLine(playerProfile, playerName, uuid, stringBuilder);
  454. fileWriter.write(stringBuilder.toString());
  455. } catch (Exception e) {
  456. e.printStackTrace();
  457. }
  458. } catch (IOException e) {
  459. e.printStackTrace();
  460. }
  461. }
  462. return playerProfile;
  463. }
  464. public @NotNull PlayerProfile loadPlayerProfile(@NotNull OfflinePlayer offlinePlayer) {
  465. return loadPlayerByUUID(offlinePlayer.getUniqueId(), offlinePlayer.getName(), offlinePlayer.isOnline());
  466. }
  467. public @NotNull PlayerProfile loadPlayerProfile(@NotNull String playerName) {
  468. return loadPlayerByName(playerName);
  469. }
  470. public @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid, @Nullable String playerName) {
  471. return loadPlayerByUUID(uuid, playerName, false);
  472. }
  473. public @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid) {
  474. return loadPlayerByUUID(uuid, null, false);
  475. }
  476. /**
  477. * Find and load a player by UUID
  478. *
  479. * @param uuid target uuid
  480. * @param playerName target player name
  481. * @param replaceName name to replace if the found name differs
  482. * @return a profile with the targets data or an unloaded profile if no data was found
  483. * @deprecated only use this if you know what you are doing, replacing the name can cause havoc
  484. */
  485. @Deprecated
  486. public @NotNull PlayerProfile loadPlayerByUUID(@NotNull UUID uuid, @Nullable String playerName, boolean replaceName) {
  487. BufferedReader in = null;
  488. synchronized (fileWritingLock) {
  489. try {
  490. // Open the user file
  491. in = new BufferedReader(new FileReader(usersFilePath));
  492. String line;
  493. while ((line = in.readLine()) != null) {
  494. // Find if the line contains the player we want.
  495. String[] rawSplitData = line.split(":");
  496. /* Don't read corrupt data */
  497. if(rawSplitData.length < (UUID_INDEX + 1)) {
  498. continue;
  499. }
  500. /* Does this entry have a UUID? */
  501. if (rawSplitData[UUID_INDEX].equalsIgnoreCase("NULL")
  502. || rawSplitData[UUID_INDEX].isEmpty()
  503. || rawSplitData[UUID_INDEX].equalsIgnoreCase("")) {
  504. continue; //No UUID entry found for this data in the DB, go to next entry
  505. }
  506. // Compare provided UUID to DB
  507. if (!rawSplitData[UUID_INDEX].equalsIgnoreCase(uuid.toString())) {
  508. continue; //Doesn't match, go to the next entry
  509. }
  510. /*
  511. * UUID Matched!
  512. * Making it this far means the current data line is considered a match
  513. */
  514. /* Check for nickname changes and update since we are here anyways */
  515. if(playerName != null) {
  516. if(replaceName) {
  517. logger.info("A users name is being updated, this can happen from either a call to our API or they simply changed their name");
  518. if (!rawSplitData[USERNAME_INDEX].equalsIgnoreCase(playerName)) {
  519. //logger.info("Name updated for player: " + rawSplitData[USERNAME_INDEX] + " => " + playerName);
  520. rawSplitData[USERNAME_INDEX] = playerName;
  521. }
  522. }
  523. }
  524. return loadFromLine(rawSplitData);
  525. }
  526. } catch (Exception e) {
  527. e.printStackTrace();
  528. } finally {
  529. // I have no idea why it's necessary to inline tryClose() here, but it removes
  530. // a resource leak warning, and I'm trusting the compiler on this one.
  531. if (in != null) {
  532. try {
  533. in.close();
  534. } catch (IOException e) {
  535. // Ignore
  536. }
  537. }
  538. }
  539. }
  540. /*
  541. * No match was found in the file
  542. */
  543. return grabUnloadedProfile(uuid, playerName); //Create an empty new profile and return
  544. }
  545. private @NotNull PlayerProfile loadPlayerByName(@NotNull String playerName) {
  546. BufferedReader in = null;
  547. synchronized (fileWritingLock) {
  548. try {
  549. // Open the user file
  550. in = new BufferedReader(new FileReader(usersFilePath));
  551. String line;
  552. while ((line = in.readLine()) != null) {
  553. if(line.startsWith("#")) {
  554. continue;
  555. }
  556. // Find if the line contains the player we want.
  557. String[] rawSplitData = line.split(":");
  558. /* Don't read corrupt data */
  559. if(rawSplitData.length < (USERNAME_INDEX + 1)) {
  560. continue;
  561. }
  562. //If we couldn't find anyone
  563. if(playerName.equalsIgnoreCase(rawSplitData[USERNAME_INDEX])) {
  564. return loadFromLine(rawSplitData);
  565. }
  566. }
  567. } catch (Exception e) {
  568. e.printStackTrace();
  569. } finally {
  570. // I have no idea why it's necessary to inline tryClose() here, but it removes
  571. // a resource leak warning, and I'm trusting the compiler on this one.
  572. if (in != null) {
  573. try {
  574. in.close();
  575. } catch (IOException e) {
  576. // Ignore
  577. }
  578. }
  579. }
  580. }
  581. //Return a new blank profile
  582. return new PlayerProfile(playerName, new UUID(0, 0), startingLevel);
  583. }
  584. private @NotNull PlayerProfile grabUnloadedProfile(@NotNull UUID uuid, @Nullable String playerName) {
  585. if(playerName == null) {
  586. playerName = ""; //No name for you boy!
  587. }
  588. return new PlayerProfile(playerName, uuid, 0);
  589. }
  590. public void convertUsers(DatabaseManager destination) {
  591. BufferedReader in = null;
  592. int convertedUsers = 0;
  593. long startMillis = System.currentTimeMillis();
  594. synchronized (fileWritingLock) {
  595. try {
  596. // Open the user file
  597. in = new BufferedReader(new FileReader(usersFilePath));
  598. String line;
  599. while ((line = in.readLine()) != null) {
  600. if(line.startsWith("#")) {
  601. continue;
  602. }
  603. String[] character = line.split(":");
  604. try {
  605. destination.saveUser(loadFromLine(character));
  606. } catch (Exception e) {
  607. e.printStackTrace();
  608. }
  609. convertedUsers++;
  610. Misc.printProgress(convertedUsers, progressInterval, startMillis);
  611. }
  612. }
  613. catch (Exception e) {
  614. e.printStackTrace();
  615. }
  616. finally {
  617. if (in != null) {
  618. try {
  619. in.close();
  620. }
  621. catch (IOException e) {
  622. // Ignore
  623. }
  624. }
  625. }
  626. }
  627. }
  628. public boolean saveUserUUID(String userName, UUID uuid) {
  629. boolean worked = false;
  630. int i = 0;
  631. BufferedReader in = null;
  632. FileWriter out = null;
  633. synchronized (fileWritingLock) {
  634. try {
  635. in = new BufferedReader(new FileReader(usersFilePath));
  636. StringBuilder writer = new StringBuilder();
  637. String line;
  638. while ((line = in.readLine()) != null) {
  639. String[] character = line.split(":");
  640. if (!worked && character[USERNAME_INDEX].equalsIgnoreCase(userName)) {
  641. if (character.length < 42) {
  642. logger.severe("Could not update UUID for " + userName + "!");
  643. logger.severe("Database entry is invalid.");
  644. continue;
  645. }
  646. line = line.replace(character[UUID_INDEX], uuid.toString());
  647. worked = true;
  648. }
  649. i++;
  650. writer.append(line).append("\r\n");
  651. }
  652. out = new FileWriter(usersFilePath); // Write out the new file
  653. out.write(writer.toString());
  654. }
  655. catch (Exception e) {
  656. logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
  657. }
  658. finally {
  659. logger.info(i + " entries written while saving UUID for " + userName);
  660. if (in != null) {
  661. try {
  662. in.close();
  663. }
  664. catch (IOException e) {
  665. // Ignore
  666. }
  667. }
  668. if (out != null) {
  669. try {
  670. out.close();
  671. }
  672. catch (IOException e) {
  673. // Ignore
  674. }
  675. }
  676. }
  677. }
  678. return worked;
  679. }
  680. public boolean saveUserUUIDs(Map<String, UUID> fetchedUUIDs) {
  681. BufferedReader in = null;
  682. FileWriter out = null;
  683. int i = 0;
  684. synchronized (fileWritingLock) {
  685. try {
  686. in = new BufferedReader(new FileReader(usersFilePath));
  687. StringBuilder writer = new StringBuilder();
  688. String line;
  689. while (((line = in.readLine()) != null)) {
  690. String[] character = line.split(":");
  691. if (!fetchedUUIDs.isEmpty() && fetchedUUIDs.containsKey(character[USERNAME_INDEX])) {
  692. if (character.length < 42) {
  693. logger.severe("Could not update UUID for " + character[USERNAME_INDEX] + "!");
  694. logger.severe("Database entry is invalid.");
  695. continue;
  696. }
  697. character[UUID_INDEX] = fetchedUUIDs.remove(character[USERNAME_INDEX]).toString();
  698. line = org.apache.commons.lang.StringUtils.join(character, ":") + ":";
  699. }
  700. i++;
  701. writer.append(line).append("\r\n");
  702. }
  703. out = new FileWriter(usersFilePath); // Write out the new file
  704. out.write(writer.toString());
  705. }
  706. catch (Exception e) {
  707. logger.severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e);
  708. }
  709. finally {
  710. logger.info(i + " entries written while saving UUID batch");
  711. if (in != null) {
  712. try {
  713. in.close();
  714. }
  715. catch (IOException e) {
  716. // Ignore
  717. }
  718. }
  719. if (out != null) {
  720. try {
  721. out.close();
  722. }
  723. catch (IOException e) {
  724. // Ignore
  725. }
  726. }
  727. }
  728. }
  729. return true;
  730. }
  731. public List<String> getStoredUsers() {
  732. ArrayList<String> users = new ArrayList<>();
  733. BufferedReader in = null;
  734. synchronized (fileWritingLock) {
  735. try {
  736. // Open the user file
  737. in = new BufferedReader(new FileReader(usersFilePath));
  738. String line;
  739. while ((line = in.readLine()) != null) {
  740. String[] character = line.split(":");
  741. users.add(character[USERNAME_INDEX]);
  742. }
  743. }
  744. catch (Exception e) {
  745. e.printStackTrace();
  746. }
  747. finally {
  748. if (in != null) {
  749. try {
  750. in.close();
  751. }
  752. catch (IOException e) {
  753. // Ignore
  754. }
  755. }
  756. }
  757. }
  758. return users;
  759. }
  760. /**
  761. * Update the leader boards.
  762. */
  763. public void updateLeaderboards() {
  764. // 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
  765. if (System.currentTimeMillis() < lastUpdate + UPDATE_WAIT_TIME) {
  766. return;
  767. }
  768. lastUpdate = System.currentTimeMillis(); // Log when the last update was run
  769. powerLevels.clear(); // Clear old values from the power levels
  770. // Initialize lists
  771. List<PlayerStat> mining = new ArrayList<>();
  772. List<PlayerStat> woodcutting = new ArrayList<>();
  773. List<PlayerStat> herbalism = new ArrayList<>();
  774. List<PlayerStat> excavation = new ArrayList<>();
  775. List<PlayerStat> acrobatics = new ArrayList<>();
  776. List<PlayerStat> repair = new ArrayList<>();
  777. List<PlayerStat> swords = new ArrayList<>();
  778. List<PlayerStat> axes = new ArrayList<>();
  779. List<PlayerStat> archery = new ArrayList<>();
  780. List<PlayerStat> unarmed = new ArrayList<>();
  781. List<PlayerStat> taming = new ArrayList<>();
  782. List<PlayerStat> fishing = new ArrayList<>();
  783. List<PlayerStat> alchemy = new ArrayList<>();
  784. BufferedReader in = null;
  785. String playerName = null;
  786. // Read from the FlatFile database and fill our arrays with information
  787. synchronized (fileWritingLock) {
  788. try {
  789. in = new BufferedReader(new FileReader(usersFilePath));
  790. String line;
  791. while ((line = in.readLine()) != null) {
  792. String[] data = line.split(":");
  793. playerName = data[USERNAME_INDEX];
  794. int powerLevel = 0;
  795. Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(data);
  796. powerLevel += putStat(acrobatics, playerName, skills.get(PrimarySkillType.ACROBATICS));
  797. powerLevel += putStat(alchemy, playerName, skills.get(PrimarySkillType.ALCHEMY));
  798. powerLevel += putStat(archery, playerName, skills.get(PrimarySkillType.ARCHERY));
  799. powerLevel += putStat(axes, playerName, skills.get(PrimarySkillType.AXES));
  800. powerLevel += putStat(excavation, playerName, skills.get(PrimarySkillType.EXCAVATION));
  801. powerLevel += putStat(fishing, playerName, skills.get(PrimarySkillType.FISHING));
  802. powerLevel += putStat(herbalism, playerName, skills.get(PrimarySkillType.HERBALISM));
  803. powerLevel += putStat(mining, playerName, skills.get(PrimarySkillType.MINING));
  804. powerLevel += putStat(repair, playerName, skills.get(PrimarySkillType.REPAIR));
  805. powerLevel += putStat(swords, playerName, skills.get(PrimarySkillType.SWORDS));
  806. powerLevel += putStat(taming, playerName, skills.get(PrimarySkillType.TAMING));
  807. powerLevel += putStat(unarmed, playerName, skills.get(PrimarySkillType.UNARMED));
  808. powerLevel += putStat(woodcutting, playerName, skills.get(PrimarySkillType.WOODCUTTING));
  809. putStat(powerLevels, playerName, powerLevel);
  810. }
  811. }
  812. catch (Exception e) {
  813. logger.severe("Exception while reading " + usersFilePath + " during user " + playerName + " (Are you sure you formatted it correctly?) " + e);
  814. }
  815. finally {
  816. if (in != null) {
  817. try {
  818. in.close();
  819. }
  820. catch (IOException e) {
  821. // Ignore
  822. }
  823. }
  824. }
  825. }
  826. SkillComparator c = new SkillComparator();
  827. mining.sort(c);
  828. woodcutting.sort(c);
  829. repair.sort(c);
  830. unarmed.sort(c);
  831. herbalism.sort(c);
  832. excavation.sort(c);
  833. archery.sort(c);
  834. swords.sort(c);
  835. axes.sort(c);
  836. acrobatics.sort(c);
  837. taming.sort(c);
  838. fishing.sort(c);
  839. alchemy.sort(c);
  840. powerLevels.sort(c);
  841. playerStatHash.put(PrimarySkillType.MINING, mining);
  842. playerStatHash.put(PrimarySkillType.WOODCUTTING, woodcutting);
  843. playerStatHash.put(PrimarySkillType.REPAIR, repair);
  844. playerStatHash.put(PrimarySkillType.UNARMED, unarmed);
  845. playerStatHash.put(PrimarySkillType.HERBALISM, herbalism);
  846. playerStatHash.put(PrimarySkillType.EXCAVATION, excavation);
  847. playerStatHash.put(PrimarySkillType.ARCHERY, archery);
  848. playerStatHash.put(PrimarySkillType.SWORDS, swords);
  849. playerStatHash.put(PrimarySkillType.AXES, axes);
  850. playerStatHash.put(PrimarySkillType.ACROBATICS, acrobatics);
  851. playerStatHash.put(PrimarySkillType.TAMING, taming);
  852. playerStatHash.put(PrimarySkillType.FISHING, fishing);
  853. playerStatHash.put(PrimarySkillType.ALCHEMY, alchemy);
  854. }
  855. private void initEmptyDB() {
  856. BufferedWriter bufferedWriter = null;
  857. synchronized (fileWritingLock) {
  858. try {
  859. // Open the file to write the player
  860. bufferedWriter = new BufferedWriter(new FileWriter(usersFilePath, true));
  861. DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
  862. LocalDateTime localDateTime = LocalDateTime.now();
  863. bufferedWriter.append("# mcMMO Database created on ").append(localDateTime.format(dateTimeFormatter)).append("\r\n"); //Empty file
  864. } catch (Exception e) {
  865. e.printStackTrace();
  866. } finally {
  867. if (bufferedWriter != null) {
  868. try {
  869. bufferedWriter.close();
  870. }
  871. catch (IOException e) {
  872. // Ignore
  873. }
  874. }
  875. }
  876. }
  877. }
  878. public @Nullable List<FlatFileDataFlag> checkFileHealthAndStructure() {
  879. ArrayList<FlatFileDataFlag> flagsFound = null;
  880. logger.info("(" + usersFile.getPath() + ") Validating database file..");
  881. FlatFileDataProcessor dataProcessor = null;
  882. if(!usersFile.exists()) {
  883. initEmptyDB();
  884. }
  885. if (usersFile.exists()) {
  886. BufferedReader bufferedReader = null;
  887. FileWriter fileWriter = null;
  888. synchronized (fileWritingLock) {
  889. dataProcessor = new FlatFileDataProcessor(logger);
  890. try {
  891. String currentLine;
  892. String dbCommentDate = null;
  893. bufferedReader = new BufferedReader(new FileReader(usersFilePath));
  894. //Analyze the data
  895. while ((currentLine = bufferedReader.readLine()) != null) {
  896. //Commented lines
  897. if(currentLine.startsWith("#") && dbCommentDate == null) { //The first commented line in the file is likely to be our note about when the file was created
  898. dbCommentDate = currentLine;
  899. continue;
  900. }
  901. if(currentLine.isEmpty())
  902. continue;
  903. //TODO: We are never passing empty lines, should we remove the flag for them?
  904. dataProcessor.processData(currentLine);
  905. }
  906. //Only update the file if needed
  907. if(dataProcessor.getFlatFileDataFlags().size() > 0) {
  908. flagsFound = new ArrayList<>(dataProcessor.getFlatFileDataFlags());
  909. logger.info("Saving the updated and or repaired FlatFile Database...");
  910. fileWriter = new FileWriter(usersFilePath);
  911. //Write data to file
  912. if(dbCommentDate != null)
  913. fileWriter.write(dbCommentDate + "\r\n");
  914. fileWriter.write(dataProcessor.processDataForSave().toString());
  915. }
  916. } catch (IOException e) {
  917. e.printStackTrace();
  918. } finally {
  919. closeResources(bufferedReader, fileWriter);
  920. }
  921. }
  922. }
  923. if(flagsFound == null || flagsFound.size() == 0) {
  924. return null;
  925. } else {
  926. return flagsFound;
  927. }
  928. }
  929. private void closeResources(BufferedReader bufferedReader, FileWriter fileWriter) {
  930. if(bufferedReader != null) {
  931. try {
  932. bufferedReader.close();
  933. }
  934. catch (IOException e) {
  935. e.printStackTrace();
  936. }
  937. }
  938. if (fileWriter != null) {
  939. try {
  940. fileWriter.close();
  941. }
  942. catch (IOException e) {
  943. e.printStackTrace();
  944. }
  945. }
  946. }
  947. private Integer getPlayerRank(String playerName, List<PlayerStat> statsList) {
  948. if (statsList == null) {
  949. return null;
  950. }
  951. int currentPos = 1;
  952. for (PlayerStat stat : statsList) {
  953. if (stat.name.equalsIgnoreCase(playerName)) {
  954. return currentPos;
  955. }
  956. currentPos++;
  957. }
  958. return null;
  959. }
  960. private int putStat(List<PlayerStat> statList, String playerName, int statValue) {
  961. statList.add(new PlayerStat(playerName, statValue));
  962. return statValue;
  963. }
  964. private static class SkillComparator implements Comparator<PlayerStat> {
  965. @Override
  966. public int compare(PlayerStat o1, PlayerStat o2) {
  967. return (o2.statVal - o1.statVal);
  968. }
  969. }
  970. private PlayerProfile loadFromLine(@NotNull String[] character) {
  971. Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(character); // Skill levels
  972. Map<PrimarySkillType, Float> skillsXp = new EnumMap<>(PrimarySkillType.class); // Skill & XP
  973. Map<SuperAbilityType, Integer> skillsDATS = new EnumMap<>(SuperAbilityType.class); // Ability & Cooldown
  974. Map<UniqueDataType, Integer> uniquePlayerDataMap = new EnumMap<>(UniqueDataType.class);
  975. int scoreboardTipsShown;
  976. long lastLogin;
  977. String username = character[USERNAME_INDEX];
  978. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.TAMING, EXP_TAMING, username);
  979. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.MINING, EXP_MINING, username);
  980. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.REPAIR, EXP_REPAIR, username);
  981. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.WOODCUTTING, EXP_WOODCUTTING, username);
  982. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.UNARMED, EXP_UNARMED, username);
  983. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.HERBALISM, EXP_HERBALISM, username);
  984. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.EXCAVATION, EXP_EXCAVATION, username);
  985. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.ARCHERY, EXP_ARCHERY, username);
  986. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.SWORDS, EXP_SWORDS, username);
  987. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.AXES, EXP_AXES, username);
  988. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.ACROBATICS, EXP_ACROBATICS, username);
  989. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.FISHING, EXP_FISHING, username);
  990. tryLoadSkillFloatValuesFromRawData(skillsXp, character, PrimarySkillType.ALCHEMY, EXP_ALCHEMY, username);
  991. // Taming - Unused
  992. tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.SUPER_BREAKER, COOLDOWN_SUPER_BREAKER, username);
  993. // Repair - Unused
  994. tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.TREE_FELLER, COOLDOWN_TREE_FELLER, username);
  995. tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.BERSERK, COOLDOWN_BERSERK, username);
  996. tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.GREEN_TERRA, COOLDOWN_GREEN_TERRA, username);
  997. tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.GIGA_DRILL_BREAKER, COOLDOWN_GIGA_DRILL_BREAKER, username);
  998. // Archery - Unused
  999. tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.SERRATED_STRIKES, COOLDOWN_SERRATED_STRIKES, username);
  1000. tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.SKULL_SPLITTER, COOLDOWN_SKULL_SPLITTER, username);
  1001. // Acrobatics - Unused
  1002. tryLoadSkillCooldownFromRawData(skillsDATS, character, SuperAbilityType.BLAST_MINING, COOLDOWN_BLAST_MINING, username);
  1003. UUID uuid;
  1004. try {
  1005. uuid = UUID.fromString(character[UUID_INDEX]);
  1006. }
  1007. catch (Exception e) {
  1008. uuid = null;
  1009. }
  1010. try {
  1011. scoreboardTipsShown = Integer.parseInt(character[SCOREBOARD_TIPS]);
  1012. }
  1013. catch (Exception e) {
  1014. scoreboardTipsShown = 0;
  1015. }
  1016. try {
  1017. uniquePlayerDataMap.put(UniqueDataType.CHIMAERA_WING_DATS, Integer.valueOf(character[COOLDOWN_CHIMAERA_WING]));
  1018. }
  1019. catch (Exception e) {
  1020. uniquePlayerDataMap.put(UniqueDataType.CHIMAERA_WING_DATS, 0);
  1021. }
  1022. try {
  1023. lastLogin = Long.parseLong(character[OVERHAUL_LAST_LOGIN]);
  1024. } catch (Exception e) {
  1025. lastLogin = -1;
  1026. }
  1027. return new PlayerProfile(username, uuid, skills, skillsXp, skillsDATS, scoreboardTipsShown, uniquePlayerDataMap, lastLogin);
  1028. }
  1029. private void tryLoadSkillCooldownFromRawData(@NotNull Map<SuperAbilityType, Integer> cooldownMap, @NotNull String[] character, @NotNull SuperAbilityType superAbilityType, int cooldownSuperBreaker, @NotNull String userName) {
  1030. try {
  1031. cooldownMap.put(superAbilityType, Integer.valueOf(character[cooldownSuperBreaker]));
  1032. } catch (NumberFormatException e) {
  1033. logger.severe("Data corruption when trying to load the value for skill "+superAbilityType+" for player named " + userName+ " setting value to zero");
  1034. e.printStackTrace();
  1035. }
  1036. }
  1037. private void tryLoadSkillFloatValuesFromRawData(@NotNull Map<PrimarySkillType, Float> skillMap, @NotNull String[] character, @NotNull PrimarySkillType primarySkillType, int index, @NotNull String userName) {
  1038. try {
  1039. float valueFromString = Integer.parseInt(character[index]);
  1040. skillMap.put(primarySkillType, valueFromString);
  1041. } catch (NumberFormatException e) {
  1042. skillMap.put(primarySkillType, 0F);
  1043. logger.severe("Data corruption when trying to load the value for skill "+primarySkillType+" for player named " + userName+ " setting value to zero");
  1044. e.printStackTrace();
  1045. }
  1046. }
  1047. private void tryLoadSkillIntValuesFromRawData(@NotNull Map<PrimarySkillType, Integer> skillMap, @NotNull String[] character, @NotNull PrimarySkillType primarySkillType, int index, @NotNull String userName) {
  1048. try {
  1049. int valueFromString = Integer.parseInt(character[index]);
  1050. skillMap.put(primarySkillType, valueFromString);
  1051. } catch (NumberFormatException e) {
  1052. skillMap.put(primarySkillType, 0);
  1053. logger.severe("Data corruption when trying to load the value for skill "+primarySkillType+" for player named " + userName+ " setting value to zero");
  1054. e.printStackTrace();
  1055. }
  1056. }
  1057. private @NotNull Map<PrimarySkillType, Integer> getSkillMapFromLine(@NotNull String[] character) {
  1058. EnumMap<PrimarySkillType, Integer> skills = new EnumMap<>(PrimarySkillType.class); // Skill & Level
  1059. String username = character[USERNAME_INDEX];
  1060. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.ACROBATICS, SKILLS_ACROBATICS, username);
  1061. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.TAMING, SKILLS_TAMING, username);
  1062. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.MINING, SKILLS_MINING, username);
  1063. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.REPAIR, SKILLS_REPAIR, username);
  1064. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.WOODCUTTING, SKILLS_WOODCUTTING, username);
  1065. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.UNARMED, SKILLS_UNARMED, username);
  1066. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.HERBALISM, SKILLS_HERBALISM, username);
  1067. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.EXCAVATION, SKILLS_EXCAVATION, username);
  1068. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.ARCHERY, SKILLS_ARCHERY, username);
  1069. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.SWORDS, SKILLS_SWORDS, username);
  1070. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.AXES, SKILLS_AXES, username);
  1071. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.FISHING, SKILLS_FISHING, username);
  1072. tryLoadSkillIntValuesFromRawData(skills, character, PrimarySkillType.ALCHEMY, SKILLS_ALCHEMY, username);
  1073. return skills;
  1074. }
  1075. public DatabaseType getDatabaseType() {
  1076. return DatabaseType.FLATFILE;
  1077. }
  1078. public @NotNull File getUsersFile() {
  1079. return usersFile;
  1080. }
  1081. @Override
  1082. public void onDisable() { }
  1083. }