SQLDatabaseManager.java 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274
  1. package com.gmail.nossr50.database;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. import java.sql.Statement;
  8. import java.util.ArrayList;
  9. import java.util.Collection;
  10. import java.util.HashMap;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.Properties;
  14. import com.gmail.nossr50.mcMMO;
  15. import com.gmail.nossr50.config.Config;
  16. import com.gmail.nossr50.datatypes.MobHealthbarType;
  17. import com.gmail.nossr50.datatypes.database.DatabaseType;
  18. import com.gmail.nossr50.datatypes.database.DatabaseUpdateType;
  19. import com.gmail.nossr50.datatypes.database.PlayerStat;
  20. import com.gmail.nossr50.datatypes.player.PlayerProfile;
  21. import com.gmail.nossr50.datatypes.skills.AbilityType;
  22. import com.gmail.nossr50.datatypes.skills.SkillType;
  23. import com.gmail.nossr50.runnables.database.SQLDatabaseKeepaliveTask;
  24. import com.gmail.nossr50.datatypes.spout.huds.HudType;
  25. import com.gmail.nossr50.runnables.database.SQLReconnectTask;
  26. import com.gmail.nossr50.util.Misc;
  27. public final class SQLDatabaseManager implements DatabaseManager {
  28. private String connectionString;
  29. private String tablePrefix = Config.getInstance().getMySQLTablePrefix();
  30. private Connection connection = null;
  31. // Scale waiting time by this much per failed attempt
  32. private final double SCALING_FACTOR = 40.0;
  33. // Minimum wait in nanoseconds (default 500ms)
  34. private final long MIN_WAIT = 500L * 1000000L;
  35. // Maximum time to wait between reconnects (default 5 minutes)
  36. private final long MAX_WAIT = 5L * 60L * 1000L * 1000000L;
  37. // How long to wait when checking if connection is valid (default 3 seconds)
  38. private final int VALID_TIMEOUT = 3;
  39. // When next to try connecting to Database in nanoseconds
  40. private long nextReconnectTimestamp = 0L;
  41. // How many connection attempts have failed
  42. private int reconnectAttempt = 0;
  43. protected SQLDatabaseManager() {
  44. checkStructure();
  45. new SQLDatabaseKeepaliveTask(this).runTaskTimerAsynchronously(mcMMO.p, 10, 60L * 60 * Misc.TICK_CONVERSION_FACTOR);
  46. }
  47. public void purgePowerlessUsers() {
  48. if (!checkConnected()) {
  49. return;
  50. }
  51. mcMMO.p.getLogger().info("Purging powerless users...");
  52. Collection<ArrayList<String>> usernames = read("SELECT u.user FROM " + tablePrefix + "skills AS s, " + tablePrefix + "users AS u WHERE s.user_id = u.id AND (s.taming+s.mining+s.woodcutting+s.repair+s.unarmed+s.herbalism+s.excavation+s.archery+s.swords+s.axes+s.acrobatics+s.fishing) = 0").values();
  53. write("DELETE FROM u, e, h, s, c USING " + tablePrefix + "users u " +
  54. "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
  55. "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
  56. "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
  57. "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
  58. "WHERE (s.taming+s.mining+s.woodcutting+s.repair+s.unarmed+s.herbalism+s.excavation+s.archery+s.swords+s.axes+s.acrobatics+s.fishing) = 0");
  59. processPurge(usernames);
  60. mcMMO.p.getLogger().info("Purged " + usernames.size() + " users from the database.");
  61. }
  62. public void purgeOldUsers() {
  63. if (!checkConnected()) {
  64. return;
  65. }
  66. long currentTime = System.currentTimeMillis();
  67. mcMMO.p.getLogger().info("Purging old users...");
  68. Collection<ArrayList<String>> usernames = read("SELECT user FROM " + tablePrefix + "users WHERE ((" + currentTime + " - lastlogin * " + Misc.TIME_CONVERSION_FACTOR + ") > " + PURGE_TIME + ")").values();
  69. write("DELETE FROM u, e, h, s, c USING " + tablePrefix + "users u " +
  70. "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
  71. "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
  72. "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
  73. "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
  74. "WHERE ((" + currentTime + " - lastlogin * " + Misc.TIME_CONVERSION_FACTOR + ") > " + PURGE_TIME + ")");
  75. processPurge(usernames);
  76. mcMMO.p.getLogger().info("Purged " + usernames.size() + " users from the database.");
  77. }
  78. public boolean removeUser(String playerName) {
  79. if (!checkConnected()) {
  80. return false;
  81. }
  82. boolean success = update("DELETE FROM u, e, h, s, c " +
  83. "USING " + tablePrefix + "users u " +
  84. "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " +
  85. "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " +
  86. "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " +
  87. "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " +
  88. "WHERE u.user = '" + playerName + "'") != 0;
  89. Misc.profileCleanup(playerName);
  90. return success;
  91. }
  92. public boolean saveUser(PlayerProfile profile) {
  93. if (!checkConnected()) {
  94. return false;
  95. }
  96. int userId = readId(profile.getPlayerName());
  97. if (userId == -1) {
  98. newUser(profile.getPlayerName());
  99. userId = readId(profile.getPlayerName());
  100. if (userId == -1) {
  101. return false;
  102. }
  103. }
  104. boolean success = true;
  105. MobHealthbarType mobHealthbarType = profile.getMobHealthbarType();
  106. HudType hudType = profile.getHudType();
  107. success &= saveLogin(userId, ((int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)));
  108. saveHuds(userId, (hudType == null ? "STANDARD" : hudType.toString()), (mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()));
  109. success &= saveLongs(
  110. "UPDATE " + tablePrefix + "cooldowns SET "
  111. + " mining = ?, woodcutting = ?, unarmed = ?"
  112. + ", herbalism = ?, excavation = ?, swords = ?"
  113. + ", axes = ?, blast_mining = ? WHERE user_id = ?",
  114. userId,
  115. profile.getAbilityDATS(AbilityType.SUPER_BREAKER),
  116. profile.getAbilityDATS(AbilityType.TREE_FELLER),
  117. profile.getAbilityDATS(AbilityType.BERSERK),
  118. profile.getAbilityDATS(AbilityType.GREEN_TERRA),
  119. profile.getAbilityDATS(AbilityType.GIGA_DRILL_BREAKER),
  120. profile.getAbilityDATS(AbilityType.SERRATED_STRIKES),
  121. profile.getAbilityDATS(AbilityType.SKULL_SPLITTER),
  122. profile.getAbilityDATS(AbilityType.BLAST_MINING));
  123. success &= saveIntegers(
  124. "UPDATE " + tablePrefix + "skills SET "
  125. + " taming = ?, mining = ?, repair = ?, woodcutting = ?"
  126. + ", unarmed = ?, herbalism = ?, excavation = ?"
  127. + ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
  128. + ", fishing = ?, alchemy = ? WHERE user_id = ?",
  129. profile.getSkillLevel(SkillType.TAMING),
  130. profile.getSkillLevel(SkillType.MINING),
  131. profile.getSkillLevel(SkillType.REPAIR),
  132. profile.getSkillLevel(SkillType.WOODCUTTING),
  133. profile.getSkillLevel(SkillType.UNARMED),
  134. profile.getSkillLevel(SkillType.HERBALISM),
  135. profile.getSkillLevel(SkillType.EXCAVATION),
  136. profile.getSkillLevel(SkillType.ARCHERY),
  137. profile.getSkillLevel(SkillType.SWORDS),
  138. profile.getSkillLevel(SkillType.AXES),
  139. profile.getSkillLevel(SkillType.ACROBATICS),
  140. profile.getSkillLevel(SkillType.FISHING),
  141. profile.getSkillLevel(SkillType.ALCHEMY),
  142. userId);
  143. success &= saveIntegers(
  144. "UPDATE " + tablePrefix + "experience SET "
  145. + " taming = ?, mining = ?, repair = ?, woodcutting = ?"
  146. + ", unarmed = ?, herbalism = ?, excavation = ?"
  147. + ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
  148. + ", fishing = ?, alchemy = ? WHERE user_id = ?",
  149. profile.getSkillXpLevel(SkillType.TAMING),
  150. profile.getSkillXpLevel(SkillType.MINING),
  151. profile.getSkillXpLevel(SkillType.REPAIR),
  152. profile.getSkillXpLevel(SkillType.WOODCUTTING),
  153. profile.getSkillXpLevel(SkillType.UNARMED),
  154. profile.getSkillXpLevel(SkillType.HERBALISM),
  155. profile.getSkillXpLevel(SkillType.EXCAVATION),
  156. profile.getSkillXpLevel(SkillType.ARCHERY),
  157. profile.getSkillXpLevel(SkillType.SWORDS),
  158. profile.getSkillXpLevel(SkillType.AXES),
  159. profile.getSkillXpLevel(SkillType.ACROBATICS),
  160. profile.getSkillXpLevel(SkillType.FISHING),
  161. profile.getSkillXpLevel(SkillType.ALCHEMY),
  162. userId);
  163. return success;
  164. }
  165. public List<PlayerStat> readLeaderboard(SkillType skill, int pageNumber, int statsPerPage) {
  166. List<PlayerStat> stats = new ArrayList<PlayerStat>();
  167. if (checkConnected()) {
  168. String query = skill == null ? "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy" : skill.name().toLowerCase();
  169. ResultSet resultSet;
  170. PreparedStatement statement = null;
  171. try {
  172. statement = connection.prepareStatement("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT ?, ?");
  173. statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage);
  174. statement.setInt(2, statsPerPage);
  175. resultSet = statement.executeQuery();
  176. while (resultSet.next()) {
  177. ArrayList<String> column = new ArrayList<String>();
  178. for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
  179. column.add(resultSet.getString(i));
  180. }
  181. stats.add(new PlayerStat(column.get(1), Integer.valueOf(column.get(0))));
  182. }
  183. }
  184. catch (SQLException ex) {
  185. printErrors(ex);
  186. }
  187. finally {
  188. if (statement != null) {
  189. try {
  190. statement.close();
  191. }
  192. catch (SQLException e) {
  193. // Ignore
  194. }
  195. }
  196. }
  197. }
  198. return stats;
  199. }
  200. public Map<SkillType, Integer> readRank(String playerName) {
  201. Map<SkillType, Integer> skills = new HashMap<SkillType, Integer>();
  202. if (checkConnected()) {
  203. ResultSet resultSet;
  204. try {
  205. for (SkillType skillType : SkillType.NON_CHILD_SKILLS) {
  206. String skillName = skillType.name().toLowerCase();
  207. String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " +
  208. "AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
  209. "WHERE user = ?)";
  210. PreparedStatement statement = connection.prepareStatement(sql);
  211. statement.setString(1, playerName);
  212. resultSet = statement.executeQuery();
  213. resultSet.next();
  214. int rank = resultSet.getInt("rank");
  215. sql = "SELECT user, " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " +
  216. "AND " + skillName + " = (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
  217. "WHERE user = '" + playerName + "') ORDER BY user";
  218. statement.close();
  219. statement = connection.prepareStatement(sql);
  220. resultSet = statement.executeQuery();
  221. while (resultSet.next()) {
  222. if (resultSet.getString("user").equalsIgnoreCase(playerName)) {
  223. skills.put(skillType, rank + resultSet.getRow());
  224. break;
  225. }
  226. }
  227. statement.close();
  228. }
  229. String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
  230. "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " +
  231. "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > " +
  232. "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " +
  233. "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)";
  234. PreparedStatement statement = connection.prepareStatement(sql);
  235. statement.setString(1, playerName);
  236. resultSet = statement.executeQuery();
  237. resultSet.next();
  238. int rank = resultSet.getInt("rank");
  239. statement.close();
  240. sql = "SELECT user, taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " +
  241. "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
  242. "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " +
  243. "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy = " +
  244. "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " +
  245. "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?) ORDER BY user";
  246. statement = connection.prepareStatement(sql);
  247. statement.setString(1, playerName);
  248. resultSet = statement.executeQuery();
  249. while (resultSet.next()) {
  250. if (resultSet.getString("user").equalsIgnoreCase(playerName)) {
  251. skills.put(null, rank + resultSet.getRow());
  252. break;
  253. }
  254. }
  255. statement.close();
  256. }
  257. catch (SQLException ex) {
  258. printErrors(ex);
  259. }
  260. }
  261. return skills;
  262. }
  263. public void newUser(String playerName) {
  264. if (!checkConnected()) {
  265. return;
  266. }
  267. PreparedStatement statement = null;
  268. try {
  269. statement = connection.prepareStatement("INSERT INTO " + tablePrefix + "users (user, lastlogin) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS);
  270. statement.setString(1, playerName);
  271. statement.setLong(2, System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
  272. statement.execute();
  273. int id = readId(playerName);
  274. writeMissingRows(id);
  275. }
  276. catch (SQLException ex) {
  277. printErrors(ex);
  278. }
  279. finally {
  280. if (statement != null) {
  281. try {
  282. statement.close();
  283. }
  284. catch (SQLException e) {
  285. // Ignore
  286. }
  287. }
  288. }
  289. }
  290. public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
  291. return loadPlayerProfile(playerName, create, true);
  292. }
  293. private PlayerProfile loadPlayerProfile(String playerName, boolean create, boolean retry) {
  294. if (!checkConnected()) {
  295. return new PlayerProfile(playerName, false); // return fake profile if not connected
  296. }
  297. PreparedStatement statement = null;
  298. try {
  299. statement = connection.prepareStatement(
  300. "SELECT "
  301. + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, "
  302. + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, "
  303. + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, "
  304. + "h.mobhealthbar "
  305. + "FROM " + tablePrefix + "users u "
  306. + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) "
  307. + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) "
  308. + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) "
  309. + "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) "
  310. + "WHERE u.user = ?");
  311. statement.setString(1, playerName);
  312. ResultSet result = statement.executeQuery();
  313. if (result.next()) {
  314. try {
  315. PlayerProfile ret = loadFromResult(playerName, result);
  316. result.close();
  317. return ret;
  318. }
  319. catch (SQLException e) {
  320. }
  321. }
  322. result.close();
  323. }
  324. catch (SQLException ex) {
  325. printErrors(ex);
  326. }
  327. finally {
  328. if (statement != null) {
  329. try {
  330. statement.close();
  331. }
  332. catch (SQLException e) {
  333. // Ignore
  334. }
  335. }
  336. }
  337. // Problem, nothing was returned
  338. // Quit if this is second time around
  339. if (!retry) {
  340. return new PlayerProfile(playerName, false);
  341. }
  342. // First, read User Id - this is to check for orphans
  343. int id = readId(playerName);
  344. if (id == -1) {
  345. // There is no such user
  346. if (create) {
  347. newUser(playerName);
  348. return loadPlayerProfile(playerName, false, false);
  349. }
  350. // Return unloaded profile if can't create
  351. return new PlayerProfile(playerName, false);
  352. }
  353. // There is such a user
  354. writeMissingRows(id);
  355. // Retry, and abort on re-failure
  356. return loadPlayerProfile(playerName, create, false);
  357. }
  358. public void convertUsers(DatabaseManager destination) {
  359. if (!checkConnected()) {
  360. return;
  361. }
  362. PreparedStatement statement = null;
  363. try {
  364. statement = connection.prepareStatement(
  365. "SELECT "
  366. + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, "
  367. + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, "
  368. + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, "
  369. + "h.mobhealthbar "
  370. + "FROM " + tablePrefix + "users u "
  371. + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) "
  372. + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) "
  373. + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) "
  374. + "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) "
  375. + "WHERE u.user = ?");
  376. List<String> usernames = getStoredUsers();
  377. ResultSet resultSet;
  378. int convertedUsers = 0;
  379. long startMillis = System.currentTimeMillis();
  380. for (String playerName : usernames) {
  381. statement.setString(1, playerName);
  382. try {
  383. resultSet = statement.executeQuery();
  384. resultSet.next();
  385. destination.saveUser(loadFromResult(playerName, resultSet));
  386. resultSet.close();
  387. }
  388. catch (SQLException e) {
  389. // Ignore
  390. }
  391. convertedUsers++;
  392. Misc.printProgress(convertedUsers, progressInterval, startMillis);
  393. }
  394. }
  395. catch (SQLException e) {
  396. printErrors(e);
  397. }
  398. finally {
  399. if (statement != null) {
  400. try {
  401. statement.close();
  402. }
  403. catch (SQLException e) {
  404. // Ignore
  405. }
  406. }
  407. }
  408. }
  409. /**
  410. * Check connection status and re-establish if dead or stale.
  411. *
  412. * If the very first immediate attempt fails, further attempts
  413. * will be made in progressively larger intervals up to MAX_WAIT
  414. * intervals.
  415. *
  416. * This allows for MySQL to time out idle connections as needed by
  417. * server operator, without affecting McMMO, while still providing
  418. * protection against a database outage taking down Bukkit's tick
  419. * processing loop due to attempting a database connection each
  420. * time McMMO needs the database.
  421. *
  422. * @return the boolean value for whether or not we are connected
  423. */
  424. public boolean checkConnected() {
  425. boolean isClosed = true;
  426. boolean isValid = false;
  427. boolean exists = (connection != null);
  428. // If we're waiting for server to recover then leave early
  429. if (nextReconnectTimestamp > 0 && nextReconnectTimestamp > System.nanoTime()) {
  430. return false;
  431. }
  432. if (exists) {
  433. try {
  434. isClosed = connection.isClosed();
  435. }
  436. catch (SQLException e) {
  437. isClosed = true;
  438. e.printStackTrace();
  439. printErrors(e);
  440. }
  441. if (!isClosed) {
  442. try {
  443. isValid = connection.isValid(VALID_TIMEOUT);
  444. }
  445. catch (SQLException e) {
  446. // Don't print stack trace because it's valid to lose idle connections to the server and have to restart them.
  447. isValid = false;
  448. }
  449. }
  450. }
  451. // Leave if all ok
  452. if (exists && !isClosed && isValid) {
  453. // Housekeeping
  454. nextReconnectTimestamp = 0;
  455. reconnectAttempt = 0;
  456. return true;
  457. }
  458. // Cleanup after ourselves for GC and MySQL's sake
  459. if (exists && !isClosed) {
  460. try {
  461. connection.close();
  462. }
  463. catch (SQLException ex) {
  464. // This is a housekeeping exercise, ignore errors
  465. }
  466. }
  467. // Try to connect again
  468. connect();
  469. // Leave if connection is good
  470. try {
  471. if (connection != null && !connection.isClosed()) {
  472. // Schedule a database save if we really had an outage
  473. if (reconnectAttempt > 1) {
  474. new SQLReconnectTask().runTaskLater(mcMMO.p, 5);
  475. }
  476. nextReconnectTimestamp = 0;
  477. reconnectAttempt = 0;
  478. return true;
  479. }
  480. }
  481. catch (SQLException e) {
  482. // Failed to check isClosed, so presume connection is bad and attempt later
  483. e.printStackTrace();
  484. printErrors(e);
  485. }
  486. reconnectAttempt++;
  487. nextReconnectTimestamp = (long) (System.nanoTime() + Math.min(MAX_WAIT, (reconnectAttempt * SCALING_FACTOR * MIN_WAIT)));
  488. return false;
  489. }
  490. public List<String> getStoredUsers() {
  491. ArrayList<String> users = new ArrayList<String>();
  492. if (checkConnected()) {
  493. Statement stmt = null;
  494. try {
  495. stmt = connection.createStatement();
  496. ResultSet result = stmt.executeQuery("SELECT user FROM " + tablePrefix + "users");
  497. while (result.next()) {
  498. users.add(result.getString("user"));
  499. }
  500. result.close();
  501. }
  502. catch (SQLException e) {
  503. printErrors(e);
  504. }
  505. finally {
  506. if (stmt != null) {
  507. try {
  508. stmt.close();
  509. }
  510. catch (SQLException e) {
  511. // Ignore
  512. }
  513. }
  514. }
  515. }
  516. return users;
  517. }
  518. /**
  519. * Attempt to connect to the mySQL database.
  520. */
  521. private void connect() {
  522. connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName();
  523. try {
  524. mcMMO.p.getLogger().info("Attempting connection to MySQL...");
  525. // Force driver to load if not yet loaded
  526. Class.forName("com.mysql.jdbc.Driver");
  527. Properties connectionProperties = new Properties();
  528. connectionProperties.put("user", Config.getInstance().getMySQLUserName());
  529. connectionProperties.put("password", Config.getInstance().getMySQLUserPassword());
  530. connectionProperties.put("autoReconnect", "false");
  531. connectionProperties.put("maxReconnects", "0");
  532. connection = DriverManager.getConnection(connectionString, connectionProperties);
  533. mcMMO.p.getLogger().info("Connection to MySQL was a success!");
  534. }
  535. catch (SQLException ex) {
  536. connection = null;
  537. if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
  538. mcMMO.p.getLogger().severe("Connection to MySQL failed!");
  539. printErrors(ex);
  540. }
  541. }
  542. catch (ClassNotFoundException ex) {
  543. connection = null;
  544. if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
  545. mcMMO.p.getLogger().severe("MySQL database driver not found!");
  546. }
  547. }
  548. }
  549. /**
  550. * Checks that the database structure is present and correct
  551. */
  552. private void checkStructure() {
  553. if (!checkConnected()) {
  554. return;
  555. }
  556. write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` ("
  557. + "`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
  558. + "`user` varchar(40) NOT NULL,"
  559. + "`lastlogin` int(32) unsigned NOT NULL,"
  560. + "PRIMARY KEY (`id`),"
  561. + "UNIQUE KEY `user` (`user`)) DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;");
  562. write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "huds` ("
  563. + "`user_id` int(10) unsigned NOT NULL,"
  564. + "`mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "',"
  565. + "PRIMARY KEY (`user_id`)) "
  566. + "DEFAULT CHARSET=latin1;");
  567. write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "cooldowns` ("
  568. + "`user_id` int(10) unsigned NOT NULL,"
  569. + "`taming` int(32) unsigned NOT NULL DEFAULT '0',"
  570. + "`mining` int(32) unsigned NOT NULL DEFAULT '0',"
  571. + "`woodcutting` int(32) unsigned NOT NULL DEFAULT '0',"
  572. + "`repair` int(32) unsigned NOT NULL DEFAULT '0',"
  573. + "`unarmed` int(32) unsigned NOT NULL DEFAULT '0',"
  574. + "`herbalism` int(32) unsigned NOT NULL DEFAULT '0',"
  575. + "`excavation` int(32) unsigned NOT NULL DEFAULT '0',"
  576. + "`archery` int(32) unsigned NOT NULL DEFAULT '0',"
  577. + "`swords` int(32) unsigned NOT NULL DEFAULT '0',"
  578. + "`axes` int(32) unsigned NOT NULL DEFAULT '0',"
  579. + "`acrobatics` int(32) unsigned NOT NULL DEFAULT '0',"
  580. + "`blast_mining` int(32) unsigned NOT NULL DEFAULT '0',"
  581. + "PRIMARY KEY (`user_id`)) "
  582. + "DEFAULT CHARSET=latin1;");
  583. write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "skills` ("
  584. + "`user_id` int(10) unsigned NOT NULL,"
  585. + "`taming` int(10) unsigned NOT NULL DEFAULT '0',"
  586. + "`mining` int(10) unsigned NOT NULL DEFAULT '0',"
  587. + "`woodcutting` int(10) unsigned NOT NULL DEFAULT '0',"
  588. + "`repair` int(10) unsigned NOT NULL DEFAULT '0',"
  589. + "`unarmed` int(10) unsigned NOT NULL DEFAULT '0',"
  590. + "`herbalism` int(10) unsigned NOT NULL DEFAULT '0',"
  591. + "`excavation` int(10) unsigned NOT NULL DEFAULT '0',"
  592. + "`archery` int(10) unsigned NOT NULL DEFAULT '0',"
  593. + "`swords` int(10) unsigned NOT NULL DEFAULT '0',"
  594. + "`axes` int(10) unsigned NOT NULL DEFAULT '0',"
  595. + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0',"
  596. + "`fishing` int(10) unsigned NOT NULL DEFAULT '0',"
  597. + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0',"
  598. + "PRIMARY KEY (`user_id`)) "
  599. + "DEFAULT CHARSET=latin1;");
  600. write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "experience` ("
  601. + "`user_id` int(10) unsigned NOT NULL,"
  602. + "`taming` int(10) unsigned NOT NULL DEFAULT '0',"
  603. + "`mining` int(10) unsigned NOT NULL DEFAULT '0',"
  604. + "`woodcutting` int(10) unsigned NOT NULL DEFAULT '0',"
  605. + "`repair` int(10) unsigned NOT NULL DEFAULT '0',"
  606. + "`unarmed` int(10) unsigned NOT NULL DEFAULT '0',"
  607. + "`herbalism` int(10) unsigned NOT NULL DEFAULT '0',"
  608. + "`excavation` int(10) unsigned NOT NULL DEFAULT '0',"
  609. + "`archery` int(10) unsigned NOT NULL DEFAULT '0',"
  610. + "`swords` int(10) unsigned NOT NULL DEFAULT '0',"
  611. + "`axes` int(10) unsigned NOT NULL DEFAULT '0',"
  612. + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0',"
  613. + "`fishing` int(10) unsigned NOT NULL DEFAULT '0',"
  614. + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0',"
  615. + "PRIMARY KEY (`user_id`)) "
  616. + "DEFAULT CHARSET=latin1;");
  617. for (DatabaseUpdateType updateType : DatabaseUpdateType.values()) {
  618. checkDatabaseStructure(updateType);
  619. }
  620. }
  621. /**
  622. * Check database structure for missing values.
  623. *
  624. * @param update Type of data to check updates for
  625. */
  626. private void checkDatabaseStructure(DatabaseUpdateType update) {
  627. String sql = "";
  628. switch (update) {
  629. case BLAST_MINING:
  630. sql = "SELECT * FROM `" + tablePrefix + "cooldowns` ORDER BY `" + tablePrefix + "cooldowns`.`blast_mining` ASC LIMIT 0 , 30";
  631. break;
  632. case FISHING:
  633. sql = "SELECT * FROM `" + tablePrefix + "experience` ORDER BY `" + tablePrefix + "experience`.`fishing` ASC LIMIT 0 , 30";
  634. break;
  635. case ALCHEMY:
  636. sql = "SELECT * FROM `" + tablePrefix + "experience` ORDER BY `" + tablePrefix + "experience`.`alchemy` ASC LIMIT 0 , 30";
  637. break;
  638. case INDEX:
  639. if (read("SHOW INDEX FROM " + tablePrefix + "skills").size() != 13 && checkConnected()) {
  640. mcMMO.p.getLogger().info("Indexing tables, this may take a while on larger databases");
  641. write("ALTER TABLE `" + tablePrefix + "skills` ADD INDEX `idx_taming` (`taming`) USING BTREE, "
  642. + "ADD INDEX `idx_mining` (`mining`) USING BTREE, "
  643. + "ADD INDEX `idx_woodcutting` (`woodcutting`) USING BTREE, "
  644. + "ADD INDEX `idx_repair` (`repair`) USING BTREE, "
  645. + "ADD INDEX `idx_unarmed` (`unarmed`) USING BTREE, "
  646. + "ADD INDEX `idx_herbalism` (`herbalism`) USING BTREE, "
  647. + "ADD INDEX `idx_excavation` (`excavation`) USING BTREE, "
  648. + "ADD INDEX `idx_archery` (`archery`) USING BTREE, "
  649. + "ADD INDEX `idx_swords` (`swords`) USING BTREE, "
  650. + "ADD INDEX `idx_axes` (`axes`) USING BTREE, "
  651. + "ADD INDEX `idx_acrobatics` (`acrobatics`) USING BTREE, "
  652. + "ADD INDEX `idx_fishing` (`fishing`) USING BTREE;");
  653. }
  654. return;
  655. case MOB_HEALTHBARS:
  656. sql = "SELECT * FROM `" + tablePrefix + "huds` ORDER BY `" + tablePrefix + "huds`.`mobhealthbar` ASC LIMIT 0 , 30";
  657. break;
  658. case PARTY_NAMES:
  659. write("ALTER TABLE `" + tablePrefix + "users` DROP COLUMN `party` ;");
  660. return;
  661. case DROPPED_SPOUT:
  662. write("ALTER TABLE `" + tablePrefix + "huds` DROP COLUMN `hudtype` ;");
  663. return;
  664. case KILL_ORPHANS:
  665. mcMMO.p.getLogger().info("Killing orphans");
  666. write(
  667. "DELETE FROM " + tablePrefix + "experience " +
  668. "WHERE NOT EXISTS (SELECT * FROM " +
  669. tablePrefix + "users u WHERE " +
  670. tablePrefix + "experience.user_id = u.id);"
  671. );
  672. write(
  673. "DELETE FROM " + tablePrefix + "huds " +
  674. "WHERE NOT EXISTS (SELECT * FROM " +
  675. tablePrefix + "users u WHERE " +
  676. tablePrefix + "huds.user_id = u.id);"
  677. );
  678. write(
  679. "DELETE FROM " + tablePrefix + "cooldowns " +
  680. "WHERE NOT EXISTS (SELECT * FROM " +
  681. tablePrefix + "users u WHERE " +
  682. tablePrefix + "cooldowns.user_id = u.id);"
  683. );
  684. write(
  685. "DELETE FROM " + tablePrefix + "skills " +
  686. "WHERE NOT EXISTS (SELECT * FROM " +
  687. tablePrefix + "users u WHERE " +
  688. tablePrefix + "skills.user_id = u.id);"
  689. );
  690. return;
  691. default:
  692. break;
  693. }
  694. ResultSet resultSet;
  695. HashMap<Integer, ArrayList<String>> rows = new HashMap<Integer, ArrayList<String>>();
  696. PreparedStatement statement = null;
  697. try {
  698. if (!checkConnected()) {
  699. return;
  700. }
  701. statement = connection.prepareStatement(sql);
  702. resultSet = statement.executeQuery();
  703. while (resultSet.next()) {
  704. ArrayList<String> column = new ArrayList<String>();
  705. for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
  706. column.add(resultSet.getString(i));
  707. }
  708. rows.put(resultSet.getRow(), column);
  709. }
  710. }
  711. catch (SQLException ex) {
  712. switch (update) {
  713. case BLAST_MINING:
  714. mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Blast Mining...");
  715. write("ALTER TABLE `"+tablePrefix + "cooldowns` ADD `blast_mining` int(32) NOT NULL DEFAULT '0' ;");
  716. break;
  717. case FISHING:
  718. mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Fishing...");
  719. write("ALTER TABLE `"+tablePrefix + "skills` ADD `fishing` int(10) NOT NULL DEFAULT '0' ;");
  720. write("ALTER TABLE `"+tablePrefix + "experience` ADD `fishing` int(10) NOT NULL DEFAULT '0' ;");
  721. break;
  722. case MOB_HEALTHBARS:
  723. mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for mob healthbars...");
  724. write("ALTER TABLE `" + tablePrefix + "huds` ADD `mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "' ;");
  725. break;
  726. case ALCHEMY:
  727. mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Alchemy...");
  728. write("ALTER TABLE `"+tablePrefix + "skills` ADD `alchemy` int(10) NOT NULL DEFAULT '0' ;");
  729. write("ALTER TABLE `"+tablePrefix + "experience` ADD `alchemy` int(10) NOT NULL DEFAULT '0' ;");
  730. break;
  731. default:
  732. break;
  733. }
  734. }
  735. finally {
  736. if (statement != null) {
  737. try {
  738. statement.close();
  739. }
  740. catch (SQLException e) {
  741. // Ignore the error, we're leaving
  742. }
  743. }
  744. }
  745. }
  746. /**
  747. * Attempt to write the SQL query.
  748. *
  749. * @param sql Query to write.
  750. * @return true if the query was successfully written, false otherwise.
  751. */
  752. private boolean write(String sql) {
  753. if (!checkConnected()) {
  754. return false;
  755. }
  756. PreparedStatement statement = null;
  757. try {
  758. statement = connection.prepareStatement(sql);
  759. statement.executeUpdate();
  760. return true;
  761. }
  762. catch (SQLException ex) {
  763. if (!sql.contains("DROP COLUMN")) {
  764. printErrors(ex);
  765. }
  766. return false;
  767. }
  768. finally {
  769. if (statement != null) {
  770. try {
  771. statement.close();
  772. }
  773. catch (SQLException e) {
  774. // Ignore
  775. }
  776. }
  777. }
  778. }
  779. /**
  780. * Returns the number of rows affected by either a DELETE or UPDATE query
  781. *
  782. * @param sql SQL query to execute
  783. * @return the number of rows affected
  784. */
  785. private int update(String sql) {
  786. int rows = 0;
  787. if (checkConnected()) {
  788. PreparedStatement statement = null;
  789. try {
  790. statement = connection.prepareStatement(sql);
  791. rows = statement.executeUpdate();
  792. }
  793. catch (SQLException ex) {
  794. printErrors(ex);
  795. }
  796. finally {
  797. if (statement != null) {
  798. try {
  799. statement.close();
  800. }
  801. catch (SQLException e) {
  802. // Ignore
  803. }
  804. }
  805. }
  806. }
  807. return rows;
  808. }
  809. /**
  810. * Read SQL query.
  811. *
  812. * @param sql SQL query to read
  813. * @return the rows in this SQL query
  814. */
  815. private HashMap<Integer, ArrayList<String>> read(String sql) {
  816. HashMap<Integer, ArrayList<String>> rows = new HashMap<Integer, ArrayList<String>>();
  817. if (checkConnected()) {
  818. PreparedStatement statement = null;
  819. ResultSet resultSet;
  820. try {
  821. statement = connection.prepareStatement(sql);
  822. resultSet = statement.executeQuery();
  823. while (resultSet.next()) {
  824. ArrayList<String> column = new ArrayList<String>();
  825. for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
  826. column.add(resultSet.getString(i));
  827. }
  828. rows.put(resultSet.getRow(), column);
  829. }
  830. }
  831. catch (SQLException ex) {
  832. printErrors(ex);
  833. }
  834. finally {
  835. if (statement != null) {
  836. try {
  837. statement.close();
  838. }
  839. catch (SQLException e) {
  840. // Ignore
  841. }
  842. }
  843. }
  844. }
  845. return rows;
  846. }
  847. /**
  848. * Get the Integer. Only return first row / first field.
  849. *
  850. * @param statement SQL query to execute
  851. * @return the value in the first row / first field
  852. */
  853. private int readInt(PreparedStatement statement) {
  854. int result = -1;
  855. if (checkConnected()) {
  856. ResultSet resultSet;
  857. try {
  858. resultSet = statement.executeQuery();
  859. if (resultSet.next()) {
  860. result = resultSet.getInt(1);
  861. }
  862. }
  863. catch (SQLException ex) {
  864. printErrors(ex);
  865. }
  866. finally {
  867. if (statement != null) {
  868. try {
  869. statement.close();
  870. }
  871. catch (SQLException e) {
  872. // Ignore
  873. }
  874. }
  875. }
  876. }
  877. return result;
  878. }
  879. private void writeMissingRows(int id) {
  880. PreparedStatement statement = null;
  881. try {
  882. statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "experience (user_id) VALUES (?)");
  883. statement.setInt(1, id);
  884. statement.execute();
  885. statement.close();
  886. statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "skills (user_id) VALUES (?)");
  887. statement.setInt(1, id);
  888. statement.execute();
  889. statement.close();
  890. statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "cooldowns (user_id) VALUES (?)");
  891. statement.setInt(1, id);
  892. statement.execute();
  893. statement.close();
  894. statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "huds (user_id, mobhealthbar) VALUES (? ,'" + Config.getInstance().getMobHealthbarDefault().name() + "')");
  895. statement.setInt(1, id);
  896. statement.execute();
  897. statement.close();
  898. }
  899. catch (SQLException ex) {
  900. printErrors(ex);
  901. }
  902. finally {
  903. if (statement != null) {
  904. try {
  905. statement.close();
  906. }
  907. catch (SQLException e) {
  908. // Ignore
  909. }
  910. }
  911. }
  912. }
  913. private void processPurge(Collection<ArrayList<String>> usernames) {
  914. for (ArrayList<String> user : usernames) {
  915. Misc.profileCleanup(user.get(0));
  916. }
  917. }
  918. private boolean saveIntegers(String sql, int... args) {
  919. PreparedStatement statement = null;
  920. try {
  921. statement = connection.prepareStatement(sql);
  922. int i = 1;
  923. for (int arg : args) {
  924. statement.setInt(i++, arg);
  925. }
  926. statement.execute();
  927. return true;
  928. }
  929. catch (SQLException ex) {
  930. printErrors(ex);
  931. return false;
  932. }
  933. finally {
  934. if (statement != null) {
  935. try {
  936. statement.close();
  937. }
  938. catch (SQLException e) {
  939. // Ignore
  940. }
  941. }
  942. }
  943. }
  944. private boolean saveLongs(String sql, int id, long... args) {
  945. PreparedStatement statement = null;
  946. try {
  947. statement = connection.prepareStatement(sql);
  948. int i = 1;
  949. for (long arg : args) {
  950. statement.setLong(i++, arg);
  951. }
  952. statement.setInt(i++, id);
  953. statement.execute();
  954. return true;
  955. }
  956. catch (SQLException ex) {
  957. printErrors(ex);
  958. return false;
  959. }
  960. finally {
  961. if (statement != null) {
  962. try {
  963. statement.close();
  964. }
  965. catch (SQLException e) {
  966. // Ignore
  967. }
  968. }
  969. }
  970. }
  971. /**
  972. * Retrieve the database id for a player
  973. *
  974. * @param playerName The name of the user to retrieve the id for
  975. * @return the requested id or -1 if not found
  976. */
  977. private int readId(String playerName) {
  978. int id = -1;
  979. try {
  980. PreparedStatement statement = connection.prepareStatement("SELECT id FROM " + tablePrefix + "users WHERE user = ?");
  981. statement.setString(1, playerName);
  982. id = readInt(statement);
  983. }
  984. catch (SQLException ex) {
  985. printErrors(ex);
  986. }
  987. return id;
  988. }
  989. private boolean saveLogin(int id, long login) {
  990. PreparedStatement statement = null;
  991. try {
  992. statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET lastlogin = ? WHERE id = ?");
  993. statement.setLong(1, login);
  994. statement.setInt(2, id);
  995. statement.execute();
  996. return true;
  997. }
  998. catch (SQLException ex) {
  999. printErrors(ex);
  1000. return false;
  1001. }
  1002. finally {
  1003. if (statement != null) {
  1004. try {
  1005. statement.close();
  1006. }
  1007. catch (SQLException e) {
  1008. // Ignore
  1009. }
  1010. }
  1011. }
  1012. }
  1013. private boolean saveHuds(int userId, String hudType, String mobHealthBar) {
  1014. PreparedStatement statement = null;
  1015. try {
  1016. statement = connection.prepareStatement("UPDATE " + tablePrefix + "huds SET mobhealthbar = ? WHERE user_id = ?");
  1017. statement.setString(1, mobHealthBar);
  1018. statement.setInt(2, userId);
  1019. statement.execute();
  1020. return true;
  1021. }
  1022. catch (SQLException ex) {
  1023. printErrors(ex);
  1024. return false;
  1025. }
  1026. finally {
  1027. if (statement != null) {
  1028. try {
  1029. statement.close();
  1030. }
  1031. catch (SQLException e) {
  1032. // Ignore
  1033. }
  1034. }
  1035. }
  1036. }
  1037. private PlayerProfile loadFromResult(String playerName, ResultSet result) throws SQLException {
  1038. Map<SkillType, Integer> skills = new HashMap<SkillType, Integer>(); // Skill & Level
  1039. Map<SkillType, Float> skillsXp = new HashMap<SkillType, Float>(); // Skill & XP
  1040. Map<AbilityType, Integer> skillsDATS = new HashMap<AbilityType, Integer>(); // Ability & Cooldown
  1041. HudType hudType;
  1042. MobHealthbarType mobHealthbarType;
  1043. final int OFFSET_SKILLS = 0; // TODO update these numbers when the query changes (a new skill is added)
  1044. final int OFFSET_XP = 13;
  1045. final int OFFSET_DATS = 26;
  1046. final int OFFSET_OTHER = 38;
  1047. skills.put(SkillType.TAMING, result.getInt(OFFSET_SKILLS + 1));
  1048. skills.put(SkillType.MINING, result.getInt(OFFSET_SKILLS + 2));
  1049. skills.put(SkillType.REPAIR, result.getInt(OFFSET_SKILLS + 3));
  1050. skills.put(SkillType.WOODCUTTING, result.getInt(OFFSET_SKILLS + 4));
  1051. skills.put(SkillType.UNARMED, result.getInt(OFFSET_SKILLS + 5));
  1052. skills.put(SkillType.HERBALISM, result.getInt(OFFSET_SKILLS + 6));
  1053. skills.put(SkillType.EXCAVATION, result.getInt(OFFSET_SKILLS + 7));
  1054. skills.put(SkillType.ARCHERY, result.getInt(OFFSET_SKILLS + 8));
  1055. skills.put(SkillType.SWORDS, result.getInt(OFFSET_SKILLS + 9));
  1056. skills.put(SkillType.AXES, result.getInt(OFFSET_SKILLS + 10));
  1057. skills.put(SkillType.ACROBATICS, result.getInt(OFFSET_SKILLS + 11));
  1058. skills.put(SkillType.FISHING, result.getInt(OFFSET_SKILLS + 12));
  1059. skills.put(SkillType.ALCHEMY, result.getInt(OFFSET_SKILLS + 13));
  1060. skillsXp.put(SkillType.TAMING, result.getFloat(OFFSET_XP + 1));
  1061. skillsXp.put(SkillType.MINING, result.getFloat(OFFSET_XP + 2));
  1062. skillsXp.put(SkillType.REPAIR, result.getFloat(OFFSET_XP + 3));
  1063. skillsXp.put(SkillType.WOODCUTTING, result.getFloat(OFFSET_XP + 4));
  1064. skillsXp.put(SkillType.UNARMED, result.getFloat(OFFSET_XP + 5));
  1065. skillsXp.put(SkillType.HERBALISM, result.getFloat(OFFSET_XP + 6));
  1066. skillsXp.put(SkillType.EXCAVATION, result.getFloat(OFFSET_XP + 7));
  1067. skillsXp.put(SkillType.ARCHERY, result.getFloat(OFFSET_XP + 8));
  1068. skillsXp.put(SkillType.SWORDS, result.getFloat(OFFSET_XP + 9));
  1069. skillsXp.put(SkillType.AXES, result.getFloat(OFFSET_XP + 10));
  1070. skillsXp.put(SkillType.ACROBATICS, result.getFloat(OFFSET_XP + 11));
  1071. skillsXp.put(SkillType.FISHING, result.getFloat(OFFSET_XP + 12));
  1072. skillsXp.put(SkillType.ALCHEMY, result.getFloat(OFFSET_XP + 13));
  1073. // Taming - Unused - result.getInt(OFFSET_DATS + 1)
  1074. skillsDATS.put(AbilityType.SUPER_BREAKER, result.getInt(OFFSET_DATS + 2));
  1075. // Repair - Unused - result.getInt(OFFSET_DATS + 3)
  1076. skillsDATS.put(AbilityType.TREE_FELLER, result.getInt(OFFSET_DATS + 4));
  1077. skillsDATS.put(AbilityType.BERSERK, result.getInt(OFFSET_DATS + 5));
  1078. skillsDATS.put(AbilityType.GREEN_TERRA, result.getInt(OFFSET_DATS + 6));
  1079. skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, result.getInt(OFFSET_DATS + 7));
  1080. // Archery - Unused - result.getInt(OFFSET_DATS + 8)
  1081. skillsDATS.put(AbilityType.SERRATED_STRIKES, result.getInt(OFFSET_DATS + 9));
  1082. skillsDATS.put(AbilityType.SKULL_SPLITTER, result.getInt(OFFSET_DATS + 10));
  1083. // Acrobatics - Unused - result.getInt(OFFSET_DATS + 11)
  1084. skillsDATS.put(AbilityType.BLAST_MINING, result.getInt(OFFSET_DATS + 12));
  1085. try {
  1086. hudType = HudType.valueOf(result.getString(OFFSET_OTHER + 1));
  1087. }
  1088. catch (Exception e) {
  1089. hudType = HudType.STANDARD; // Shouldn't happen unless database is being tampered with
  1090. }
  1091. try {
  1092. mobHealthbarType = MobHealthbarType.valueOf(result.getString(OFFSET_OTHER + 2));
  1093. }
  1094. catch (Exception e) {
  1095. mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
  1096. }
  1097. return new PlayerProfile(playerName, skills, skillsXp, skillsDATS, hudType, mobHealthbarType);
  1098. }
  1099. private void printErrors(SQLException ex) {
  1100. mcMMO.p.getLogger().severe("SQLException: " + ex.getMessage());
  1101. mcMMO.p.getLogger().severe("SQLState: " + ex.getSQLState());
  1102. mcMMO.p.getLogger().severe("VendorError: " + ex.getErrorCode());
  1103. }
  1104. public DatabaseType getDatabaseType() {
  1105. return DatabaseType.SQL;
  1106. }
  1107. }