FlatFileDatabaseManager.java 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409
  1. package com.gmail.nossr50.database;
  2. import com.gmail.nossr50.config.AdvancedConfig;
  3. import com.gmail.nossr50.config.Config;
  4. import com.gmail.nossr50.datatypes.database.DatabaseType;
  5. import com.gmail.nossr50.datatypes.database.PlayerStat;
  6. import com.gmail.nossr50.datatypes.database.UpgradeType;
  7. import com.gmail.nossr50.datatypes.player.*;
  8. import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
  9. import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
  10. import com.gmail.nossr50.mcMMO;
  11. import com.gmail.nossr50.runnables.database.UUIDUpdateAsyncTask;
  12. import com.gmail.nossr50.util.Misc;
  13. import com.gmail.nossr50.util.experience.MMOExperienceBarManager;
  14. import com.gmail.nossr50.util.skills.SkillUtils;
  15. import com.google.common.collect.ImmutableMap;
  16. import com.neetgames.mcmmo.UniqueDataType;
  17. import com.neetgames.mcmmo.exceptions.InvalidSkillException;
  18. import com.neetgames.mcmmo.player.MMOPlayer;
  19. import com.neetgames.mcmmo.skill.SkillBossBarState;
  20. import org.bukkit.OfflinePlayer;
  21. import org.bukkit.entity.Player;
  22. import org.jetbrains.annotations.NotNull;
  23. import org.jetbrains.annotations.Nullable;
  24. import java.io.*;
  25. import java.util.*;
  26. public final class FlatFileDatabaseManager implements DatabaseManager {
  27. public static final String FLATFILE_SPLIT_CHARACTER_REGEX = ":";
  28. public static final String NULL_VALUE = "NULL";
  29. private final HashMap<PrimarySkillType, List<PlayerStat>> playerStatHash = new HashMap<>();
  30. private final List<PlayerStat> powerLevels = new ArrayList<>();
  31. private long lastUpdate = 0;
  32. private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
  33. private final File usersFile;
  34. private static final Object fileWritingLock = new Object();
  35. protected FlatFileDatabaseManager() {
  36. usersFile = new File(mcMMO.getUsersFilePath());
  37. checkStructure();
  38. updateLeaderboards();
  39. if (mcMMO.getUpgradeManager().shouldUpgrade(UpgradeType.ADD_UUIDS)) {
  40. new UUIDUpdateAsyncTask(mcMMO.p, getStoredUsers()).start();
  41. }
  42. }
  43. public void purgePowerlessUsers() {
  44. int purgedUsers = 0;
  45. mcMMO.p.getLogger().info("Purging powerless users...");
  46. BufferedReader in = null;
  47. FileWriter out = null;
  48. String usersFilePath = mcMMO.getUsersFilePath();
  49. // This code is O(n) instead of O(n²)
  50. synchronized (fileWritingLock) {
  51. try {
  52. in = new BufferedReader(new FileReader(usersFilePath));
  53. StringBuilder writer = new StringBuilder();
  54. String line;
  55. while ((line = in.readLine()) != null) {
  56. String[] character = line.split(":");
  57. Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(character);
  58. boolean powerless = true;
  59. for (int skill : skills.values()) {
  60. if (skill != 0) {
  61. powerless = false;
  62. break;
  63. }
  64. }
  65. // If they're still around, rewrite them to the file.
  66. if (!powerless) {
  67. writer.append(line).append("\r\n");
  68. }
  69. else {
  70. purgedUsers++;
  71. }
  72. }
  73. // Write the new file
  74. out = new FileWriter(usersFilePath);
  75. out.write(writer.toString());
  76. }
  77. catch (IOException e) {
  78. mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
  79. }
  80. finally {
  81. if (in != null) {
  82. try {
  83. in.close();
  84. }
  85. catch (IOException e) {
  86. // Ignore
  87. }
  88. }
  89. if (out != null) {
  90. try {
  91. out.close();
  92. }
  93. catch (IOException e) {
  94. // Ignore
  95. }
  96. }
  97. }
  98. }
  99. mcMMO.p.getLogger().info("Purged " + purgedUsers + " users from the database.");
  100. }
  101. public void purgeOldUsers() {
  102. int removedPlayers = 0;
  103. long currentTime = System.currentTimeMillis();
  104. mcMMO.p.getLogger().info("Purging old users...");
  105. BufferedReader in = null;
  106. FileWriter out = null;
  107. String usersFilePath = mcMMO.getUsersFilePath();
  108. // This code is O(n) instead of O(n²)
  109. synchronized (fileWritingLock) {
  110. try {
  111. in = new BufferedReader(new FileReader(usersFilePath));
  112. StringBuilder writer = new StringBuilder();
  113. String line;
  114. while ((line = in.readLine()) != null) {
  115. String[] character = line.split(":");
  116. String name = character[FlatFileMappings.USERNAME];
  117. long lastPlayed = 0;
  118. boolean rewrite = false;
  119. try {
  120. lastPlayed = Long.parseLong(character[37]) * Misc.TIME_CONVERSION_FACTOR;
  121. }
  122. catch (NumberFormatException e) {
  123. e.printStackTrace();
  124. }
  125. if (lastPlayed == 0) {
  126. OfflinePlayer player = mcMMO.p.getServer().getOfflinePlayer(name);
  127. lastPlayed = player.getLastPlayed();
  128. rewrite = true;
  129. }
  130. if (currentTime - lastPlayed > PURGE_TIME) {
  131. removedPlayers++;
  132. }
  133. else {
  134. if (rewrite) {
  135. // Rewrite their data with a valid time
  136. character[37] = Long.toString(lastPlayed);
  137. String newLine = org.apache.commons.lang.StringUtils.join(character, ":");
  138. writer.append(newLine).append("\r\n");
  139. }
  140. else {
  141. writer.append(line).append("\r\n");
  142. }
  143. }
  144. }
  145. // Write the new file
  146. out = new FileWriter(usersFilePath);
  147. out.write(writer.toString());
  148. }
  149. catch (IOException e) {
  150. mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
  151. }
  152. finally {
  153. if (in != null) {
  154. try {
  155. in.close();
  156. }
  157. catch (IOException e) {
  158. // Ignore
  159. }
  160. }
  161. if (out != null) {
  162. try {
  163. out.close();
  164. }
  165. catch (IOException e) {
  166. // Ignore
  167. }
  168. }
  169. }
  170. }
  171. mcMMO.p.getLogger().info("Purged " + removedPlayers + " users from the database.");
  172. }
  173. public boolean removeUser(@NotNull String playerName, @Nullable UUID uuid) {
  174. //NOTE: UUID is unused for FlatFile for this interface implementation
  175. boolean worked = false;
  176. BufferedReader in = null;
  177. FileWriter out = null;
  178. String usersFilePath = mcMMO.getUsersFilePath();
  179. synchronized (fileWritingLock) {
  180. try {
  181. in = new BufferedReader(new FileReader(usersFilePath));
  182. StringBuilder writer = new StringBuilder();
  183. String line;
  184. while ((line = in.readLine()) != null) {
  185. // Write out the same file but when we get to the player we want to remove, we skip his line.
  186. if (!worked && line.split(":")[FlatFileMappings.USERNAME].equalsIgnoreCase(playerName)) {
  187. mcMMO.p.getLogger().info("User found, removing...");
  188. worked = true;
  189. continue; // Skip the player
  190. }
  191. writer.append(line).append("\r\n");
  192. }
  193. out = new FileWriter(usersFilePath); // Write out the new file
  194. out.write(writer.toString());
  195. }
  196. catch (Exception e) {
  197. mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
  198. }
  199. finally {
  200. if (in != null) {
  201. try {
  202. in.close();
  203. }
  204. catch (IOException e) {
  205. // Ignore
  206. }
  207. }
  208. if (out != null) {
  209. try {
  210. out.close();
  211. }
  212. catch (IOException e) {
  213. // Ignore
  214. }
  215. }
  216. }
  217. }
  218. Misc.profileCleanup(playerName);
  219. return worked;
  220. }
  221. @Override
  222. public void cleanupUser(UUID uuid) {
  223. //Not used in FlatFile
  224. }
  225. public boolean saveUser(@NotNull MMOPlayer mmoPlayer) {
  226. PlayerData mmoPlayerData = mmoPlayer.getPlayerData();
  227. String playerName = dataSnapshot.getPlayerName();
  228. UUID uuid = dataSnapshot.getPlayerUUID();
  229. BufferedReader in = null;
  230. FileWriter out = null;
  231. String usersFilePath = mcMMO.getUsersFilePath();
  232. boolean corruptDataFound = false;
  233. synchronized (fileWritingLock) {
  234. try {
  235. // Open the file
  236. in = new BufferedReader(new FileReader(usersFilePath));
  237. StringBuilder writer = new StringBuilder();
  238. String line;
  239. boolean wroteUser = false;
  240. // While not at the end of the file
  241. while ((line = in.readLine()) != null) {
  242. //Check for incomplete or corrupted data
  243. if(!line.contains(":")) {
  244. if(!corruptDataFound) {
  245. mcMMO.p.getLogger().severe("mcMMO found some unexpected or corrupted data in mcmmo.users and is removing it, it is possible some data has been lost.");
  246. corruptDataFound = true;
  247. }
  248. continue;
  249. }
  250. String[] splitData = line.split(":");
  251. //This would be rare, but check the splitData for having enough entries to contain a username
  252. if(splitData.length < USERNAME) { //UUID have been in mcMMO DB for a very long time so any user without
  253. //Something is wrong if we don't have enough split data to have an entry for a username
  254. if(!corruptDataFound) {
  255. mcMMO.p.getLogger().severe("mcMMO found some unexpected or corrupted data in mcmmo.users and is removing it, it is possible some data has been lost.");
  256. corruptDataFound = true;
  257. }
  258. continue;
  259. }
  260. if (!(uuid != null
  261. && splitData[FlatFileMappings.UUID_INDEX].equalsIgnoreCase(uuid.toString()))
  262. && !splitData[FlatFileMappings.USERNAME].equalsIgnoreCase(playerName)) {
  263. writer.append(line).append("\r\n"); //Not the user so write it to file and move on
  264. } else {
  265. //User found
  266. writeUserToLine(profile, playerName, uuid, writer);
  267. wroteUser = true;
  268. }
  269. }
  270. /*
  271. * If we couldn't find the user in the DB we need to add him
  272. */
  273. if(!wroteUser) {
  274. writeUserToLine(profile, playerName, uuid, writer);
  275. }
  276. // Write the new file
  277. out = new FileWriter(usersFilePath);
  278. out.write(writer.toString());
  279. return true;
  280. }
  281. catch (Exception e) {
  282. e.printStackTrace();
  283. return false;
  284. }
  285. finally {
  286. if (in != null) {
  287. try {
  288. in.close();
  289. }
  290. catch (IOException e) {
  291. // Ignore
  292. }
  293. }
  294. if (out != null) {
  295. try {
  296. out.close();
  297. }
  298. catch (IOException e) {
  299. // Ignore
  300. }
  301. }
  302. }
  303. }
  304. }
  305. private void writeUserToLine(@NotNull MMODataSnapshot mmoDataSnapshot, @NotNull String playerName, @Nullable UUID uuid, @NotNull StringBuilder writer) {
  306. ImmutableMap<PrimarySkillType, Integer> primarySkillLevelMap = mmoDataSnapshot.getSkillLevelValues();
  307. ImmutableMap<PrimarySkillType, Float> primarySkillExperienceValueMap = mmoDataSnapshot.getSkillExperienceValues();
  308. writer.append(playerName).append(":");
  309. writer.append(primarySkillLevelMap.get(PrimarySkillType.MINING)).append(":");
  310. writer.append(":");
  311. writer.append(":");
  312. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.MINING)).append(":");
  313. writer.append(primarySkillLevelMap.get(PrimarySkillType.WOODCUTTING)).append(":");
  314. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.WOODCUTTING)).append(":");
  315. writer.append(primarySkillLevelMap.get(PrimarySkillType.REPAIR)).append(":");
  316. writer.append(primarySkillLevelMap.get(PrimarySkillType.UNARMED)).append(":");
  317. writer.append(primarySkillLevelMap.get(PrimarySkillType.HERBALISM)).append(":");
  318. writer.append(primarySkillLevelMap.get(PrimarySkillType.EXCAVATION)).append(":");
  319. writer.append(primarySkillLevelMap.get(PrimarySkillType.ARCHERY)).append(":");
  320. writer.append(primarySkillLevelMap.get(PrimarySkillType.SWORDS)).append(":");
  321. writer.append(primarySkillLevelMap.get(PrimarySkillType.AXES)).append(":");
  322. writer.append(primarySkillLevelMap.get(PrimarySkillType.ACROBATICS)).append(":");
  323. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.REPAIR)).append(":");
  324. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.UNARMED)).append(":");
  325. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.HERBALISM)).append(":");
  326. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.EXCAVATION)).append(":");
  327. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.ARCHERY)).append(":");
  328. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.SWORDS)).append(":");
  329. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.AXES)).append(":");
  330. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.ACROBATICS)).append(":");
  331. writer.append(":");
  332. writer.append(primarySkillLevelMap.get(PrimarySkillType.TAMING)).append(":");
  333. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.TAMING)).append(":");
  334. writer.append((int) mmoDataSnapshot.getAbilityDATS(SuperAbilityType.BERSERK)).append(":");
  335. writer.append((int) mmoDataSnapshot.getAbilityDATS(SuperAbilityType.GIGA_DRILL_BREAKER)).append(":");
  336. writer.append((int) mmoDataSnapshot.getAbilityDATS(SuperAbilityType.TREE_FELLER)).append(":");
  337. writer.append((int) mmoDataSnapshot.getAbilityDATS(SuperAbilityType.GREEN_TERRA)).append(":");
  338. writer.append((int) mmoDataSnapshot.getAbilityDATS(SuperAbilityType.SERRATED_STRIKES)).append(":");
  339. writer.append((int) mmoDataSnapshot.getAbilityDATS(SuperAbilityType.SKULL_SPLITTER)).append(":");
  340. writer.append((int) mmoDataSnapshot.getAbilityDATS(SuperAbilityType.SUPER_BREAKER)).append(":");
  341. writer.append(":");
  342. writer.append(primarySkillLevelMap.get(PrimarySkillType.FISHING)).append(":");
  343. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.FISHING)).append(":");
  344. writer.append((int) mmoDataSnapshot.getAbilityDATS(SuperAbilityType.BLAST_MINING)).append(":");
  345. writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
  346. writer.append(NULL_VALUE).append(":"); //Mob Health Bars are no longer based on player data
  347. writer.append(primarySkillLevelMap.get(PrimarySkillType.ALCHEMY)).append(":");
  348. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.ALCHEMY)).append(":");
  349. writer.append(uuid != null ? uuid.toString() : NULL_VALUE).append(":"); //Can be NULL
  350. writer.append(mmoDataSnapshot.getScoreboardTipsShown()).append(":");
  351. writer.append(mmoDataSnapshot.getUniqueData(UniqueDataType.CHIMAERA_WING_DATS)).append(":");
  352. /*
  353. public static int SKILLS_TRIDENTS = 44;
  354. public static int EXP_TRIDENTS = 45;
  355. public static int SKILLS_CROSSBOWS = 46;
  356. public static int EXP_CROSSBOWS = 47;
  357. public static int BARSTATE_ACROBATICS = 48;
  358. public static int BARSTATE_ALCHEMY = 49;
  359. public static int BARSTATE_ARCHERY = 50;
  360. public static int BARSTATE_AXES = 51;
  361. public static int BARSTATE_EXCAVATION = 52;
  362. public static int BARSTATE_FISHING = 53;
  363. public static int BARSTATE_HERBALISM = 54;
  364. public static int BARSTATE_MINING = 55;
  365. public static int BARSTATE_REPAIR = 56;
  366. public static int BARSTATE_SALVAGE = 57;
  367. public static int BARSTATE_SMELTING = 58;
  368. public static int BARSTATE_SWORDS = 59;
  369. public static int BARSTATE_TAMING = 60;
  370. public static int BARSTATE_UNARMED = 61;
  371. public static int BARSTATE_WOODCUTTING = 62;
  372. public static int BARSTATE_TRIDENTS = 63;
  373. public static int BARSTATE_CROSSBOWS = 64;
  374. */
  375. writer.append(primarySkillLevelMap.get(PrimarySkillType.TRIDENTS)).append(":");
  376. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.TRIDENTS)).append(":");
  377. writer.append(primarySkillLevelMap.get(PrimarySkillType.CROSSBOWS)).append(":");
  378. writer.append(primarySkillExperienceValueMap.get(PrimarySkillType.CROSSBOWS)).append(":");
  379. //XPBar States
  380. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.ACROBATICS).toString()).append(":");
  381. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.ALCHEMY).toString()).append(":");
  382. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.ARCHERY).toString()).append(":");
  383. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.AXES).toString()).append(":");
  384. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.EXCAVATION).toString()).append(":");
  385. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.FISHING).toString()).append(":");
  386. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.HERBALISM).toString()).append(":");
  387. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.MINING).toString()).append(":");
  388. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.REPAIR).toString()).append(":");
  389. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.SALVAGE).toString()).append(":");
  390. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.SMELTING).toString()).append(":");
  391. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.SWORDS).toString()).append(":");
  392. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.TAMING).toString()).append(":");
  393. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.UNARMED).toString()).append(":");
  394. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.WOODCUTTING).toString()).append(":");
  395. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.TRIDENTS).toString()).append(":");
  396. writer.append(mmoDataSnapshot.getBarStateMap().get(PrimarySkillType.CROSSBOWS).toString()).append(":");
  397. writer.append(0).append(":"); //archery super 1 cd
  398. writer.append(0).append(":"); //xbow super 1 cd
  399. writer.append(0).append(":"); //tridents super 1 cd
  400. writer.append(0).append(":"); //chatspy toggle
  401. writer.append(0).append(":"); //leaderboard ignored
  402. writer.append("\r\n");
  403. }
  404. public @NotNull List<PlayerStat> readLeaderboard(@NotNull PrimarySkillType skill, int pageNumber, int statsPerPage) {
  405. //Fix for a plugin that people are using that is throwing SQL errors
  406. if(skill != null && skill.isChildSkill()) {
  407. mcMMO.p.getLogger().severe("A plugin hooking into mcMMO is being naughty with our database commands, update all plugins that hook into mcMMO and contact their devs!");
  408. 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!");
  409. }
  410. updateLeaderboards();
  411. List<PlayerStat> statsList = skill == null ? powerLevels : playerStatHash.get(skill);
  412. int fromIndex = (Math.max(pageNumber, 1) - 1) * statsPerPage;
  413. return statsList.subList(Math.min(fromIndex, statsList.size()), Math.min(fromIndex + statsPerPage, statsList.size()));
  414. }
  415. public Map<PrimarySkillType, Integer> readRank(String playerName) {
  416. updateLeaderboards();
  417. Map<PrimarySkillType, Integer> skills = new HashMap<>();
  418. for (PrimarySkillType skill : PrimarySkillType.NON_CHILD_SKILLS) {
  419. skills.put(skill, getPlayerRank(playerName, playerStatHash.get(skill)));
  420. }
  421. skills.put(null, getPlayerRank(playerName, powerLevels));
  422. return skills;
  423. }
  424. public @NotNull PlayerProfile newUser(@NotNull Player player) {
  425. newUser(player.getName(), player.getUniqueId());
  426. return new PlayerProfile(player.getName(), player.getUniqueId(), true);
  427. }
  428. public void newUser(String playerName, UUID uuid) {
  429. BufferedWriter out = null;
  430. synchronized (fileWritingLock) {
  431. try {
  432. // Open the file to write the player
  433. out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true));
  434. String startingLevel = AdvancedConfig.getInstance().getStartingLevel() + ":";
  435. // Add the player to the end
  436. out.append(playerName).append(":");
  437. out.append(startingLevel); // Mining
  438. out.append(":");
  439. out.append(":");
  440. out.append("0:"); // Xp
  441. out.append(startingLevel); // Woodcutting
  442. out.append("0:"); // WoodCuttingXp
  443. out.append(startingLevel); // Repair
  444. out.append(startingLevel); // Unarmed
  445. out.append(startingLevel); // Herbalism
  446. out.append(startingLevel); // Excavation
  447. out.append(startingLevel); // Archery
  448. out.append(startingLevel); // Swords
  449. out.append(startingLevel); // Axes
  450. out.append(startingLevel); // Acrobatics
  451. out.append("0:"); // RepairXp
  452. out.append("0:"); // UnarmedXp
  453. out.append("0:"); // HerbalismXp
  454. out.append("0:"); // ExcavationXp
  455. out.append("0:"); // ArcheryXp
  456. out.append("0:"); // SwordsXp
  457. out.append("0:"); // AxesXp
  458. out.append("0:"); // AcrobaticsXp
  459. out.append(":");
  460. out.append(startingLevel); // Taming
  461. out.append("0:"); // TamingXp
  462. out.append("0:"); // DATS
  463. out.append("0:"); // DATS
  464. out.append("0:"); // DATS
  465. out.append("0:"); // DATS
  466. out.append("0:"); // DATS
  467. out.append("0:"); // DATS
  468. out.append("0:"); // DATS
  469. out.append(":");
  470. out.append(startingLevel); // Fishing
  471. out.append("0:"); // FishingXp
  472. out.append("0:"); // Blast Mining
  473. out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
  474. out.append(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD
  475. out.append(startingLevel); // Alchemy
  476. out.append("0:"); // AlchemyXp
  477. out.append(uuid != null ? uuid.toString() : NULL_VALUE).append(":"); // UUID
  478. out.append("0:"); // Scoreboard tips shown
  479. out.append("0:"); // Chimaera Wing Dats
  480. out.append("0:"); // Tridents Skill Level
  481. out.append("0:"); // Tridents XP
  482. out.append("0:"); // Crossbow Skill Level
  483. out.append("0:"); // Crossbow XP Level
  484. //Barstates for the 15 currently existing skills by ordinal value
  485. out.append("NORMAL:"); // Acrobatics
  486. out.append("NORMAL:"); // Alchemy
  487. out.append("NORMAL:"); // Archery
  488. out.append("NORMAL:"); // Axes
  489. out.append("NORMAL:"); // Excavation
  490. out.append("NORMAL:"); // Fishing
  491. out.append("NORMAL:"); // Herbalism
  492. out.append("NORMAL:"); // Mining
  493. out.append("NORMAL:"); // Repair
  494. out.append("DISABLED:"); // Salvage
  495. out.append("DISABLED:"); // Smelting
  496. out.append("NORMAL:"); // Swords
  497. out.append("NORMAL:"); // Taming
  498. out.append("NORMAL:"); // Unarmed
  499. out.append("NORMAL:"); // Woodcutting
  500. out.append("NORMAL:"); // Tridents
  501. out.append("NORMAL:"); // Crossbows
  502. //2.2.000+
  503. out.append("0:"); // arch super 1
  504. out.append("0:"); //xbow super 1
  505. out.append("0:"); //tridents super 1
  506. out.append("0:"); //chatspy toggle
  507. out.append("0:"); //leaderboard ignored toggle
  508. // Add more in the same format as the line above
  509. out.newLine();
  510. }
  511. catch (Exception e) {
  512. e.printStackTrace();
  513. }
  514. finally {
  515. if (out != null) {
  516. try {
  517. out.close();
  518. }
  519. catch (IOException e) {
  520. // Ignore
  521. }
  522. }
  523. }
  524. }
  525. }
  526. public @NotNull PlayerProfile loadPlayerProfile(@NotNull String playerName) {
  527. return loadPlayerByName(playerName);
  528. }
  529. public @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid, @Nullable String playerName) {
  530. return loadPlayerByUUID(uuid, playerName);
  531. }
  532. private @NotNull PlayerProfile loadPlayerByUUID(@NotNull UUID uuid, @Nullable String playerName) {
  533. BufferedReader in = null;
  534. String usersFilePath = mcMMO.getUsersFilePath();
  535. //Retrieve player
  536. synchronized (fileWritingLock) {
  537. try {
  538. // Open the user file
  539. in = new BufferedReader(new FileReader(usersFilePath));
  540. String line;
  541. while ((line = in.readLine()) != null) {
  542. // Find if the line contains the player we want.
  543. String[] rawSplitData = line.split(":");
  544. /* Don't read corrupt data */
  545. if(rawSplitData.length < (FlatFileMappings.UUID_INDEX + 1)) {
  546. continue;
  547. }
  548. /* Does this entry have a UUID? */
  549. if (rawSplitData[FlatFileMappings.UUID_INDEX].equalsIgnoreCase("NULL")
  550. || rawSplitData[FlatFileMappings.UUID_INDEX].isEmpty()
  551. || rawSplitData[FlatFileMappings.UUID_INDEX].equalsIgnoreCase("")) {
  552. continue; //No UUID entry found for this data in the DB, go to next entry
  553. }
  554. // Compare provided UUID to DB
  555. if (!rawSplitData[FlatFileMappings.UUID_INDEX].equalsIgnoreCase(uuid.toString())) {
  556. continue; //Doesn't match, go to the next entry
  557. }
  558. /*
  559. * UUID Matched!
  560. * Making it this far means the current data line is considered a match
  561. */
  562. /* Check for nickname changes and update since we are here anyways */
  563. if (!rawSplitData[FlatFileMappings.USERNAME].equalsIgnoreCase(playerName)) {
  564. //mcMMO.p.getLogger().info("Name updated for player: " + rawSplitData[FlatFileMappings.USERNAME] + " => " + playerName);
  565. rawSplitData[FlatFileMappings.USERNAME] = playerName;
  566. }
  567. return loadFromLine(rawSplitData);
  568. }
  569. } catch (Exception e) {
  570. e.printStackTrace();
  571. } finally {
  572. // I have no idea why it's necessary to inline tryClose() here, but it removes
  573. // a resource leak warning, and I'm trusting the compiler on this one.
  574. if (in != null) {
  575. try {
  576. in.close();
  577. } catch (IOException e) {
  578. // Ignore
  579. }
  580. }
  581. }
  582. }
  583. /*
  584. * No match was found in the file
  585. */
  586. return grabUnloadedProfile(uuid, playerName); //Create an empty new profile and return
  587. }
  588. private @NotNull PlayerProfile loadPlayerByName(@NotNull String playerName) {
  589. BufferedReader in = null;
  590. String usersFilePath = mcMMO.getUsersFilePath();
  591. synchronized (fileWritingLock) {
  592. try {
  593. // Open the user file
  594. in = new BufferedReader(new FileReader(usersFilePath));
  595. String line;
  596. while ((line = in.readLine()) != null) {
  597. // Find if the line contains the player we want.
  598. String[] rawSplitData = line.split(":");
  599. /* Don't read corrupt data */
  600. if(rawSplitData.length < (USERNAME + 1)) {
  601. continue;
  602. }
  603. //If we couldn't find anyone
  604. if(playerName.equalsIgnoreCase(rawSplitData[FlatFileMappings.USERNAME])) {
  605. return loadFromLine(rawSplitData);
  606. }
  607. }
  608. } catch (Exception e) {
  609. e.printStackTrace();
  610. } finally {
  611. // I have no idea why it's necessary to inline tryClose() here, but it removes
  612. // a resource leak warning, and I'm trusting the compiler on this one.
  613. if (in != null) {
  614. try {
  615. in.close();
  616. } catch (IOException e) {
  617. // Ignore
  618. }
  619. }
  620. }
  621. }
  622. //Return a new blank profile
  623. return new PlayerProfile(playerName, null);
  624. }
  625. private @NotNull PlayerProfile grabUnloadedProfile(@NotNull UUID uuid, @Nullable String playerName) {
  626. if(playerName == null) {
  627. playerName = ""; //No name for you boy!
  628. }
  629. return new PlayerProfile(playerName, uuid);
  630. }
  631. public void convertUsers(@NotNull DatabaseManager destination) {
  632. BufferedReader in = null;
  633. String usersFilePath = mcMMO.getUsersFilePath();
  634. int convertedUsers = 0;
  635. long startMillis = System.currentTimeMillis();
  636. synchronized (fileWritingLock) {
  637. try {
  638. // Open the user file
  639. in = new BufferedReader(new FileReader(usersFilePath));
  640. String line;
  641. while ((line = in.readLine()) != null) {
  642. String[] character = line.split(":");
  643. try {
  644. destination.saveUser(loadFromLine(character));
  645. }
  646. catch (Exception e) {
  647. e.printStackTrace();
  648. }
  649. convertedUsers++;
  650. Misc.printProgress(convertedUsers, progressInterval, startMillis);
  651. }
  652. }
  653. catch (Exception e) {
  654. e.printStackTrace();
  655. }
  656. finally {
  657. if (in != null) {
  658. try {
  659. in.close();
  660. }
  661. catch (IOException e) {
  662. // Ignore
  663. }
  664. }
  665. }
  666. }
  667. }
  668. public boolean saveUserUUID(String userName, UUID uuid) {
  669. boolean worked = false;
  670. int i = 0;
  671. BufferedReader in = null;
  672. FileWriter out = null;
  673. String usersFilePath = mcMMO.getUsersFilePath();
  674. synchronized (fileWritingLock) {
  675. try {
  676. in = new BufferedReader(new FileReader(usersFilePath));
  677. StringBuilder writer = new StringBuilder();
  678. String line;
  679. while ((line = in.readLine()) != null) {
  680. String[] character = line.split(":");
  681. if (!worked && character[FlatFileMappings.USERNAME].equalsIgnoreCase(userName)) {
  682. if (character.length < 42) {
  683. mcMMO.p.getLogger().severe("Could not update UUID for " + userName + "!");
  684. mcMMO.p.getLogger().severe("Database entry is invalid.");
  685. continue;
  686. }
  687. line = line.replace(character[FlatFileMappings.UUID_INDEX], uuid.toString());
  688. worked = true;
  689. }
  690. i++;
  691. writer.append(line).append("\r\n");
  692. }
  693. out = new FileWriter(usersFilePath); // Write out the new file
  694. out.write(writer.toString());
  695. }
  696. catch (Exception e) {
  697. mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
  698. }
  699. finally {
  700. mcMMO.p.getLogger().info(i + " entries written while saving UUID for " + userName);
  701. if (in != null) {
  702. try {
  703. in.close();
  704. }
  705. catch (IOException e) {
  706. // Ignore
  707. }
  708. }
  709. if (out != null) {
  710. try {
  711. out.close();
  712. }
  713. catch (IOException e) {
  714. // Ignore
  715. }
  716. }
  717. }
  718. }
  719. return worked;
  720. }
  721. public boolean saveUserUUIDs(Map<String, UUID> fetchedUUIDs) {
  722. BufferedReader in = null;
  723. FileWriter out = null;
  724. String usersFilePath = mcMMO.getUsersFilePath();
  725. int i = 0;
  726. synchronized (fileWritingLock) {
  727. try {
  728. in = new BufferedReader(new FileReader(usersFilePath));
  729. StringBuilder writer = new StringBuilder();
  730. String line;
  731. while (((line = in.readLine()) != null)) {
  732. String[] character = line.split(":");
  733. if (!fetchedUUIDs.isEmpty() && fetchedUUIDs.containsKey(character[FlatFileMappings.USERNAME])) {
  734. if (character.length < 42) {
  735. mcMMO.p.getLogger().severe("Could not update UUID for " + character[FlatFileMappings.USERNAME] + "!");
  736. mcMMO.p.getLogger().severe("Database entry is invalid.");
  737. continue;
  738. }
  739. character[FlatFileMappings.UUID_INDEX] = fetchedUUIDs.remove(character[FlatFileMappings.USERNAME]).toString();
  740. line = org.apache.commons.lang.StringUtils.join(character, ":") + ":";
  741. }
  742. i++;
  743. writer.append(line).append("\r\n");
  744. }
  745. out = new FileWriter(usersFilePath); // Write out the new file
  746. out.write(writer.toString());
  747. }
  748. catch (Exception e) {
  749. mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
  750. }
  751. finally {
  752. mcMMO.p.getLogger().info(i + " entries written while saving UUID batch");
  753. if (in != null) {
  754. try {
  755. in.close();
  756. }
  757. catch (IOException e) {
  758. // Ignore
  759. }
  760. }
  761. if (out != null) {
  762. try {
  763. out.close();
  764. }
  765. catch (IOException e) {
  766. // Ignore
  767. }
  768. }
  769. }
  770. }
  771. return true;
  772. }
  773. public List<String> getStoredUsers() {
  774. ArrayList<String> users = new ArrayList<>();
  775. BufferedReader in = null;
  776. String usersFilePath = mcMMO.getUsersFilePath();
  777. synchronized (fileWritingLock) {
  778. try {
  779. // Open the user file
  780. in = new BufferedReader(new FileReader(usersFilePath));
  781. String line;
  782. while ((line = in.readLine()) != null) {
  783. String[] character = line.split(":");
  784. users.add(character[FlatFileMappings.USERNAME]);
  785. }
  786. }
  787. catch (Exception e) {
  788. e.printStackTrace();
  789. }
  790. finally {
  791. if (in != null) {
  792. try {
  793. in.close();
  794. }
  795. catch (IOException e) {
  796. // Ignore
  797. }
  798. }
  799. }
  800. }
  801. return users;
  802. }
  803. /**
  804. * Update the leader boards.
  805. */
  806. private void updateLeaderboards() {
  807. // 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
  808. if (System.currentTimeMillis() < lastUpdate + UPDATE_WAIT_TIME) {
  809. return;
  810. }
  811. String usersFilePath = mcMMO.getUsersFilePath();
  812. lastUpdate = System.currentTimeMillis(); // Log when the last update was run
  813. powerLevels.clear(); // Clear old values from the power levels
  814. // Initialize lists
  815. List<PlayerStat> mining = new ArrayList<>();
  816. List<PlayerStat> woodcutting = new ArrayList<>();
  817. List<PlayerStat> herbalism = new ArrayList<>();
  818. List<PlayerStat> excavation = new ArrayList<>();
  819. List<PlayerStat> acrobatics = new ArrayList<>();
  820. List<PlayerStat> repair = new ArrayList<>();
  821. List<PlayerStat> swords = new ArrayList<>();
  822. List<PlayerStat> axes = new ArrayList<>();
  823. List<PlayerStat> archery = new ArrayList<>();
  824. List<PlayerStat> unarmed = new ArrayList<>();
  825. List<PlayerStat> taming = new ArrayList<>();
  826. List<PlayerStat> fishing = new ArrayList<>();
  827. List<PlayerStat> alchemy = new ArrayList<>();
  828. BufferedReader in = null;
  829. String playerName = null;
  830. // Read from the FlatFile database and fill our arrays with information
  831. synchronized (fileWritingLock) {
  832. try {
  833. in = new BufferedReader(new FileReader(usersFilePath));
  834. String line;
  835. while ((line = in.readLine()) != null) {
  836. String[] data = line.split(":");
  837. playerName = data[FlatFileMappings.USERNAME];
  838. int powerLevel = 0;
  839. Map<PrimarySkillType, Integer> skills = getSkillMapFromLine(data);
  840. powerLevel += putStat(acrobatics, playerName, skills.get(PrimarySkillType.ACROBATICS));
  841. powerLevel += putStat(alchemy, playerName, skills.get(PrimarySkillType.ALCHEMY));
  842. powerLevel += putStat(archery, playerName, skills.get(PrimarySkillType.ARCHERY));
  843. powerLevel += putStat(axes, playerName, skills.get(PrimarySkillType.AXES));
  844. powerLevel += putStat(excavation, playerName, skills.get(PrimarySkillType.EXCAVATION));
  845. powerLevel += putStat(fishing, playerName, skills.get(PrimarySkillType.FISHING));
  846. powerLevel += putStat(herbalism, playerName, skills.get(PrimarySkillType.HERBALISM));
  847. powerLevel += putStat(mining, playerName, skills.get(PrimarySkillType.MINING));
  848. powerLevel += putStat(repair, playerName, skills.get(PrimarySkillType.REPAIR));
  849. powerLevel += putStat(swords, playerName, skills.get(PrimarySkillType.SWORDS));
  850. powerLevel += putStat(taming, playerName, skills.get(PrimarySkillType.TAMING));
  851. powerLevel += putStat(unarmed, playerName, skills.get(PrimarySkillType.UNARMED));
  852. powerLevel += putStat(woodcutting, playerName, skills.get(PrimarySkillType.WOODCUTTING));
  853. powerLevel += putStat(woodcutting, playerName, skills.get(PrimarySkillType.CROSSBOWS));
  854. powerLevel += putStat(woodcutting, playerName, skills.get(PrimarySkillType.TRIDENTS));
  855. putStat(powerLevels, playerName, powerLevel);
  856. }
  857. }
  858. catch (Exception e) {
  859. mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " during user " + playerName + " (Are you sure you formatted it correctly?) " + e.toString());
  860. }
  861. finally {
  862. if (in != null) {
  863. try {
  864. in.close();
  865. }
  866. catch (IOException e) {
  867. // Ignore
  868. }
  869. }
  870. }
  871. }
  872. SkillComparator c = new SkillComparator();
  873. mining.sort(c);
  874. woodcutting.sort(c);
  875. repair.sort(c);
  876. unarmed.sort(c);
  877. herbalism.sort(c);
  878. excavation.sort(c);
  879. archery.sort(c);
  880. swords.sort(c);
  881. axes.sort(c);
  882. acrobatics.sort(c);
  883. taming.sort(c);
  884. fishing.sort(c);
  885. alchemy.sort(c);
  886. powerLevels.sort(c);
  887. playerStatHash.put(PrimarySkillType.MINING, mining);
  888. playerStatHash.put(PrimarySkillType.WOODCUTTING, woodcutting);
  889. playerStatHash.put(PrimarySkillType.REPAIR, repair);
  890. playerStatHash.put(PrimarySkillType.UNARMED, unarmed);
  891. playerStatHash.put(PrimarySkillType.HERBALISM, herbalism);
  892. playerStatHash.put(PrimarySkillType.EXCAVATION, excavation);
  893. playerStatHash.put(PrimarySkillType.ARCHERY, archery);
  894. playerStatHash.put(PrimarySkillType.SWORDS, swords);
  895. playerStatHash.put(PrimarySkillType.AXES, axes);
  896. playerStatHash.put(PrimarySkillType.ACROBATICS, acrobatics);
  897. playerStatHash.put(PrimarySkillType.TAMING, taming);
  898. playerStatHash.put(PrimarySkillType.FISHING, fishing);
  899. playerStatHash.put(PrimarySkillType.ALCHEMY, alchemy);
  900. playerStatHash.put(PrimarySkillType.TRIDENTS, alchemy);
  901. playerStatHash.put(PrimarySkillType.CROSSBOWS, alchemy);
  902. }
  903. /**
  904. * Checks that the file is present and valid
  905. */
  906. private void checkStructure() {
  907. boolean corruptDataFound = false;
  908. if (usersFile.exists()) {
  909. BufferedReader in = null;
  910. FileWriter out = null;
  911. String usersFilePath = mcMMO.getUsersFilePath();
  912. synchronized (fileWritingLock) {
  913. try {
  914. in = new BufferedReader(new FileReader(usersFilePath));
  915. StringBuilder writer = new StringBuilder();
  916. String line;
  917. HashSet<String> usernames = new HashSet<>();
  918. HashSet<String> players = new HashSet<>();
  919. while ((line = in.readLine()) != null) {
  920. // Remove empty lines from the file
  921. if (line.isEmpty()) {
  922. continue;
  923. }
  924. // Length checks depend on last rawSplitData being ':'
  925. if (line.charAt(line.length() - 1) != ':') {
  926. line = line.concat(":");
  927. }
  928. String[] rawSplitData = line.split(":");
  929. //Not enough data found to be considered a user reliably (NOTE: not foolproof)
  930. if(rawSplitData.length < (FlatFileMappings.UUID_INDEX + 1)) {
  931. if(!corruptDataFound) {
  932. mcMMO.p.getLogger().severe("Some corrupt data was found in mcmmo.users and has been repaired, it is possible that some player data has been lost in this process.");
  933. corruptDataFound = true;
  934. }
  935. if(rawSplitData.length >= 10 //The value here is kind of arbitrary, it shouldn't be too low to avoid false positives, but also we aren't really going to correctly identify when player data has been corrupted or not with 100% accuracy ever
  936. && rawSplitData[0] != null && !rawSplitData[0].isEmpty()) {
  937. if(rawSplitData[0].length() <= 16 && rawSplitData[0].length() >= 3) {
  938. mcMMO.p.getLogger().severe("Not enough data found to recover corrupted player data for user: "+rawSplitData[0]);
  939. }
  940. }
  941. //This user may have had a name so declare it
  942. continue;
  943. }
  944. // Prevent the same username from being present multiple times
  945. if (!usernames.add(rawSplitData[FlatFileMappings.USERNAME])) {
  946. //TODO: Check if the commented out code was even necessary
  947. rawSplitData[FlatFileMappings.USERNAME] = "_INVALID_OLD_USERNAME_'";
  948. if (rawSplitData.length < FlatFileMappings.UUID_INDEX + 1 || rawSplitData[FlatFileMappings.UUID_INDEX].equals("NULL")) {
  949. mcMMO.p.getLogger().severe("Fixing duplicate player names found in mcmmo.users");
  950. continue;
  951. }
  952. }
  953. // Prevent the same player from being present multiple times
  954. if (rawSplitData.length >= (FlatFileMappings.UUID_INDEX + 1) //TODO: Test this condition
  955. && (!rawSplitData[FlatFileMappings.UUID_INDEX].isEmpty()
  956. && !rawSplitData[FlatFileMappings.UUID_INDEX].equals("NULL") && !players.add(rawSplitData[FlatFileMappings.UUID_INDEX]))) {
  957. mcMMO.p.getLogger().severe("Removing duplicate player data from mcmmo.users");
  958. mcMMO.p.getLogger().info("Duplicate Data: "+line);
  959. continue;
  960. }
  961. //Correctly size the data (null entries for missing values)
  962. if(line.length() < FlatFileMappings.LENGTH_OF_SPLIT_DATA_ARRAY) { //TODO: Test this condition
  963. String[] correctSizeSplitData = Arrays.copyOf(rawSplitData, FlatFileMappings.LENGTH_OF_SPLIT_DATA_ARRAY);
  964. line = org.apache.commons.lang.StringUtils.join(correctSizeSplitData, ":") + ":";
  965. rawSplitData = line.split(":");
  966. PlayerProfile temporaryProfile = loadFromLine(rawSplitData);
  967. writeUserToLine(temporaryProfile, rawSplitData[FlatFileMappings.USERNAME], temporaryProfile.getUniqueId(), writer);
  968. } else {
  969. writer.append(line).append("\r\n");
  970. }
  971. }
  972. // Write the new file
  973. out = new FileWriter(usersFilePath);
  974. out.write(writer.toString());
  975. }
  976. catch (IOException e) {
  977. mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
  978. }
  979. finally {
  980. if (in != null) {
  981. try {
  982. in.close();
  983. }
  984. catch (IOException e) {
  985. e.printStackTrace();
  986. }
  987. }
  988. if (out != null) {
  989. try {
  990. out.close();
  991. }
  992. catch (IOException e) {
  993. e.printStackTrace();
  994. }
  995. }
  996. }
  997. }
  998. if(corruptDataFound)
  999. mcMMO.p.getLogger().info("Corrupt data was found and removed, everything should be working fine. It is possible some player data was lost.");
  1000. return;
  1001. }
  1002. usersFile.getParentFile().mkdir();
  1003. try {
  1004. mcMMO.p.debug("Creating mcmmo.users file...");
  1005. new File(mcMMO.getUsersFilePath()).createNewFile();
  1006. }
  1007. catch (IOException e) {
  1008. e.printStackTrace();
  1009. }
  1010. }
  1011. private Integer getPlayerRank(String playerName, List<PlayerStat> statsList) {
  1012. if (statsList == null) {
  1013. return null;
  1014. }
  1015. int currentPos = 1;
  1016. for (PlayerStat stat : statsList) {
  1017. if (stat.name.equalsIgnoreCase(playerName)) {
  1018. return currentPos;
  1019. }
  1020. currentPos++;
  1021. }
  1022. return null;
  1023. }
  1024. private int putStat(List<PlayerStat> statList, String playerName, int statValue) {
  1025. statList.add(new PlayerStat(playerName, statValue));
  1026. return statValue;
  1027. }
  1028. private static class SkillComparator implements Comparator<PlayerStat> {
  1029. @Override
  1030. public int compare(PlayerStat o1, PlayerStat o2) {
  1031. return (o2.statVal - o1.statVal);
  1032. }
  1033. }
  1034. private @Nullable PlayerData loadFromLine(@NotNull String[] dataStrSplit) {
  1035. MMODataBuilder playerDataBuilder = new MMODataBuilder();
  1036. String username = dataStrSplit[FlatFileMappings.USERNAME];
  1037. Map<PrimarySkillType, Integer> skillLevelMap = getSkillMapFromLine(dataStrSplit); // Skill levels
  1038. Map<PrimarySkillType, Float> skillExperienceValueMap = new HashMap<>(); // Skill & XP
  1039. Map<SuperAbilityType, Integer> skillAbilityDeactivationTimeStamp = new HashMap<>(); // Ability & Cooldown
  1040. Map<UniqueDataType, Integer> uniquePlayerDataMap = new EnumMap<UniqueDataType, Integer>(UniqueDataType.class);
  1041. Map<PrimarySkillType, SkillBossBarState> xpBarStateMap = new HashMap<>();
  1042. int scoreboardTipsShown;
  1043. //XP Values
  1044. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.TAMING, FlatFileMappings.EXP_TAMING, username);
  1045. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.MINING, FlatFileMappings.EXP_MINING, username);
  1046. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.REPAIR, FlatFileMappings.EXP_REPAIR, username);
  1047. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.WOODCUTTING, FlatFileMappings.EXP_WOODCUTTING, username);
  1048. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.UNARMED, FlatFileMappings.EXP_UNARMED, username);
  1049. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.HERBALISM, FlatFileMappings.EXP_HERBALISM, username);
  1050. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.EXCAVATION, FlatFileMappings.EXP_EXCAVATION, username);
  1051. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.ARCHERY, FlatFileMappings.EXP_ARCHERY, username);
  1052. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.SWORDS, FlatFileMappings.EXP_SWORDS, username);
  1053. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.AXES, FlatFileMappings.EXP_AXES, username);
  1054. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.ACROBATICS, FlatFileMappings.EXP_ACROBATICS, username);
  1055. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.FISHING, FlatFileMappings.EXP_FISHING, username);
  1056. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.ALCHEMY, FlatFileMappings.EXP_ALCHEMY, username);
  1057. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.TRIDENTS, FlatFileMappings.EXP_TRIDENTS, username);
  1058. tryLoadSkillFloatValuesFromRawData(skillExperienceValueMap, dataStrSplit, PrimarySkillType.CROSSBOWS, FlatFileMappings.EXP_CROSSBOWS, username);
  1059. //Set Skill XP
  1060. // Taming - Unused
  1061. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.SUPER_BREAKER, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_SUPER_BREAKER]));
  1062. // Repair - Unused
  1063. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.TREE_FELLER, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_TREE_FELLER]));
  1064. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.BERSERK, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_BERSERK]));
  1065. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.GREEN_TERRA, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_GREEN_TERRA]));
  1066. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.GIGA_DRILL_BREAKER, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_GIGA_DRILL_BREAKER]));
  1067. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.SERRATED_STRIKES, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_SERRATED_STRIKES]));
  1068. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.SKULL_SPLITTER, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_SKULL_SPLITTER]));
  1069. // Acrobatics - Unused
  1070. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.BLAST_MINING, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_BLAST_MINING]));
  1071. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.ARCHERY_SUPER, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_ARCHERY_SUPER_1]));
  1072. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.SUPER_SHOTGUN, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_CROSSBOWS_SUPER_1]));
  1073. skillAbilityDeactivationTimeStamp.put(SuperAbilityType.TRIDENT_SUPER, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_TRIDENTS_SUPER_1]));
  1074. /*
  1075. * Mob Health Bars are no longer saved per player and are ignored from the data
  1076. */
  1077. //Sometimes players are retrieved by name
  1078. UUID playerUUID;
  1079. try {
  1080. playerUUID = UUID.fromString(dataStrSplit[FlatFileMappings.UUID_INDEX]);
  1081. }
  1082. catch (Exception e) {
  1083. playerUUID = null;
  1084. }
  1085. try {
  1086. scoreboardTipsShown = Integer.parseInt(dataStrSplit[FlatFileMappings.SCOREBOARD_TIPS]);
  1087. }
  1088. catch (Exception e) {
  1089. scoreboardTipsShown = 0;
  1090. }
  1091. try {
  1092. uniquePlayerDataMap.put(UniqueDataType.CHIMAERA_WING_DATS, Integer.valueOf(dataStrSplit[FlatFileMappings.COOLDOWN_CHIMAERA_WING]));
  1093. }
  1094. catch (Exception e) {
  1095. uniquePlayerDataMap.put(UniqueDataType.CHIMAERA_WING_DATS, 0);
  1096. }
  1097. try {
  1098. xpBarStateMap.put(PrimarySkillType.ACROBATICS, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_ACROBATICS]));
  1099. xpBarStateMap.put(PrimarySkillType.ALCHEMY, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_ALCHEMY]));
  1100. xpBarStateMap.put(PrimarySkillType.ARCHERY, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_ARCHERY]));
  1101. xpBarStateMap.put(PrimarySkillType.AXES, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_AXES]));
  1102. xpBarStateMap.put(PrimarySkillType.EXCAVATION, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_EXCAVATION]));
  1103. xpBarStateMap.put(PrimarySkillType.FISHING, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_FISHING]));
  1104. xpBarStateMap.put(PrimarySkillType.HERBALISM, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_HERBALISM]));
  1105. xpBarStateMap.put(PrimarySkillType.MINING, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_MINING]));
  1106. xpBarStateMap.put(PrimarySkillType.REPAIR, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_REPAIR]));
  1107. xpBarStateMap.put(PrimarySkillType.SALVAGE, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_SALVAGE]));
  1108. xpBarStateMap.put(PrimarySkillType.SMELTING, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_SMELTING]));
  1109. xpBarStateMap.put(PrimarySkillType.SWORDS, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_SWORDS]));
  1110. xpBarStateMap.put(PrimarySkillType.TAMING, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_TAMING]));
  1111. xpBarStateMap.put(PrimarySkillType.UNARMED, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_UNARMED]));
  1112. xpBarStateMap.put(PrimarySkillType.WOODCUTTING, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_WOODCUTTING]));
  1113. xpBarStateMap.put(PrimarySkillType.TRIDENTS, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_TRIDENTS]));
  1114. xpBarStateMap.put(PrimarySkillType.CROSSBOWS, SkillUtils.asBarState(dataStrSplit[FlatFileMappings.BARSTATE_CROSSBOWS]));
  1115. } catch (Exception e) {
  1116. xpBarStateMap = MMOExperienceBarManager.generateDefaultBarStateMap();
  1117. }
  1118. PlayerData mmoPlayerData;
  1119. try {
  1120. //Set Player Data
  1121. playerDataBuilder.setSkillLevelValues(skillLevelMap)
  1122. .setSkillExperienceValues(skillExperienceValueMap)
  1123. .setAbilityDeactivationTimestamps(skillAbilityDeactivationTimeStamp)
  1124. // .setMobHealthBarType(mobHealthbarType)
  1125. .setPlayerUUID(playerUUID)
  1126. .setScoreboardTipsShown(scoreboardTipsShown)
  1127. .setUniquePlayerData(uniquePlayerDataMap)
  1128. .setBarStateMap(xpBarStateMap);
  1129. //Build Data
  1130. return playerDataBuilder.build();
  1131. } catch (Exception e) {
  1132. mcMMO.p.getLogger().severe("Critical failure when trying to construct persistent player data!");
  1133. e.printStackTrace();
  1134. return null;
  1135. }
  1136. }
  1137. private @NotNull Map<PrimarySkillType, Integer> getSkillMapFromLine(@NotNull String[] splitDataArray) {
  1138. Map<PrimarySkillType, Integer> skills = new EnumMap<>(PrimarySkillType.class); // Skill & Level
  1139. String username = splitDataArray[FlatFileMappings.USERNAME];
  1140. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.TAMING, FlatFileMappings.SKILLS_TAMING, username);
  1141. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.MINING, FlatFileMappings.SKILLS_MINING, username);
  1142. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.REPAIR, FlatFileMappings.SKILLS_REPAIR, username);
  1143. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.WOODCUTTING, FlatFileMappings.SKILLS_WOODCUTTING, username);
  1144. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.UNARMED, FlatFileMappings.SKILLS_UNARMED, username);
  1145. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.HERBALISM, FlatFileMappings.SKILLS_HERBALISM, username);
  1146. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.EXCAVATION, FlatFileMappings.SKILLS_EXCAVATION, username);
  1147. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.ARCHERY, FlatFileMappings.SKILLS_ARCHERY, username);
  1148. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.SWORDS, FlatFileMappings.SKILLS_SWORDS, username);
  1149. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.AXES, FlatFileMappings.SKILLS_AXES, username);
  1150. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.ACROBATICS, FlatFileMappings.SKILLS_ACROBATICS, username);
  1151. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.FISHING, FlatFileMappings.SKILLS_FISHING, username);
  1152. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.ALCHEMY, FlatFileMappings.SKILLS_ALCHEMY, username);
  1153. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.TRIDENTS, FlatFileMappings.SKILLS_TRIDENTS, username);
  1154. tryLoadSkillIntValuesFromRawData(skills, splitDataArray, PrimarySkillType.CROSSBOWS, FlatFileMappings.SKILLS_CROSSBOWS, username);
  1155. return skills;
  1156. }
  1157. private void tryLoadSkillCooldownFromRawData(@NotNull Map<SuperAbilityType, Integer> cooldownMap, @NotNull String[] character, @NotNull SuperAbilityType superAbilityType, int cooldownSuperBreaker, @NotNull String userName) {
  1158. try {
  1159. cooldownMap.put(superAbilityType, Integer.valueOf(character[cooldownSuperBreaker]));
  1160. } catch (NumberFormatException e) {
  1161. mcMMO.p.getLogger().severe("Data corruption when trying to load the value for skill "+superAbilityType.toString()+" for player named " + userName+ " setting value to zero");
  1162. e.printStackTrace();
  1163. }
  1164. }
  1165. private void tryLoadSkillFloatValuesFromRawData(@NotNull Map<PrimarySkillType, Float> skillMap, @NotNull String[] character, @NotNull PrimarySkillType primarySkillType, int index, @NotNull String userName) {
  1166. try {
  1167. float valueFromString = Integer.parseInt(character[index]);
  1168. skillMap.put(primarySkillType, valueFromString);
  1169. } catch (NumberFormatException e) {
  1170. skillMap.put(primarySkillType, 0F);
  1171. mcMMO.p.getLogger().severe("Data corruption when trying to load the value for skill "+primarySkillType.toString()+" for player named " + userName+ " setting value to zero");
  1172. e.printStackTrace();
  1173. }
  1174. }
  1175. private void tryLoadSkillIntValuesFromRawData(@NotNull Map<PrimarySkillType, Integer> skillMap, @NotNull String[] character, @NotNull PrimarySkillType primarySkillType, int index, @NotNull String userName) {
  1176. try {
  1177. int valueFromString = Integer.parseInt(character[index]);
  1178. skillMap.put(primarySkillType, valueFromString);
  1179. } catch (NumberFormatException e) {
  1180. skillMap.put(primarySkillType, 0);
  1181. mcMMO.p.getLogger().severe("Data corruption when trying to load the value for skill "+primarySkillType.toString()+" for player named " + userName+ " setting value to zero");
  1182. e.printStackTrace();
  1183. }
  1184. }
  1185. public @NotNull DatabaseType getDatabaseType() {
  1186. return DatabaseType.FLATFILE;
  1187. }
  1188. private int getSkillIndex(@NotNull PrimarySkillType primarySkillType) {
  1189. switch (primarySkillType) {
  1190. case ACROBATICS:
  1191. return FlatFileMappings.SKILLS_ACROBATICS;
  1192. case ALCHEMY:
  1193. return FlatFileMappings.SKILLS_ALCHEMY;
  1194. case ARCHERY:
  1195. return FlatFileMappings.SKILLS_ARCHERY;
  1196. case AXES:
  1197. return FlatFileMappings.SKILLS_AXES;
  1198. case EXCAVATION:
  1199. return FlatFileMappings.SKILLS_EXCAVATION;
  1200. case FISHING:
  1201. return FlatFileMappings.SKILLS_FISHING;
  1202. case HERBALISM:
  1203. return FlatFileMappings.SKILLS_HERBALISM;
  1204. case MINING:
  1205. return FlatFileMappings.SKILLS_MINING;
  1206. case REPAIR:
  1207. return FlatFileMappings.SKILLS_REPAIR;
  1208. case SWORDS:
  1209. return FlatFileMappings.SKILLS_SWORDS;
  1210. case TAMING:
  1211. return FlatFileMappings.SKILLS_TAMING;
  1212. case UNARMED:
  1213. return FlatFileMappings.SKILLS_UNARMED;
  1214. case WOODCUTTING:
  1215. return FlatFileMappings.SKILLS_WOODCUTTING;
  1216. case TRIDENTS:
  1217. return FlatFileMappings.SKILLS_TRIDENTS;
  1218. case CROSSBOWS:
  1219. return FlatFileMappings.SKILLS_CROSSBOWS;
  1220. default:
  1221. throw new RuntimeException("Primary Skills only");
  1222. }
  1223. }
  1224. @Override
  1225. public void onDisable() { }
  1226. }