001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.database; 020 021import com.google.common.base.Charsets; 022import com.plotsquared.core.PlotSquared; 023import com.plotsquared.core.configuration.ConfigurationSection; 024import com.plotsquared.core.configuration.Settings; 025import com.plotsquared.core.configuration.Storage; 026import com.plotsquared.core.configuration.caption.CaptionUtility; 027import com.plotsquared.core.configuration.file.YamlConfiguration; 028import com.plotsquared.core.inject.annotations.WorldConfig; 029import com.plotsquared.core.listener.PlotListener; 030import com.plotsquared.core.location.BlockLoc; 031import com.plotsquared.core.plot.Plot; 032import com.plotsquared.core.plot.PlotArea; 033import com.plotsquared.core.plot.PlotCluster; 034import com.plotsquared.core.plot.PlotId; 035import com.plotsquared.core.plot.PlotSettings; 036import com.plotsquared.core.plot.comment.PlotComment; 037import com.plotsquared.core.plot.flag.FlagContainer; 038import com.plotsquared.core.plot.flag.FlagParseException; 039import com.plotsquared.core.plot.flag.GlobalFlagContainer; 040import com.plotsquared.core.plot.flag.PlotFlag; 041import com.plotsquared.core.plot.flag.types.BlockTypeListFlag; 042import com.plotsquared.core.util.EventDispatcher; 043import com.plotsquared.core.util.HashUtil; 044import com.plotsquared.core.util.StringMan; 045import com.plotsquared.core.util.task.RunnableVal; 046import com.plotsquared.core.util.task.TaskManager; 047import org.apache.logging.log4j.LogManager; 048import org.apache.logging.log4j.Logger; 049import org.checkerframework.checker.nullness.qual.NonNull; 050 051import java.sql.Connection; 052import java.sql.DatabaseMetaData; 053import java.sql.PreparedStatement; 054import java.sql.ResultSet; 055import java.sql.SQLException; 056import java.sql.Statement; 057import java.sql.Timestamp; 058import java.text.ParseException; 059import java.text.SimpleDateFormat; 060import java.util.ArrayList; 061import java.util.Collection; 062import java.util.HashMap; 063import java.util.HashSet; 064import java.util.Iterator; 065import java.util.LinkedHashMap; 066import java.util.List; 067import java.util.Map; 068import java.util.Map.Entry; 069import java.util.Queue; 070import java.util.Set; 071import java.util.UUID; 072import java.util.concurrent.CompletableFuture; 073import java.util.concurrent.ConcurrentHashMap; 074import java.util.concurrent.ConcurrentLinkedQueue; 075import java.util.concurrent.atomic.AtomicInteger; 076 077 078@SuppressWarnings("SqlDialectInspection") 079public class SQLManager implements AbstractDB { 080 081 private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + SQLManager.class.getSimpleName()); 082 083 // Public final 084 public final String SET_OWNER; 085 public final String GET_ALL_PLOTS; 086 public final String CREATE_PLOTS; 087 public final String CREATE_SETTINGS; 088 public final String CREATE_TIERS; 089 public final String CREATE_PLOT; 090 public final String CREATE_PLOT_SAFE; 091 public final String CREATE_CLUSTER; 092 093 // Private Final 094 private final String prefix; 095 private final Database database; 096 private final boolean mySQL; 097 @SuppressWarnings({"unused", "FieldCanBeLocal"}) 098 private final EventDispatcher eventDispatcher; 099 @SuppressWarnings({"unused", "FieldCanBeLocal"}) 100 private final PlotListener plotListener; 101 private final YamlConfiguration worldConfiguration; 102 /** 103 * important tasks 104 */ 105 public volatile Queue<Runnable> globalTasks; 106 /** 107 * Notify tasks 108 */ 109 public volatile Queue<Runnable> notifyTasks; 110 /** 111 * plot 112 * plot_denied 113 * plot_helpers 114 * plot_trusted 115 * plot_comments 116 * plot_settings 117 * plot_rating 118 */ 119 public volatile ConcurrentHashMap<Plot, Queue<UniqueStatement>> plotTasks; 120 /** 121 * player_meta 122 */ 123 public volatile ConcurrentHashMap<UUID, Queue<UniqueStatement>> playerTasks; 124 /** 125 * cluster 126 * cluster_helpers 127 * cluster_invited 128 * cluster_settings 129 */ 130 public volatile ConcurrentHashMap<PlotCluster, Queue<UniqueStatement>> clusterTasks; 131 // Private 132 private Connection connection; 133 private boolean supportsGetGeneratedKeys; 134 private boolean closed = false; 135 136 /** 137 * Constructor 138 * 139 * @param database 140 * @param prefix prefix 141 * @throws SQLException 142 * @throws ClassNotFoundException 143 */ 144 public SQLManager( 145 final @NonNull Database database, 146 final @NonNull String prefix, 147 final @NonNull EventDispatcher eventDispatcher, 148 final @NonNull PlotListener plotListener, 149 @WorldConfig final @NonNull YamlConfiguration worldConfiguration 150 ) 151 throws SQLException, ClassNotFoundException { 152 // Private final 153 this.eventDispatcher = eventDispatcher; 154 this.plotListener = plotListener; 155 this.worldConfiguration = worldConfiguration; 156 this.database = database; 157 this.connection = database.openConnection(); 158 final DatabaseMetaData databaseMetaData = this.connection.getMetaData(); 159 this.supportsGetGeneratedKeys = databaseMetaData.supportsGetGeneratedKeys(); 160 this.mySQL = database instanceof MySQL; 161 this.globalTasks = new ConcurrentLinkedQueue<>(); 162 this.notifyTasks = new ConcurrentLinkedQueue<>(); 163 this.plotTasks = new ConcurrentHashMap<>(); 164 this.playerTasks = new ConcurrentHashMap<>(); 165 this.clusterTasks = new ConcurrentHashMap<>(); 166 this.prefix = prefix; 167 168 if (mySQL && !supportsGetGeneratedKeys) { 169 String driver = databaseMetaData.getDriverName(); 170 String driverVersion = databaseMetaData.getDriverVersion(); 171 throw new SQLException("Database Driver for MySQL does not support Statement#getGeneratedKeys - which breaks " + 172 "PlotSquared functionality (Using " + driver + ":" + driverVersion + ")"); 173 } 174 175 this.SET_OWNER = "UPDATE `" + this.prefix 176 + "plot` SET `owner` = ? WHERE `plot_id_x` = ? AND `plot_id_z` = ? AND `world` = ?"; 177 this.GET_ALL_PLOTS = 178 "SELECT `id`, `plot_id_x`, `plot_id_z`, `world` FROM `" + this.prefix + "plot`"; 179 this.CREATE_PLOTS = "INSERT INTO `" + this.prefix 180 + "plot`(`plot_id_x`, `plot_id_z`, `owner`, `world`, `timestamp`) values "; 181 this.CREATE_SETTINGS = 182 "INSERT INTO `" + this.prefix + "plot_settings` (`plot_plot_id`) values "; 183 this.CREATE_TIERS = 184 "INSERT INTO `" + this.prefix + "plot_%tier%` (`plot_plot_id`, `user_uuid`) values "; 185 String tempCreatePlot = "INSERT INTO `" + this.prefix 186 + "plot`(`plot_id_x`, `plot_id_z`, `owner`, `world`, `timestamp`) VALUES(?, ?, ?, ?, ?)"; 187 if (!supportsGetGeneratedKeys) { 188 tempCreatePlot += " RETURNING `id`"; 189 } 190 this.CREATE_PLOT = tempCreatePlot; 191 if (mySQL) { 192 this.CREATE_PLOT_SAFE = "INSERT IGNORE INTO `" + this.prefix 193 + "plot`(`plot_id_x`, `plot_id_z`, `owner`, `world`, `timestamp`) SELECT ?, ?, ?, ?, ? FROM DUAL WHERE NOT EXISTS (SELECT null FROM `" 194 + this.prefix + "plot` WHERE `world` = ? AND `plot_id_x` = ? AND `plot_id_z` = ?)"; 195 } else { 196 String tempCreatePlotSafe = "INSERT INTO `" + this.prefix 197 + "plot`(`plot_id_x`, `plot_id_z`, `owner`, `world`, `timestamp`) SELECT ?, ?, ?, ?, ? WHERE NOT EXISTS (SELECT null FROM `" 198 + this.prefix + "plot` WHERE `world` = ? AND `plot_id_x` = ? AND `plot_id_z` = ?)"; 199 if (!supportsGetGeneratedKeys) { 200 tempCreatePlotSafe += " RETURNING `id`"; 201 } 202 this.CREATE_PLOT_SAFE = tempCreatePlotSafe; 203 } 204 String tempCreateCluster = "INSERT INTO `" + this.prefix 205 + "cluster`(`pos1_x`, `pos1_z`, `pos2_x`, `pos2_z`, `owner`, `world`) VALUES(?, ?, ?, ?, ?, ?)"; 206 if (!supportsGetGeneratedKeys) { 207 tempCreateCluster += " RETURNING `id`"; 208 } 209 this.CREATE_CLUSTER = tempCreateCluster; 210 211 try { 212 createTables(); 213 } catch (SQLException e) { 214 e.printStackTrace(); 215 } 216 TaskManager.runTaskAsync(() -> { 217 long last = System.currentTimeMillis(); 218 while (!SQLManager.this.closed) { 219 boolean hasTask = 220 !globalTasks.isEmpty() || !playerTasks.isEmpty() || !plotTasks.isEmpty() 221 || !clusterTasks.isEmpty(); 222 if (hasTask) { 223 if (SQLManager.this.mySQL && System.currentTimeMillis() - last > 550000 224 || !isValid()) { 225 last = System.currentTimeMillis(); 226 reconnect(); 227 } 228 if (!sendBatch()) { 229 try { 230 if (!getNotifyTasks().isEmpty()) { 231 for (Runnable task : getNotifyTasks()) { 232 TaskManager.runTask(task); 233 } 234 getNotifyTasks().clear(); 235 } 236 Thread.sleep(50); 237 } catch (InterruptedException e) { 238 e.printStackTrace(); 239 } 240 } 241 } else { 242 try { 243 Thread.sleep(1000); 244 } catch (InterruptedException e) { 245 e.printStackTrace(); 246 } 247 } 248 } 249 }); 250 } 251 252 public boolean isValid() { 253 try { 254 if (connection.isClosed()) { 255 return false; 256 } 257 } catch (SQLException e) { 258 return false; 259 } 260 try (PreparedStatement stmt = this.connection.prepareStatement("SELECT 1")) { 261 stmt.execute(); 262 return true; 263 } catch (Throwable e) { 264 return false; 265 } 266 } 267 268 public void reconnect() { 269 try { 270 close(); 271 SQLManager.this.closed = false; 272 SQLManager.this.connection = database.forceConnection(); 273 } catch (SQLException | ClassNotFoundException e) { 274 e.printStackTrace(); 275 } 276 } 277 278 public synchronized Queue<Runnable> getGlobalTasks() { 279 return this.globalTasks; 280 } 281 282 public synchronized Queue<Runnable> getNotifyTasks() { 283 return this.notifyTasks; 284 } 285 286 public synchronized void addPlotTask(@NonNull Plot plot, UniqueStatement task) { 287 Queue<UniqueStatement> tasks = this.plotTasks.get(plot); 288 if (tasks == null) { 289 tasks = new ConcurrentLinkedQueue<>(); 290 this.plotTasks.put(plot, tasks); 291 } 292 if (task == null) { 293 task = new UniqueStatement(String.valueOf(plot.hashCode())) { 294 295 @Override 296 public PreparedStatement get() { 297 return null; 298 } 299 300 @Override 301 public void set(PreparedStatement statement) { 302 } 303 304 @Override 305 public void addBatch(PreparedStatement statement) { 306 } 307 308 @Override 309 public void execute(PreparedStatement statement) { 310 } 311 312 }; 313 } 314 tasks.add(task); 315 } 316 317 public synchronized void addPlayerTask(UUID uuid, UniqueStatement task) { 318 if (uuid == null) { 319 return; 320 } 321 Queue<UniqueStatement> tasks = this.playerTasks.get(uuid); 322 if (tasks == null) { 323 tasks = new ConcurrentLinkedQueue<>(); 324 this.playerTasks.put(uuid, tasks); 325 } 326 if (task == null) { 327 task = new UniqueStatement(String.valueOf(uuid.hashCode())) { 328 329 @Override 330 public PreparedStatement get() { 331 return null; 332 } 333 334 @Override 335 public void set(PreparedStatement statement) { 336 } 337 338 @Override 339 public void addBatch(PreparedStatement statement) { 340 } 341 342 @Override 343 public void execute(PreparedStatement statement) { 344 } 345 346 }; 347 } 348 tasks.add(task); 349 } 350 351 public synchronized void addClusterTask(PlotCluster cluster, UniqueStatement task) { 352 Queue<UniqueStatement> tasks = this.clusterTasks.get(cluster); 353 if (tasks == null) { 354 tasks = new ConcurrentLinkedQueue<>(); 355 this.clusterTasks.put(cluster, tasks); 356 } 357 if (task == null) { 358 task = new UniqueStatement(String.valueOf(cluster.hashCode())) { 359 360 @Override 361 public PreparedStatement get() { 362 return null; 363 } 364 365 @Override 366 public void set(PreparedStatement statement) { 367 } 368 369 @Override 370 public void addBatch(PreparedStatement statement) { 371 } 372 373 @Override 374 public void execute(PreparedStatement statement) { 375 } 376 377 }; 378 } 379 tasks.add(task); 380 } 381 382 public synchronized void addGlobalTask(Runnable task) { 383 getGlobalTasks().add(task); 384 } 385 386 public synchronized void addNotifyTask(Runnable task) { 387 if (task != null) { 388 getNotifyTasks().add(task); 389 } 390 } 391 392 public synchronized boolean sendBatch() { 393 try { 394 if (!getGlobalTasks().isEmpty()) { 395 if (this.connection.getAutoCommit()) { 396 this.connection.setAutoCommit(false); 397 } 398 Runnable task = getGlobalTasks().remove(); 399 if (task != null) { 400 try { 401 task.run(); 402 } catch (Throwable e) { 403 LOGGER.error("============ DATABASE ERROR ============"); 404 LOGGER.error("============ DATABASE ERROR ============"); 405 LOGGER.error("There was an error updating the database."); 406 LOGGER.error(" - It will be corrected on shutdown"); 407 e.printStackTrace(); 408 LOGGER.error("========================================"); 409 } 410 } 411 commit(); 412 return true; 413 } 414 int count = -1; 415 if (!this.plotTasks.isEmpty()) { 416 count = Math.max(count, 0); 417 if (this.connection.getAutoCommit()) { 418 this.connection.setAutoCommit(false); 419 } 420 String method = null; 421 PreparedStatement statement = null; 422 UniqueStatement task = null; 423 UniqueStatement lastTask = null; 424 Iterator<Entry<Plot, Queue<UniqueStatement>>> iterator = 425 this.plotTasks.entrySet().iterator(); 426 while (iterator.hasNext()) { 427 try { 428 Entry<Plot, Queue<UniqueStatement>> entry = iterator.next(); 429 Queue<UniqueStatement> tasks = entry.getValue(); 430 if (tasks.isEmpty()) { 431 iterator.remove(); 432 continue; 433 } 434 task = tasks.remove(); 435 count++; 436 if (task != null) { 437 if (task.method == null || !task.method.equals(method) 438 || statement == null) { 439 if (statement != null) { 440 lastTask.execute(statement); 441 statement.close(); 442 } 443 method = task.method; 444 statement = task.get(); 445 } 446 task.set(statement); 447 task.addBatch(statement); 448 try { 449 if (statement.isClosed()) { 450 statement = null; 451 } 452 } catch (NullPointerException | AbstractMethodError ignore) { 453 } 454 } 455 lastTask = task; 456 } catch (Throwable e) { 457 LOGGER.error("============ DATABASE ERROR ============"); 458 LOGGER.error("There was an error updating the database."); 459 LOGGER.error(" - It will be corrected on shutdown"); 460 LOGGER.error("========================================"); 461 e.printStackTrace(); 462 LOGGER.error("========================================"); 463 } 464 } 465 if (statement != null && task != null) { 466 task.execute(statement); 467 statement.close(); 468 } 469 } 470 if (!this.playerTasks.isEmpty()) { 471 count = Math.max(count, 0); 472 if (this.connection.getAutoCommit()) { 473 this.connection.setAutoCommit(false); 474 } 475 String method = null; 476 PreparedStatement statement = null; 477 UniqueStatement task = null; 478 UniqueStatement lastTask = null; 479 for (Entry<UUID, Queue<UniqueStatement>> entry : this.playerTasks.entrySet()) { 480 try { 481 UUID uuid = entry.getKey(); 482 if (this.playerTasks.get(uuid).isEmpty()) { 483 this.playerTasks.remove(uuid); 484 continue; 485 } 486 task = this.playerTasks.get(uuid).remove(); 487 count++; 488 if (task != null) { 489 if (task.method == null || !task.method.equals(method)) { 490 if (statement != null) { 491 lastTask.execute(statement); 492 statement.close(); 493 } 494 method = task.method; 495 statement = task.get(); 496 } 497 task.set(statement); 498 task.addBatch(statement); 499 } 500 lastTask = task; 501 } catch (Throwable e) { 502 LOGGER.error("============ DATABASE ERROR ============"); 503 LOGGER.error("There was an error updating the database."); 504 LOGGER.error(" - It will be corrected on shutdown"); 505 LOGGER.error("========================================"); 506 e.printStackTrace(); 507 LOGGER.error("========================================"); 508 } 509 } 510 if (statement != null && task != null) { 511 task.execute(statement); 512 statement.close(); 513 } 514 } 515 if (!this.clusterTasks.isEmpty()) { 516 count = Math.max(count, 0); 517 if (this.connection.getAutoCommit()) { 518 this.connection.setAutoCommit(false); 519 } 520 String method = null; 521 PreparedStatement statement = null; 522 UniqueStatement task = null; 523 UniqueStatement lastTask = null; 524 for (Entry<PlotCluster, Queue<UniqueStatement>> entry : this.clusterTasks 525 .entrySet()) { 526 try { 527 PlotCluster cluster = entry.getKey(); 528 if (this.clusterTasks.get(cluster).isEmpty()) { 529 this.clusterTasks.remove(cluster); 530 continue; 531 } 532 task = this.clusterTasks.get(cluster).remove(); 533 count++; 534 if (task != null) { 535 if (task.method == null || !task.method.equals(method)) { 536 if (statement != null) { 537 lastTask.execute(statement); 538 statement.close(); 539 } 540 method = task.method; 541 statement = task.get(); 542 } 543 task.set(statement); 544 task.addBatch(statement); 545 } 546 lastTask = task; 547 } catch (Throwable e) { 548 LOGGER.error("============ DATABASE ERROR ============"); 549 LOGGER.error("There was an error updating the database."); 550 LOGGER.error(" - It will be corrected on shutdown"); 551 LOGGER.error("========================================"); 552 e.printStackTrace(); 553 LOGGER.error("========================================"); 554 } 555 } 556 if (statement != null && task != null) { 557 task.execute(statement); 558 statement.close(); 559 } 560 } 561 if (count > 0) { 562 commit(); 563 return true; 564 } 565 if (count != -1) { 566 if (!this.connection.getAutoCommit()) { 567 this.connection.setAutoCommit(true); 568 } 569 } 570 if (!this.clusterTasks.isEmpty()) { 571 this.clusterTasks.clear(); 572 } 573 if (!this.plotTasks.isEmpty()) { 574 this.plotTasks.clear(); 575 } 576 } catch (Throwable e) { 577 LOGGER.error("============ DATABASE ERROR ============"); 578 LOGGER.error("There was an error updating the database."); 579 LOGGER.error(" - It will be corrected on shutdown"); 580 LOGGER.error("========================================"); 581 e.printStackTrace(); 582 LOGGER.error("========================================"); 583 } 584 return false; 585 } 586 587 public Connection getConnection() { 588 return this.connection; 589 } 590 591 /** 592 * Set Plot owner 593 * 594 * @param plot Plot Object 595 * @param uuid Owner UUID 596 */ 597 @Override 598 public void setOwner(final Plot plot, final UUID uuid) { 599 addPlotTask(plot, new UniqueStatement("setOwner") { 600 @Override 601 public void set(PreparedStatement statement) throws SQLException { 602 statement.setString(1, uuid.toString()); 603 statement.setInt(2, plot.getId().getX()); 604 statement.setInt(3, plot.getId().getY()); 605 statement.setString(4, plot.getArea().toString()); 606 } 607 608 @Override 609 public PreparedStatement get() throws SQLException { 610 return SQLManager.this.connection.prepareStatement(SQLManager.this.SET_OWNER); 611 } 612 }); 613 } 614 615 @Override 616 public void createPlotsAndData(final List<Plot> myList, final Runnable whenDone) { 617 addGlobalTask(() -> { 618 try { 619 // Create the plots 620 createPlots(myList, () -> { 621 final Map<PlotId, Integer> idMap = new HashMap<>(); 622 623 try { 624 // Creating datastructures 625 HashMap<PlotId, Plot> plotMap = new HashMap<>(); 626 for (Plot plot : myList) { 627 plotMap.put(plot.getId(), plot); 628 } 629 ArrayList<LegacySettings> settings = new ArrayList<>(); 630 final ArrayList<UUIDPair> helpers = new ArrayList<>(); 631 final ArrayList<UUIDPair> trusted = new ArrayList<>(); 632 final ArrayList<UUIDPair> denied = new ArrayList<>(); 633 634 // Populating structures 635 try (PreparedStatement stmt = SQLManager.this.connection 636 .prepareStatement(SQLManager.this.GET_ALL_PLOTS); 637 ResultSet result = stmt.executeQuery()) { 638 while (result.next()) { 639 int id = result.getInt("id"); 640 int x = result.getInt("plot_id_x"); 641 int y = result.getInt("plot_id_z"); 642 PlotId plotId = PlotId.of(x, y); 643 Plot plot = plotMap.get(plotId); 644 idMap.put(plotId, id); 645 if (plot != null) { 646 settings.add(new LegacySettings(id, plot.getSettings())); 647 for (UUID uuid : plot.getDenied()) { 648 denied.add(new UUIDPair(id, uuid)); 649 } 650 for (UUID uuid : plot.getMembers()) { 651 trusted.add(new UUIDPair(id, uuid)); 652 } 653 for (UUID uuid : plot.getTrusted()) { 654 helpers.add(new UUIDPair(id, uuid)); 655 } 656 } 657 } 658 } 659 660 createFlags(idMap, myList, () -> createSettings( 661 settings, 662 () -> createTiers(helpers, "helpers", 663 () -> createTiers(trusted, "trusted", 664 () -> createTiers(denied, "denied", () -> { 665 try { 666 SQLManager.this.connection.commit(); 667 } catch (SQLException e) { 668 e.printStackTrace(); 669 } 670 if (whenDone != null) { 671 whenDone.run(); 672 } 673 }) 674 ) 675 ) 676 )); 677 } catch (SQLException e) { 678 LOGGER.warn("Failed to set all flags and member tiers for plots", e); 679 try { 680 SQLManager.this.connection.commit(); 681 } catch (SQLException e1) { 682 e1.printStackTrace(); 683 } 684 } 685 }); 686 } catch (Exception e) { 687 LOGGER.warn("Warning! Failed to set all helper for plots", e); 688 try { 689 SQLManager.this.connection.commit(); 690 } catch (SQLException e1) { 691 e1.printStackTrace(); 692 } 693 } 694 }); 695 } 696 697 /** 698 * Create a plot 699 * 700 * @param myList list of plots to be created 701 */ 702 public void createTiers(ArrayList<UUIDPair> myList, final String tier, Runnable whenDone) { 703 StmtMod<UUIDPair> mod = new StmtMod<>() { 704 @Override 705 public String getCreateMySQL(int size) { 706 return getCreateMySQL(size, SQLManager.this.CREATE_TIERS.replaceAll("%tier%", tier), 707 2 708 ); 709 } 710 711 @Override 712 public String getCreateSQLite(int size) { 713 return getCreateSQLite(size, 714 "INSERT INTO `" + SQLManager.this.prefix + "plot_" + tier 715 + "` SELECT ? AS `plot_plot_id`, ? AS `user_uuid`", 2 716 ); 717 } 718 719 @Override 720 public String getCreateSQL() { 721 return "INSERT INTO `" + SQLManager.this.prefix + "plot_" + tier 722 + "` (`plot_plot_id`, `user_uuid`) VALUES(?,?)"; 723 } 724 725 @Override 726 public void setMySQL(PreparedStatement stmt, int i, UUIDPair pair) 727 throws SQLException { 728 stmt.setInt(i * 2 + 1, pair.id); 729 stmt.setString(i * 2 + 2, pair.uuid.toString()); 730 } 731 732 @Override 733 public void setSQLite(PreparedStatement stmt, int i, UUIDPair pair) 734 throws SQLException { 735 stmt.setInt(i * 2 + 1, pair.id); 736 stmt.setString(i * 2 + 2, pair.uuid.toString()); 737 } 738 739 @Override 740 public void setSQL(PreparedStatement stmt, UUIDPair pair) 741 throws SQLException { 742 stmt.setInt(1, pair.id); 743 stmt.setString(2, pair.uuid.toString()); 744 } 745 }; 746 setBulk(myList, mod, whenDone); 747 } 748 749 public void createFlags(Map<PlotId, Integer> ids, List<Plot> plots, Runnable whenDone) { 750 try (final PreparedStatement preparedStatement = this.connection.prepareStatement( 751 "INSERT INTO `" + SQLManager.this.prefix 752 + "plot_flags`(`plot_id`, `flag`, `value`) VALUES(?, ?, ?)")) { 753 for (final Plot plot : plots) { 754 final FlagContainer flagContainer = plot.getFlagContainer(); 755 for (final PlotFlag<?, ?> flagEntry : flagContainer.getFlagMap().values()) { 756 preparedStatement.setInt(1, ids.get(plot.getId())); 757 preparedStatement.setString(2, flagEntry.getName()); 758 preparedStatement.setString(3, flagEntry.toString()); 759 preparedStatement.addBatch(); 760 } 761 try { 762 preparedStatement.executeBatch(); 763 } catch (final Exception e) { 764 LOGGER.error("Failed to store flag values for plot with entry ID: {}", plot); 765 e.printStackTrace(); 766 continue; 767 } 768 LOGGER.info( 769 "- Finished converting flag values for plot with entry ID: {}", 770 plot.getId() 771 ); 772 } 773 } catch (final Exception e) { 774 LOGGER.error("Failed to store flag values", e); 775 } 776 LOGGER.info("Finished converting flags ({} plots processed)", plots.size()); 777 whenDone.run(); 778 } 779 780 /** 781 * Create a plot 782 * 783 * @param myList list of plots to be created 784 */ 785 public void createPlots(List<Plot> myList, Runnable whenDone) { 786 StmtMod<Plot> mod = new StmtMod<>() { 787 @Override 788 public String getCreateMySQL(int size) { 789 return getCreateMySQL(size, SQLManager.this.CREATE_PLOTS, 5); 790 } 791 792 @Override 793 public String getCreateSQLite(int size) { 794 return getCreateSQLite(size, "INSERT INTO `" + SQLManager.this.prefix 795 + "plot` SELECT ? AS `id`, ? AS `plot_id_x`, ? AS `plot_id_z`, ? AS `owner`, ? AS `world`, ? AS `timestamp` ", 796 6 797 ); 798 } 799 800 @Override 801 public String getCreateSQL() { 802 return SQLManager.this.CREATE_PLOT; 803 } 804 805 @Override 806 public void setMySQL(PreparedStatement stmt, int i, Plot plot) 807 throws SQLException { 808 stmt.setInt(i * 5 + 1, plot.getId().getX()); 809 stmt.setInt(i * 5 + 2, plot.getId().getY()); 810 try { 811 stmt.setString(i * 5 + 3, plot.getOwnerAbs().toString()); 812 } catch (SQLException ignored) { 813 stmt.setString(i * 5 + 3, everyone.toString()); 814 } 815 stmt.setString(i * 5 + 4, plot.getArea().toString()); 816 stmt.setTimestamp(i * 5 + 5, new Timestamp(plot.getTimestamp())); 817 } 818 819 @Override 820 public void setSQLite(PreparedStatement stmt, int i, Plot plot) 821 throws SQLException { 822 stmt.setNull(i * 6 + 1, 4); 823 stmt.setInt(i * 6 + 2, plot.getId().getX()); 824 stmt.setInt(i * 6 + 3, plot.getId().getY()); 825 try { 826 stmt.setString(i * 6 + 4, plot.getOwnerAbs().toString()); 827 } catch (SQLException ignored) { 828 stmt.setString(i * 6 + 4, everyone.toString()); 829 } 830 stmt.setString(i * 6 + 5, plot.getArea().toString()); 831 stmt.setTimestamp(i * 6 + 6, new Timestamp(plot.getTimestamp())); 832 } 833 834 @Override 835 public void setSQL(PreparedStatement stmt, Plot plot) throws SQLException { 836 stmt.setInt(1, plot.getId().getX()); 837 stmt.setInt(2, plot.getId().getY()); 838 stmt.setString(3, plot.getOwnerAbs().toString()); 839 stmt.setString(4, plot.getArea().toString()); 840 stmt.setTimestamp(5, new Timestamp(plot.getTimestamp())); 841 842 } 843 }; 844 setBulk(myList, mod, whenDone); 845 } 846 847 public <T> void setBulk(List<T> objList, StmtMod<T> mod, Runnable whenDone) { 848 int size = objList.size(); 849 if (size == 0) { 850 if (whenDone != null) { 851 whenDone.run(); 852 } 853 return; 854 } 855 int packet; 856 if (this.mySQL) { 857 packet = Math.min(size, 5000); 858 } else { 859 packet = Math.min(size, 50); 860 } 861 int amount = size / packet; 862 try { 863 int count = 0; 864 PreparedStatement preparedStmt = null; 865 int last = -1; 866 for (int j = 0; j <= amount; j++) { 867 List<T> subList = objList.subList(j * packet, Math.min(size, (j + 1) * packet)); 868 if (subList.isEmpty()) { 869 break; 870 } 871 String statement; 872 if (last == -1) { 873 last = subList.size(); 874 statement = mod.getCreateMySQL(subList.size()); 875 preparedStmt = this.connection.prepareStatement(statement); 876 } 877 if (subList.size() != last || count % 5000 == 0 && count > 0) { 878 preparedStmt.executeBatch(); 879 preparedStmt.close(); 880 statement = mod.getCreateMySQL(subList.size()); 881 preparedStmt = this.connection.prepareStatement(statement); 882 } 883 for (int i = 0; i < subList.size(); i++) { 884 count++; 885 T obj = subList.get(i); 886 mod.setMySQL(preparedStmt, i, obj); 887 } 888 last = subList.size(); 889 preparedStmt.addBatch(); 890 } 891 preparedStmt.executeBatch(); 892 preparedStmt.clearParameters(); 893 preparedStmt.close(); 894 if (whenDone != null) { 895 whenDone.run(); 896 } 897 return; 898 } catch (SQLException e) { 899 if (this.mySQL) { 900 LOGGER.error("1: | {}", objList.get(0).getClass().getCanonicalName()); 901 e.printStackTrace(); 902 } 903 } 904 try { 905 int count = 0; 906 PreparedStatement preparedStmt = null; 907 int last = -1; 908 for (int j = 0; j <= amount; j++) { 909 List<T> subList = objList.subList(j * packet, Math.min(size, (j + 1) * packet)); 910 if (subList.isEmpty()) { 911 break; 912 } 913 String statement; 914 if (last == -1) { 915 last = subList.size(); 916 statement = mod.getCreateSQLite(subList.size()); 917 preparedStmt = this.connection.prepareStatement(statement); 918 } 919 if (subList.size() != last || count % 5000 == 0 && count > 0) { 920 preparedStmt.executeBatch(); 921 preparedStmt.clearParameters(); 922 statement = mod.getCreateSQLite(subList.size()); 923 preparedStmt = this.connection.prepareStatement(statement); 924 } 925 for (int i = 0; i < subList.size(); i++) { 926 count++; 927 T obj = subList.get(i); 928 mod.setSQLite(preparedStmt, i, obj); 929 } 930 last = subList.size(); 931 preparedStmt.addBatch(); 932 } 933 preparedStmt.executeBatch(); 934 preparedStmt.clearParameters(); 935 preparedStmt.close(); 936 } catch (SQLException e) { 937 e.printStackTrace(); 938 LOGGER.error("2: | {}", objList.get(0).getClass().getCanonicalName()); 939 LOGGER.error("Could not bulk save!"); 940 try (PreparedStatement preparedStmt = this.connection 941 .prepareStatement(mod.getCreateSQL())) { 942 for (T obj : objList) { 943 mod.setSQL(preparedStmt, obj); 944 preparedStmt.addBatch(); 945 } 946 preparedStmt.executeBatch(); 947 } catch (SQLException e3) { 948 LOGGER.error("Failed to save all", e); 949 e3.printStackTrace(); 950 } 951 } 952 if (whenDone != null) { 953 whenDone.run(); 954 } 955 } 956 957 public void createSettings(final ArrayList<LegacySettings> myList, final Runnable whenDone) { 958 try (final PreparedStatement preparedStatement = this.connection.prepareStatement( 959 "INSERT INTO `" + SQLManager.this.prefix + "plot_settings`" 960 + "(`plot_plot_id`,`biome`,`rain`,`custom_time`,`time`,`deny_entry`,`alias`,`merged`,`position`) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)")) { 961 962 int packet; 963 if (this.mySQL) { 964 packet = Math.min(myList.size(), 5000); 965 } else { 966 packet = Math.min(myList.size(), 50); 967 } 968 969 int totalUpdated = 0; 970 int updated = 0; 971 972 for (final LegacySettings legacySettings : myList) { 973 preparedStatement.setInt(1, legacySettings.id); 974 preparedStatement.setNull(2, 4); 975 preparedStatement.setNull(3, 4); 976 preparedStatement.setNull(4, 4); 977 preparedStatement.setNull(5, 4); 978 preparedStatement.setNull(6, 4); 979 if (legacySettings.settings.getAlias().isEmpty()) { 980 preparedStatement.setNull(7, 4); 981 } else { 982 preparedStatement.setString(7, legacySettings.settings.getAlias()); 983 } 984 boolean[] merged = legacySettings.settings.getMerged(); 985 int hash = HashUtil.hash(merged); 986 preparedStatement.setInt(8, hash); 987 BlockLoc loc = legacySettings.settings.getPosition(); 988 String position; 989 if (loc.getY() == 0) { 990 position = "DEFAULT"; 991 } else { 992 position = loc.getX() + "," + loc.getY() + ',' + loc.getZ(); 993 } 994 preparedStatement.setString(9, position); 995 preparedStatement.addBatch(); 996 if (++updated >= packet) { 997 try { 998 preparedStatement.executeBatch(); 999 } catch (final Exception e) { 1000 LOGGER.error("Failed to store settings for plot with entry ID: {}", legacySettings.id); 1001 e.printStackTrace(); 1002 continue; 1003 } 1004 } 1005 totalUpdated += 1; 1006 } 1007 1008 if (totalUpdated < myList.size()) { 1009 try { 1010 preparedStatement.executeBatch(); 1011 } catch (final Exception e) { 1012 LOGGER.error("Failed to store settings", e); 1013 } 1014 } 1015 } catch (final Exception e) { 1016 LOGGER.error("Failed to store settings", e); 1017 } 1018 LOGGER.info("Finished converting settings ({} plots processed)", myList.size()); 1019 whenDone.run(); 1020 } 1021 1022 public void createEmptySettings(final ArrayList<Integer> myList, final Runnable whenDone) { 1023 final StmtMod<Integer> mod = new StmtMod<>() { 1024 @Override 1025 public String getCreateMySQL(int size) { 1026 return getCreateMySQL(size, SQLManager.this.CREATE_SETTINGS, 1); 1027 } 1028 1029 @Override 1030 public String getCreateSQLite(int size) { 1031 return getCreateSQLite(size, "INSERT INTO `" + SQLManager.this.prefix 1032 + "plot_settings` SELECT ? AS `plot_plot_id`, ? AS `biome`, ? AS `rain`, ? AS `custom_time`, ? AS `time`, ? AS " 1033 + "`deny_entry`, ? AS `alias`, ? AS `merged`, ? AS `position` ", 10); 1034 } 1035 1036 @Override 1037 public String getCreateSQL() { 1038 return "INSERT INTO `" + SQLManager.this.prefix 1039 + "plot_settings`(`plot_plot_id`) VALUES(?)"; 1040 } 1041 1042 @Override 1043 public void setMySQL(PreparedStatement stmt, int i, Integer id) 1044 throws SQLException { 1045 stmt.setInt(i + 1, id); 1046 } 1047 1048 @Override 1049 public void setSQLite(PreparedStatement stmt, int i, Integer id) 1050 throws SQLException { 1051 stmt.setInt(i * 10 + 1, id); 1052 stmt.setNull(i * 10 + 2, 4); 1053 stmt.setNull(i * 10 + 3, 4); 1054 stmt.setNull(i * 10 + 4, 4); 1055 stmt.setNull(i * 10 + 5, 4); 1056 stmt.setNull(i * 10 + 6, 4); 1057 stmt.setNull(i * 10 + 7, 4); 1058 stmt.setNull(i * 10 + 8, 4); 1059 stmt.setString(i * 10 + 9, "DEFAULT"); 1060 } 1061 1062 @Override 1063 public void setSQL(PreparedStatement stmt, Integer id) throws SQLException { 1064 stmt.setInt(1, id); 1065 } 1066 }; 1067 addGlobalTask(() -> setBulk(myList, mod, whenDone)); 1068 } 1069 1070 public void createPlotSafe(final Plot plot, final Runnable success, final Runnable failure) { 1071 addPlotTask(plot, new UniqueStatement("createPlotSafe_" + plot.hashCode()) { 1072 @Override 1073 public void set(PreparedStatement statement) throws SQLException { 1074 statement.setInt(1, plot.getId().getX()); 1075 statement.setInt(2, plot.getId().getY()); 1076 statement.setString(3, plot.getOwnerAbs().toString()); 1077 statement.setString(4, plot.getArea().toString()); 1078 statement.setTimestamp(5, new Timestamp(plot.getTimestamp())); 1079 statement.setString(6, plot.getArea().toString()); 1080 statement.setInt(7, plot.getId().getX()); 1081 statement.setInt(8, plot.getId().getY()); 1082 } 1083 1084 @Override 1085 public PreparedStatement get() throws SQLException { 1086 return SQLManager.this.connection.prepareStatement( 1087 SQLManager.this.CREATE_PLOT_SAFE, 1088 Statement.RETURN_GENERATED_KEYS 1089 ); 1090 } 1091 1092 @Override 1093 public void execute(PreparedStatement statement) { 1094 1095 } 1096 1097 @Override 1098 public void addBatch(PreparedStatement statement) throws SQLException { 1099 if (statement.execute() || statement.getUpdateCount() > 0) { 1100 try (ResultSet keys = supportsGetGeneratedKeys ? statement.getGeneratedKeys() : statement.getResultSet()) { 1101 if (keys.next()) { 1102 plot.temp = keys.getInt(1); 1103 addPlotTask(plot, new UniqueStatement( 1104 "createPlotAndSettings_settings_" + plot.hashCode()) { 1105 @Override 1106 public void set(PreparedStatement statement) 1107 throws SQLException { 1108 statement.setInt(1, getId(plot)); 1109 } 1110 1111 @Override 1112 public PreparedStatement get() throws SQLException { 1113 return SQLManager.this.connection.prepareStatement( 1114 "INSERT INTO `" + SQLManager.this.prefix 1115 + "plot_settings`(`plot_plot_id`) VALUES(?)"); 1116 } 1117 }); 1118 if (success != null) { 1119 addNotifyTask(success); 1120 } 1121 return; 1122 } 1123 } 1124 } 1125 if (failure != null) { 1126 failure.run(); 1127 } 1128 } 1129 }); 1130 } 1131 1132 public void commit() { 1133 if (this.closed) { 1134 return; 1135 } 1136 try { 1137 if (!this.connection.getAutoCommit()) { 1138 this.connection.commit(); 1139 this.connection.setAutoCommit(true); 1140 } 1141 } catch (SQLException e) { 1142 e.printStackTrace(); 1143 } 1144 } 1145 1146 @Override 1147 public void createPlotAndSettings(final Plot plot, Runnable whenDone) { 1148 addPlotTask(plot, new UniqueStatement("createPlotAndSettings_" + plot.hashCode()) { 1149 @Override 1150 public void set(PreparedStatement statement) throws SQLException { 1151 statement.setInt(1, plot.getId().getX()); 1152 statement.setInt(2, plot.getId().getY()); 1153 statement.setString(3, plot.getOwnerAbs().toString()); 1154 statement.setString(4, plot.getArea().toString()); 1155 statement.setTimestamp(5, new Timestamp(plot.getTimestamp())); 1156 } 1157 1158 @Override 1159 public PreparedStatement get() throws SQLException { 1160 return SQLManager.this.connection 1161 .prepareStatement(SQLManager.this.CREATE_PLOT, Statement.RETURN_GENERATED_KEYS); 1162 } 1163 1164 @Override 1165 public void execute(PreparedStatement statement) { 1166 } 1167 1168 @Override 1169 public void addBatch(PreparedStatement statement) throws SQLException { 1170 statement.execute(); 1171 try (ResultSet keys = supportsGetGeneratedKeys ? statement.getGeneratedKeys() : statement.getResultSet()) { 1172 if (keys.next()) { 1173 plot.temp = keys.getInt(1); 1174 } 1175 } 1176 } 1177 }); 1178 addPlotTask(plot, new UniqueStatement("createPlotAndSettings_settings_" + plot.hashCode()) { 1179 @Override 1180 public void set(PreparedStatement statement) throws SQLException { 1181 statement.setInt(1, getId(plot)); 1182 } 1183 1184 @Override 1185 public PreparedStatement get() throws SQLException { 1186 return SQLManager.this.connection.prepareStatement( 1187 "INSERT INTO `" + SQLManager.this.prefix 1188 + "plot_settings`(`plot_plot_id`) VALUES(?)"); 1189 } 1190 }); 1191 addNotifyTask(whenDone); 1192 } 1193 1194 /** 1195 * Create tables. 1196 * 1197 * @throws SQLException 1198 */ 1199 @Override 1200 public void createTables() throws SQLException { 1201 String[] tables = 1202 new String[]{"plot", "plot_denied", "plot_helpers", "plot_comments", "plot_trusted", 1203 "plot_rating", "plot_settings", "cluster", "player_meta", "plot_flags"}; 1204 DatabaseMetaData meta = this.connection.getMetaData(); 1205 int create = 0; 1206 for (String s : tables) { 1207 ResultSet set = meta.getTables(null, null, this.prefix + s, new String[]{"TABLE"}); 1208 // ResultSet set = meta.getTables(null, null, prefix + s, null); 1209 if (!set.next()) { 1210 create++; 1211 } 1212 set.close(); 1213 } 1214 if (create == 0) { 1215 return; 1216 } 1217 boolean addConstraint = create == tables.length; 1218 try (Statement stmt = this.connection.createStatement()) { 1219 if (this.mySQL) { 1220 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot` (" 1221 + "`id` INT(11) NOT NULL AUTO_INCREMENT," + "`plot_id_x` INT(11) NOT NULL," 1222 + "`plot_id_z` INT(11) NOT NULL," + "`owner` VARCHAR(40) NOT NULL," 1223 + "`world` VARCHAR(45) NOT NULL," 1224 + "`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," 1225 + "PRIMARY KEY (`id`)" 1226 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=0"); 1227 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix 1228 + "plot_denied` (`plot_plot_id` INT(11) NOT NULL," 1229 + "`user_uuid` VARCHAR(40) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1230 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_helpers` (" 1231 + "`plot_plot_id` INT(11) NOT NULL," + "`user_uuid` VARCHAR(40) NOT NULL" 1232 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1233 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_comments` (" 1234 + "`world` VARCHAR(40) NOT NULL, `hashcode` INT(11) NOT NULL," 1235 + "`comment` VARCHAR(40) NOT NULL," + "`inbox` VARCHAR(40) NOT NULL," 1236 + "`timestamp` INT(11) NOT NULL," + "`sender` VARCHAR(40) NOT NULL" 1237 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1238 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_trusted` (" 1239 + "`plot_plot_id` INT(11) NOT NULL," + "`user_uuid` VARCHAR(40) NOT NULL" 1240 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1241 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_settings` (" 1242 + " `plot_plot_id` INT(11) NOT NULL," 1243 + " `biome` VARCHAR(45) DEFAULT 'FOREST'," + " `rain` INT(1) DEFAULT 0," 1244 + " `custom_time` TINYINT(1) DEFAULT '0'," + " `time` INT(11) DEFAULT '8000'," 1245 + " `deny_entry` TINYINT(1) DEFAULT '0'," 1246 + " `alias` VARCHAR(50) DEFAULT NULL," + " `merged` INT(11) DEFAULT NULL," 1247 + " `position` VARCHAR(50) NOT NULL DEFAULT 'DEFAULT'," 1248 + " PRIMARY KEY (`plot_plot_id`)" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1249 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix 1250 + "plot_rating` ( `plot_plot_id` INT(11) NOT NULL, `rating` INT(2) NOT NULL, `player` VARCHAR(40) NOT NULL) ENGINE=InnoDB " 1251 + "DEFAULT CHARSET=utf8"); 1252 if (addConstraint) { 1253 stmt.addBatch("ALTER TABLE `" + this.prefix + "plot_settings` ADD CONSTRAINT `" 1254 + this.prefix 1255 + "plot_settings_ibfk_1` FOREIGN KEY (`plot_plot_id`) REFERENCES `" 1256 + this.prefix + "plot` (`id`) ON DELETE CASCADE"); 1257 } 1258 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "cluster` (" 1259 + "`id` INT(11) NOT NULL AUTO_INCREMENT," + "`pos1_x` INT(11) NOT NULL," 1260 + "`pos1_z` INT(11) NOT NULL," + "`pos2_x` INT(11) NOT NULL," 1261 + "`pos2_z` INT(11) NOT NULL," + "`owner` VARCHAR(40) NOT NULL," 1262 + "`world` VARCHAR(45) NOT NULL," 1263 + "`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," 1264 + "PRIMARY KEY (`id`)" 1265 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=0"); 1266 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "cluster_helpers` (" 1267 + "`cluster_id` INT(11) NOT NULL," + "`user_uuid` VARCHAR(40) NOT NULL" 1268 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1269 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "cluster_invited` (" 1270 + "`cluster_id` INT(11) NOT NULL," + "`user_uuid` VARCHAR(40) NOT NULL" 1271 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1272 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "cluster_settings` (" 1273 + " `cluster_id` INT(11) NOT NULL," + " `biome` VARCHAR(45) DEFAULT 'FOREST'," 1274 + " `rain` INT(1) DEFAULT 0," + " `custom_time` TINYINT(1) DEFAULT '0'," 1275 + " `time` INT(11) DEFAULT '8000'," + " `deny_entry` TINYINT(1) DEFAULT '0'," 1276 + " `alias` VARCHAR(50) DEFAULT NULL," + " `merged` INT(11) DEFAULT NULL," 1277 + " `position` VARCHAR(50) NOT NULL DEFAULT 'DEFAULT'," 1278 + " PRIMARY KEY (`cluster_id`)" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1279 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "player_meta` (" 1280 + " `meta_id` INT(11) NOT NULL AUTO_INCREMENT," 1281 + " `uuid` VARCHAR(40) NOT NULL," + " `key` VARCHAR(32) NOT NULL," 1282 + " `value` blob NOT NULL," + " PRIMARY KEY (`meta_id`)" 1283 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1284 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_flags`(" 1285 + "`id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY," 1286 + "`plot_id` INT(11) NOT NULL," + " `flag` VARCHAR(64)," 1287 + " `value` VARCHAR(512)," + "FOREIGN KEY (plot_id) REFERENCES `" + this.prefix 1288 + "plot` (id) ON DELETE CASCADE, " + "UNIQUE (plot_id, flag)" 1289 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1290 } else { 1291 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot` (" 1292 + "`id` INTEGER PRIMARY KEY AUTOINCREMENT," + "`plot_id_x` INT(11) NOT NULL," 1293 + "`plot_id_z` INT(11) NOT NULL," + "`owner` VARCHAR(45) NOT NULL," 1294 + "`world` VARCHAR(45) NOT NULL," 1295 + "`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP)"); 1296 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix 1297 + "plot_denied` (`plot_plot_id` INT(11) NOT NULL," 1298 + "`user_uuid` VARCHAR(40) NOT NULL)"); 1299 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix 1300 + "plot_helpers` (`plot_plot_id` INT(11) NOT NULL," 1301 + "`user_uuid` VARCHAR(40) NOT NULL)"); 1302 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix 1303 + "plot_trusted` (`plot_plot_id` INT(11) NOT NULL," 1304 + "`user_uuid` VARCHAR(40) NOT NULL)"); 1305 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_comments` (" 1306 + "`world` VARCHAR(40) NOT NULL, `hashcode` INT(11) NOT NULL," 1307 + "`comment` VARCHAR(40) NOT NULL," 1308 + "`inbox` VARCHAR(40) NOT NULL, `timestamp` INT(11) NOT NULL," 1309 + "`sender` VARCHAR(40) NOT NULL" + ')'); 1310 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_settings` (" 1311 + " `plot_plot_id` INT(11) NOT NULL," 1312 + " `biome` VARCHAR(45) DEFAULT 'FOREST'," + " `rain` INT(1) DEFAULT 0," 1313 + " `custom_time` TINYINT(1) DEFAULT '0'," + " `time` INT(11) DEFAULT '8000'," 1314 + " `deny_entry` TINYINT(1) DEFAULT '0'," 1315 + " `alias` VARCHAR(50) DEFAULT NULL," + " `merged` INT(11) DEFAULT NULL," 1316 + " `position` VARCHAR(50) NOT NULL DEFAULT 'DEFAULT'," 1317 + " PRIMARY KEY (`plot_plot_id`)" + ')'); 1318 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix 1319 + "plot_rating` (`plot_plot_id` INT(11) NOT NULL, `rating` INT(2) NOT NULL, `player` VARCHAR(40) NOT NULL)"); 1320 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "cluster` (" 1321 + "`id` INTEGER PRIMARY KEY AUTOINCREMENT," + "`pos1_x` INT(11) NOT NULL," 1322 + "`pos1_z` INT(11) NOT NULL," + "`pos2_x` INT(11) NOT NULL," 1323 + "`pos2_z` INT(11) NOT NULL," + "`owner` VARCHAR(40) NOT NULL," 1324 + "`world` VARCHAR(45) NOT NULL," 1325 + "`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP" + ')'); 1326 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix 1327 + "cluster_helpers` (`cluster_id` INT(11) NOT NULL," 1328 + "`user_uuid` VARCHAR(40) NOT NULL)"); 1329 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix 1330 + "cluster_invited` (`cluster_id` INT(11) NOT NULL," 1331 + "`user_uuid` VARCHAR(40) NOT NULL)"); 1332 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "cluster_settings` (" 1333 + " `cluster_id` INT(11) NOT NULL," + " `biome` VARCHAR(45) DEFAULT 'FOREST'," 1334 + " `rain` INT(1) DEFAULT 0," + " `custom_time` TINYINT(1) DEFAULT '0'," 1335 + " `time` INT(11) DEFAULT '8000'," + " `deny_entry` TINYINT(1) DEFAULT '0'," 1336 + " `alias` VARCHAR(50) DEFAULT NULL," + " `merged` INT(11) DEFAULT NULL," 1337 + " `position` VARCHAR(50) NOT NULL DEFAULT 'DEFAULT'," 1338 + " PRIMARY KEY (`cluster_id`)" + ')'); 1339 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "player_meta` (" 1340 + " `meta_id` INTEGER PRIMARY KEY AUTOINCREMENT," 1341 + " `uuid` VARCHAR(40) NOT NULL," + " `key` VARCHAR(32) NOT NULL," 1342 + " `value` blob NOT NULL" + ')'); 1343 stmt.addBatch("CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_flags`(" 1344 + "`id` INTEGER PRIMARY KEY AUTOINCREMENT," + "`plot_id` INTEGER NOT NULL," 1345 + " `flag` VARCHAR(64)," + " `value` VARCHAR(512)," 1346 + "FOREIGN KEY (plot_id) REFERENCES `" + this.prefix 1347 + "plot` (id) ON DELETE CASCADE, " + "UNIQUE (plot_id, flag))"); 1348 } 1349 stmt.executeBatch(); 1350 stmt.clearBatch(); 1351 } 1352 } 1353 1354 @Override 1355 public void deleteSettings(final Plot plot) { 1356 addPlotTask(plot, new UniqueStatement("delete_plot_settings") { 1357 @Override 1358 public void set(PreparedStatement statement) throws SQLException { 1359 statement.setInt(1, getId(plot)); 1360 } 1361 1362 @Override 1363 public PreparedStatement get() throws SQLException { 1364 return SQLManager.this.connection.prepareStatement( 1365 "DELETE FROM `" + SQLManager.this.prefix 1366 + "plot_settings` WHERE `plot_plot_id` = ?"); 1367 } 1368 }); 1369 } 1370 1371 @Override 1372 public void deleteHelpers(final Plot plot) { 1373 if (plot.getTrusted().isEmpty()) { 1374 return; 1375 } 1376 addPlotTask(plot, new UniqueStatement("delete_plot_helpers") { 1377 @Override 1378 public void set(PreparedStatement statement) throws SQLException { 1379 statement.setInt(1, getId(plot)); 1380 } 1381 1382 @Override 1383 public PreparedStatement get() throws SQLException { 1384 return SQLManager.this.connection.prepareStatement( 1385 "DELETE FROM `" + SQLManager.this.prefix 1386 + "plot_helpers` WHERE `plot_plot_id` = ?"); 1387 } 1388 }); 1389 } 1390 1391 @Override 1392 public void deleteTrusted(final Plot plot) { 1393 if (plot.getMembers().isEmpty()) { 1394 return; 1395 } 1396 addPlotTask(plot, new UniqueStatement("delete_plot_trusted") { 1397 @Override 1398 public void set(PreparedStatement statement) throws SQLException { 1399 statement.setInt(1, getId(plot)); 1400 } 1401 1402 @Override 1403 public PreparedStatement get() throws SQLException { 1404 return SQLManager.this.connection.prepareStatement( 1405 "DELETE FROM `" + SQLManager.this.prefix 1406 + "plot_trusted` WHERE `plot_plot_id` = ?"); 1407 } 1408 }); 1409 } 1410 1411 @Override 1412 public void deleteDenied(final Plot plot) { 1413 if (plot.getDenied().isEmpty()) { 1414 return; 1415 } 1416 addPlotTask(plot, new UniqueStatement("delete_plot_denied") { 1417 @Override 1418 public void set(PreparedStatement statement) throws SQLException { 1419 statement.setInt(1, getId(plot)); 1420 } 1421 1422 @Override 1423 public PreparedStatement get() throws SQLException { 1424 return SQLManager.this.connection.prepareStatement( 1425 "DELETE FROM `" + SQLManager.this.prefix 1426 + "plot_denied` WHERE `plot_plot_id` = ?"); 1427 } 1428 }); 1429 } 1430 1431 @Override 1432 public void deleteComments(final Plot plot) { 1433 addPlotTask(plot, new UniqueStatement("delete_plot_comments") { 1434 @Override 1435 public void set(PreparedStatement statement) throws SQLException { 1436 statement.setString(1, plot.getArea().toString()); 1437 statement.setInt(2, plot.hashCode()); 1438 } 1439 1440 @Override 1441 public PreparedStatement get() throws SQLException { 1442 return SQLManager.this.connection.prepareStatement( 1443 "DELETE FROM `" + SQLManager.this.prefix 1444 + "plot_comments` WHERE `world` = ? AND `hashcode` = ?"); 1445 } 1446 }); 1447 } 1448 1449 @Override 1450 public void deleteRatings(final Plot plot) { 1451 if (Settings.Enabled_Components.RATING_CACHE && plot.getSettings().getRatings().isEmpty()) { 1452 return; 1453 } 1454 addPlotTask(plot, new UniqueStatement("delete_plot_ratings") { 1455 @Override 1456 public void set(PreparedStatement statement) throws SQLException { 1457 statement.setInt(1, getId(plot)); 1458 } 1459 1460 @Override 1461 public PreparedStatement get() throws SQLException { 1462 return SQLManager.this.connection.prepareStatement( 1463 "DELETE FROM `" + SQLManager.this.prefix 1464 + "plot_rating` WHERE `plot_plot_id` = ?"); 1465 } 1466 }); 1467 } 1468 1469 /** 1470 * Delete a plot. 1471 * 1472 * @param plot 1473 */ 1474 @Override 1475 public void delete(final Plot plot) { 1476 deleteSettings(plot); 1477 deleteDenied(plot); 1478 deleteHelpers(plot); 1479 deleteTrusted(plot); 1480 deleteComments(plot); 1481 deleteRatings(plot); 1482 addPlotTask(plot, new UniqueStatement("delete_plot") { 1483 @Override 1484 public void set(PreparedStatement statement) throws SQLException { 1485 statement.setInt(1, getId(plot)); 1486 } 1487 1488 @Override 1489 public PreparedStatement get() throws SQLException { 1490 return SQLManager.this.connection.prepareStatement( 1491 "DELETE FROM `" + SQLManager.this.prefix + "plot` WHERE `id` = ?"); 1492 } 1493 }); 1494 } 1495 1496 /** 1497 * Creates plot settings 1498 * 1499 * @param id 1500 * @param plot 1501 */ 1502 @Override 1503 public void createPlotSettings(final int id, Plot plot) { 1504 addPlotTask(plot, new UniqueStatement("createPlotSettings") { 1505 @Override 1506 public void set(PreparedStatement statement) throws SQLException { 1507 statement.setInt(1, id); 1508 } 1509 1510 @Override 1511 public PreparedStatement get() throws SQLException { 1512 return SQLManager.this.connection.prepareStatement( 1513 "INSERT INTO `" + SQLManager.this.prefix 1514 + "plot_settings`(`plot_plot_id`) VALUES(?)"); 1515 } 1516 }); 1517 } 1518 1519 @Override 1520 public int getClusterId(PlotCluster cluster) { 1521 if (cluster.temp > 0) { 1522 return cluster.temp; 1523 } 1524 try { 1525 commit(); 1526 if (cluster.temp > 0) { 1527 return cluster.temp; 1528 } 1529 int c_id; 1530 try (PreparedStatement stmt = this.connection.prepareStatement( 1531 "SELECT `id` FROM `" + this.prefix 1532 + "cluster` WHERE `pos1_x` = ? AND `pos1_z` = ? AND `pos2_x` = ? AND `pos2_z` = ? AND `world` = ? ORDER BY `timestamp` ASC")) { 1533 stmt.setInt(1, cluster.getP1().getX()); 1534 stmt.setInt(2, cluster.getP1().getY()); 1535 stmt.setInt(3, cluster.getP2().getX()); 1536 stmt.setInt(4, cluster.getP2().getY()); 1537 stmt.setString(5, cluster.area.toString()); 1538 try (ResultSet resultSet = stmt.executeQuery()) { 1539 c_id = Integer.MAX_VALUE; 1540 while (resultSet.next()) { 1541 c_id = resultSet.getInt("id"); 1542 } 1543 } 1544 } 1545 if (c_id == Integer.MAX_VALUE || c_id == 0) { 1546 if (cluster.temp > 0) { 1547 return cluster.temp; 1548 } 1549 throw new SQLException("Cluster does not exist in database"); 1550 } 1551 cluster.temp = c_id; 1552 return c_id; 1553 } catch (SQLException e) { 1554 e.printStackTrace(); 1555 } 1556 return Integer.MAX_VALUE; 1557 } 1558 1559 @Override 1560 public int getId(Plot plot) { 1561 if (plot.temp > 0) { 1562 return plot.temp; 1563 } 1564 try { 1565 commit(); 1566 if (plot.temp > 0) { 1567 return plot.temp; 1568 } 1569 int id; 1570 try (PreparedStatement statement = this.connection.prepareStatement( 1571 "SELECT `id` FROM `" + this.prefix 1572 + "plot` WHERE `plot_id_x` = ? AND `plot_id_z` = ? AND world = ? ORDER BY `timestamp` ASC")) { 1573 statement.setInt(1, plot.getId().getX()); 1574 statement.setInt(2, plot.getId().getY()); 1575 statement.setString(3, plot.getArea().toString()); 1576 try (ResultSet resultSet = statement.executeQuery()) { 1577 id = Integer.MAX_VALUE; 1578 while (resultSet.next()) { 1579 id = resultSet.getInt("id"); 1580 } 1581 } 1582 } 1583 if (id == Integer.MAX_VALUE || id == 0) { 1584 if (plot.temp > 0) { 1585 return plot.temp; 1586 } 1587 throw new SQLException("Plot does not exist in database"); 1588 } 1589 plot.temp = id; 1590 return id; 1591 } catch (SQLException e) { 1592 e.printStackTrace(); 1593 } 1594 return Integer.MAX_VALUE; 1595 } 1596 1597 @Override 1598 public void updateTables(int[] oldVersion) { 1599 try { 1600 if (this.mySQL && !PlotSquared.get().checkVersion(oldVersion, 3, 3, 2)) { 1601 try (Statement stmt = this.connection.createStatement()) { 1602 stmt.executeUpdate( 1603 "ALTER TABLE `" + this.prefix + "plots` DROP INDEX `unique_alias`"); 1604 } catch (SQLException ignored) { 1605 } 1606 } 1607 DatabaseMetaData data = this.connection.getMetaData(); 1608 ResultSet rs = 1609 data.getColumns(null, null, this.prefix + "plot_comments", "plot_plot_id"); 1610 if (rs.next()) { 1611 rs.close(); 1612 rs = data.getColumns(null, null, this.prefix + "plot_comments", "hashcode"); 1613 if (!rs.next()) { 1614 rs.close(); 1615 try (Statement statement = this.connection.createStatement()) { 1616 statement.addBatch("DROP TABLE `" + this.prefix + "plot_comments`"); 1617 if (Storage.MySQL.USE) { 1618 statement.addBatch( 1619 "CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_comments` (" 1620 + "`world` VARCHAR(40) NOT NULL, `hashcode` INT(11) NOT NULL," 1621 + "`comment` VARCHAR(40) NOT NULL," 1622 + "`inbox` VARCHAR(40) NOT NULL," 1623 + "`timestamp` INT(11) NOT NULL," 1624 + "`sender` VARCHAR(40) NOT NULL" 1625 + ") ENGINE=InnoDB DEFAULT CHARSET=utf8"); 1626 } else { 1627 statement.addBatch( 1628 "CREATE TABLE IF NOT EXISTS `" + this.prefix + "plot_comments` (" 1629 + "`world` VARCHAR(40) NOT NULL, `hashcode` INT(11) NOT NULL," 1630 + "`comment` VARCHAR(40) NOT NULL," 1631 + "`inbox` VARCHAR(40) NOT NULL, `timestamp` INT(11) NOT NULL," 1632 + "`sender` VARCHAR(40) NOT NULL" + ')'); 1633 } 1634 statement.executeBatch(); 1635 } catch (SQLException ignored) { 1636 try (Statement statement = this.connection.createStatement()) { 1637 statement.addBatch("ALTER IGNORE TABLE `" + this.prefix 1638 + "plot_comments` ADD `inbox` VARCHAR(11) DEFAULT `public`"); 1639 statement.addBatch("ALTER IGNORE TABLE `" + this.prefix 1640 + "plot_comments` ADD `timestamp` INT(11) DEFAULT 0"); 1641 statement.addBatch("ALTER TABLE `" + this.prefix + "plot` DROP `tier`"); 1642 statement.executeBatch(); 1643 } 1644 } 1645 } 1646 } 1647 rs.close(); 1648 rs = data.getColumns(null, null, this.prefix + "plot_denied", "plot_plot_id"); 1649 if (rs.next()) { 1650 try (Statement statement = this.connection.createStatement()) { 1651 statement.executeUpdate("DELETE FROM `" + this.prefix 1652 + "plot_denied` WHERE `plot_plot_id` NOT IN (SELECT `id` FROM `" 1653 + this.prefix + "plot`)"); 1654 } catch (SQLException e) { 1655 e.printStackTrace(); 1656 } 1657 1658 rs.close(); 1659 try (Statement statement = this.connection.createStatement()) { 1660 for (String table : new String[]{"plot_denied", "plot_helpers", 1661 "plot_trusted"}) { 1662 ResultSet result = statement.executeQuery( 1663 "SELECT plot_plot_id, user_uuid, COUNT(*) FROM " + this.prefix + table 1664 + " GROUP BY plot_plot_id, user_uuid HAVING COUNT(*) > 1"); 1665 if (result.next()) { 1666 result.close(); 1667 statement.executeUpdate( 1668 "CREATE TABLE " + this.prefix + table + "_tmp AS SELECT * FROM " 1669 + this.prefix + table + " GROUP BY plot_plot_id, user_uuid"); 1670 statement.executeUpdate("DROP TABLE " + this.prefix + table); 1671 statement.executeUpdate( 1672 "CREATE TABLE " + this.prefix + table + " AS SELECT * FROM " 1673 + this.prefix + table + "_tmp"); 1674 statement.executeUpdate("DROP TABLE " + this.prefix + table + "_tmp"); 1675 } 1676 } 1677 } catch (SQLException e2) { 1678 e2.printStackTrace(); 1679 } 1680 } 1681 } catch (SQLException e) { 1682 e.printStackTrace(); 1683 } 1684 1685 } 1686 1687 public void deleteRows(ArrayList<Integer> rowIds, final String table, final String column) { 1688 setBulk(rowIds, new StmtMod<>() { 1689 1690 @Override 1691 public String getCreateMySQL(int size) { 1692 return getCreateMySQL(1, "DELETE FROM `" + table + "` WHERE `" + column + "` IN ", 1693 size 1694 ); 1695 } 1696 1697 @Override 1698 public String getCreateSQLite(int size) { 1699 return getCreateMySQL(1, "DELETE FROM `" + table + "` WHERE `" + column + "` IN ", 1700 size 1701 ); 1702 } 1703 1704 @Override 1705 public String getCreateSQL() { 1706 return "DELETE FROM `" + table + "` WHERE `" + column + "` = ?"; 1707 } 1708 1709 @Override 1710 public void setMySQL(PreparedStatement stmt, int i, Integer obj) 1711 throws SQLException { 1712 stmt.setInt(i + 1, obj); 1713 } 1714 1715 @Override 1716 public void setSQLite(PreparedStatement stmt, int i, Integer obj) 1717 throws SQLException { 1718 stmt.setInt(i + 1, obj); 1719 } 1720 1721 @Override 1722 public void setSQL(PreparedStatement stmt, Integer obj) throws SQLException { 1723 stmt.setInt(1, obj); 1724 } 1725 }, null); 1726 } 1727 1728 @Override 1729 public boolean convertFlags() { 1730 final Map<Integer, Map<String, String>> flagMap = new HashMap<>(); 1731 try { 1732 // only migrate flags, if plot_settings table has flags column 1733 DatabaseMetaData metaData = this.connection.getMetaData(); 1734 try (ResultSet rs = metaData.getColumns(null, null, this.prefix + "plot_settings", "flags")) { 1735 if (!rs.next()) { 1736 return true; 1737 } 1738 } 1739 } catch (SQLException e) { 1740 LOGGER.error("Failed to query table metadata", e); 1741 return false; 1742 } 1743 try (Statement statement = this.connection.createStatement()) { 1744 try (ResultSet resultSet = statement 1745 .executeQuery("SELECT * FROM `" + this.prefix + "plot_settings`")) { 1746 while (resultSet.next()) { 1747 final int id = resultSet.getInt("plot_plot_id"); 1748 final String plotFlags = resultSet.getString("flags"); 1749 if (plotFlags == null || plotFlags.isEmpty()) { 1750 continue; 1751 } 1752 flagMap.put(id, new HashMap<>()); 1753 for (String element : plotFlags.split(",")) { 1754 if (element.contains(":")) { 1755 String[] split = element.split(":"); // splits flag:value 1756 try { 1757 String flag_str = split[1].replace("¯", ":").replace("´", ","); 1758 flagMap.get(id).put(split[0], flag_str); 1759 } catch (Exception e) { 1760 LOGGER.error("Failed to migrate flag value", e); 1761 } 1762 } 1763 } 1764 } 1765 } 1766 } catch (final Exception e) { 1767 LOGGER.error("Failed to load old flag values", e); 1768 return false; 1769 } 1770 LOGGER.info("Loaded {} plot flag collections...", flagMap.size()); 1771 LOGGER.info("Attempting to store these flags in the new table..."); 1772 try (final PreparedStatement preparedStatement = this.connection.prepareStatement( 1773 "INSERT INTO `" + this.prefix + "plot_flags`(`plot_id`, `flag`, `value`) VALUES(?, ?, ?)")) { 1774 1775 long timeStarted = System.currentTimeMillis(); 1776 int flagsProcessed = 0; 1777 int plotsProcessed = 0; 1778 1779 int totalFlags = 0; 1780 for (final Map<String, String> flags : flagMap.values()) { 1781 totalFlags += flags.size(); 1782 } 1783 1784 for (final Map.Entry<Integer, Map<String, String>> plotFlagEntry : flagMap.entrySet()) { 1785 for (final Map.Entry<String, String> flagEntry : plotFlagEntry.getValue() 1786 .entrySet()) { 1787 preparedStatement.setInt(1, plotFlagEntry.getKey()); 1788 preparedStatement.setString(2, flagEntry.getKey()); 1789 preparedStatement.setString(3, flagEntry.getValue()); 1790 preparedStatement.addBatch(); 1791 flagsProcessed += 1; 1792 } 1793 plotsProcessed += 1; 1794 1795 try { 1796 preparedStatement.executeBatch(); 1797 } catch (final Exception e) { 1798 LOGGER.error("Failed to store flag values for plot with entry ID: {}", plotFlagEntry.getKey(), e); 1799 continue; 1800 } 1801 1802 if (System.currentTimeMillis() - timeStarted >= 1000L || plotsProcessed >= flagMap.size()) { 1803 timeStarted = System.currentTimeMillis(); 1804 LOGGER.info( 1805 "... Flag conversion in progress. {}% done", 1806 String.format("%.1f", ((float) flagsProcessed / totalFlags) * 100) 1807 ); 1808 } 1809 LOGGER.info("- Finished converting flags for plot with entry ID: {}", plotFlagEntry.getKey()); 1810 } 1811 } catch (final Exception e) { 1812 LOGGER.error("Failed to store flag values", e); 1813 return false; 1814 } 1815 return true; 1816 } 1817 1818 /** 1819 * Load all plots, helpers, denied, trusted, and every setting from DB into a {@link HashMap}. 1820 */ 1821 @Override 1822 public HashMap<String, HashMap<PlotId, Plot>> getPlots() { 1823 HashMap<String, HashMap<PlotId, Plot>> newPlots = new HashMap<>(); 1824 HashMap<Integer, Plot> plots = new HashMap<>(); 1825 try { 1826 HashSet<String> areas = new HashSet<>(); 1827 if (this.worldConfiguration.contains("worlds")) { 1828 ConfigurationSection worldSection = this.worldConfiguration.getConfigurationSection("worlds"); 1829 if (worldSection != null) { 1830 for (String worldKey : worldSection.getKeys(false)) { 1831 areas.add(worldKey); 1832 ConfigurationSection areaSection = 1833 worldSection.getConfigurationSection(worldKey + ".areas"); 1834 if (areaSection != null) { 1835 for (String areaKey : areaSection.getKeys(false)) { 1836 String[] split = areaKey.split("(?<![;])-"); 1837 if (split.length == 3) { 1838 areas.add(worldKey + ';' + split[0]); 1839 } 1840 } 1841 } 1842 } 1843 } 1844 } 1845 HashMap<String, UUID> uuids = new HashMap<>(); 1846 HashMap<String, AtomicInteger> noExist = new HashMap<>(); 1847 1848 /* 1849 * Getting plots 1850 */ 1851 try (Statement statement = this.connection.createStatement()) { 1852 int id; 1853 String o; 1854 UUID user; 1855 try (ResultSet resultSet = statement.executeQuery( 1856 "SELECT `id`, `plot_id_x`, `plot_id_z`, `owner`, `world`, `timestamp` FROM `" 1857 + this.prefix + "plot`")) { 1858 ArrayList<Integer> toDelete = new ArrayList<>(); 1859 while (resultSet.next()) { 1860 PlotId plot_id = PlotId.of( 1861 resultSet.getInt("plot_id_x"), 1862 resultSet.getInt("plot_id_z") 1863 ); 1864 id = resultSet.getInt("id"); 1865 String areaID = resultSet.getString("world"); 1866 if (!areas.contains(areaID)) { 1867 if (Settings.Enabled_Components.DATABASE_PURGER) { 1868 toDelete.add(id); 1869 continue; 1870 } else { 1871 AtomicInteger value = noExist.get(areaID); 1872 if (value != null) { 1873 value.incrementAndGet(); 1874 } else { 1875 noExist.put(areaID, new AtomicInteger(1)); 1876 } 1877 } 1878 } 1879 o = resultSet.getString("owner"); 1880 user = uuids.get(o); 1881 if (user == null) { 1882 try { 1883 user = UUID.fromString(o); 1884 } catch (IllegalArgumentException e) { 1885 if (Settings.UUID.FORCE_LOWERCASE) { 1886 user = UUID.nameUUIDFromBytes( 1887 ("OfflinePlayer:" + o.toLowerCase()) 1888 .getBytes(Charsets.UTF_8)); 1889 } else { 1890 user = UUID.nameUUIDFromBytes( 1891 ("OfflinePlayer:" + o).getBytes(Charsets.UTF_8)); 1892 } 1893 } 1894 uuids.put(o, user); 1895 } 1896 long time; 1897 try { 1898 Timestamp timestamp = resultSet.getTimestamp("timestamp"); 1899 time = timestamp.getTime(); 1900 } catch (SQLException exception) { 1901 String parsable = resultSet.getString("timestamp"); 1902 try { 1903 time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(parsable) 1904 .getTime(); 1905 } catch (ParseException e) { 1906 LOGGER.error("Could not parse date for plot: #{}({};{}) ({})", 1907 id, areaID, plot_id, parsable 1908 ); 1909 time = System.currentTimeMillis() + id; 1910 } 1911 } 1912 Plot p = new Plot(plot_id, user, new HashSet<>(), new HashSet<>(), 1913 new HashSet<>(), "", null, null, null, 1914 new boolean[]{false, false, false, false}, time, id 1915 ); 1916 HashMap<PlotId, Plot> map = newPlots.get(areaID); 1917 if (map != null) { 1918 Plot last = map.put(p.getId(), p); 1919 if (last != null) { 1920 if (Settings.Enabled_Components.DATABASE_PURGER) { 1921 toDelete.add(last.temp); 1922 } else { 1923 LOGGER.info( 1924 "Plot #{}({}) in `{}plot` is a duplicate." 1925 + " Delete this plot or set `database-purger: true` in the settings.yml", 1926 id, 1927 last, 1928 this.prefix 1929 ); 1930 } 1931 } 1932 } else { 1933 map = new HashMap<>(); 1934 newPlots.put(areaID, map); 1935 map.put(p.getId(), p); 1936 } 1937 plots.put(id, p); 1938 } 1939 deleteRows(toDelete, this.prefix + "plot", "id"); 1940 } 1941 if (Settings.Enabled_Components.RATING_CACHE) { 1942 try (ResultSet r = statement.executeQuery( 1943 "SELECT `plot_plot_id`, `player`, `rating` FROM `" + this.prefix 1944 + "plot_rating`")) { 1945 ArrayList<Integer> toDelete = new ArrayList<>(); 1946 while (r.next()) { 1947 id = r.getInt("plot_plot_id"); 1948 o = r.getString("player"); 1949 user = uuids.get(o); 1950 if (user == null) { 1951 user = UUID.fromString(o); 1952 uuids.put(o, user); 1953 } 1954 Plot plot = plots.get(id); 1955 if (plot != null) { 1956 plot.getSettings().getRatings().put(user, r.getInt("rating")); 1957 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 1958 toDelete.add(id); 1959 } else { 1960 LOGGER.warn("Entry #{}({}) in `plot_rating` does not exist." 1961 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 1962 } 1963 } 1964 deleteRows(toDelete, this.prefix + "plot_rating", "plot_plot_id"); 1965 } 1966 } 1967 1968 /* 1969 * Getting helpers 1970 */ 1971 try (ResultSet r = statement.executeQuery( 1972 "SELECT `user_uuid`, `plot_plot_id` FROM `" + this.prefix + "plot_helpers`")) { 1973 ArrayList<Integer> toDelete = new ArrayList<>(); 1974 while (r.next()) { 1975 id = r.getInt("plot_plot_id"); 1976 o = r.getString("user_uuid"); 1977 user = uuids.get(o); 1978 if (user == null) { 1979 user = UUID.fromString(o); 1980 uuids.put(o, user); 1981 } 1982 Plot plot = plots.get(id); 1983 if (plot != null) { 1984 plot.getTrusted().add(user); 1985 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 1986 toDelete.add(id); 1987 } else { 1988 LOGGER.warn("Entry #{}({}) in `plot_helpers` does not exist." 1989 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 1990 } 1991 } 1992 deleteRows(toDelete, this.prefix + "plot_helpers", "plot_plot_id"); 1993 } 1994 1995 /* 1996 * Getting trusted 1997 */ 1998 try (ResultSet r = statement.executeQuery( 1999 "SELECT `user_uuid`, `plot_plot_id` FROM `" + this.prefix + "plot_trusted`")) { 2000 ArrayList<Integer> toDelete = new ArrayList<>(); 2001 while (r.next()) { 2002 id = r.getInt("plot_plot_id"); 2003 o = r.getString("user_uuid"); 2004 user = uuids.get(o); 2005 if (user == null) { 2006 user = UUID.fromString(o); 2007 uuids.put(o, user); 2008 } 2009 Plot plot = plots.get(id); 2010 if (plot != null) { 2011 plot.getMembers().add(user); 2012 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 2013 toDelete.add(id); 2014 } else { 2015 LOGGER.warn("Entry #{}({}) in `plot_trusted` does not exist." 2016 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 2017 } 2018 } 2019 deleteRows(toDelete, this.prefix + "plot_trusted", "plot_plot_id"); 2020 } 2021 2022 /* 2023 * Getting denied 2024 */ 2025 try (ResultSet r = statement.executeQuery( 2026 "SELECT `user_uuid`, `plot_plot_id` FROM `" + this.prefix + "plot_denied`")) { 2027 ArrayList<Integer> toDelete = new ArrayList<>(); 2028 while (r.next()) { 2029 id = r.getInt("plot_plot_id"); 2030 o = r.getString("user_uuid"); 2031 user = uuids.get(o); 2032 if (user == null) { 2033 user = UUID.fromString(o); 2034 uuids.put(o, user); 2035 } 2036 Plot plot = plots.get(id); 2037 if (plot != null) { 2038 plot.getDenied().add(user); 2039 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 2040 toDelete.add(id); 2041 } else { 2042 LOGGER.warn("Entry #{}({}) in `plot_denied` does not exist." 2043 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 2044 } 2045 } 2046 deleteRows(toDelete, this.prefix + "plot_denied", "plot_plot_id"); 2047 } 2048 2049 try (final ResultSet resultSet = statement 2050 .executeQuery("SELECT * FROM `" + this.prefix + "plot_flags`")) { 2051 BlockTypeListFlag.skipCategoryVerification = 2052 true; // allow invalid tags, as initialized lazily 2053 final ArrayList<Integer> toDelete = new ArrayList<>(); 2054 final Map<Plot, Collection<PlotFlag<?, ?>>> invalidFlags = new HashMap<>(); 2055 while (resultSet.next()) { 2056 id = resultSet.getInt("plot_id"); 2057 final String flag = resultSet.getString("flag"); 2058 String value = resultSet.getString("value"); 2059 final Plot plot = plots.get(id); 2060 if (plot != null) { 2061 final PlotFlag<?, ?> plotFlag = 2062 GlobalFlagContainer.getInstance().getFlagFromString(flag); 2063 if (plotFlag == null) { 2064 plot.getFlagContainer().addUnknownFlag(flag, value); 2065 } else { 2066 value = CaptionUtility.stripClickEvents(plotFlag, value); 2067 try { 2068 plot.getFlagContainer().addFlag(plotFlag.parse(value)); 2069 } catch (final FlagParseException e) { 2070 e.printStackTrace(); 2071 LOGGER.error("Plot with ID {} has an invalid value:", id); 2072 LOGGER.error("Failed to parse flag '{}', value '{}': {}", 2073 plotFlag.getName(), e.getValue(), e.getErrorMessage() 2074 ); 2075 if (!invalidFlags.containsKey(plot)) { 2076 invalidFlags.put(plot, new ArrayList<>()); 2077 } 2078 invalidFlags.get(plot).add(plotFlag); 2079 } 2080 } 2081 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 2082 toDelete.add(id); 2083 } else { 2084 LOGGER.warn("Entry #{}({}) in `plot_flags` does not exist." 2085 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 2086 } 2087 } 2088 BlockTypeListFlag.skipCategoryVerification = 2089 false; // don't allow invalid tags anymore 2090 if (Settings.Enabled_Components.DATABASE_PURGER) { 2091 for (final Map.Entry<Plot, Collection<PlotFlag<?, ?>>> plotFlagEntry : invalidFlags 2092 .entrySet()) { 2093 for (final PlotFlag<?, ?> flag : plotFlagEntry.getValue()) { 2094 LOGGER.info( 2095 "Plot {} has an invalid flag ({}). A fix has been attempted", 2096 plotFlagEntry.getKey(), flag.getName() 2097 ); 2098 removeFlag(plotFlagEntry.getKey(), flag); 2099 } 2100 } 2101 } 2102 deleteRows(toDelete, this.prefix + "plot_flags", "plot_id"); 2103 } 2104 2105 try (ResultSet resultSet = statement 2106 .executeQuery("SELECT * FROM `" + this.prefix + "plot_settings`")) { 2107 ArrayList<Integer> toDelete = new ArrayList<>(); 2108 while (resultSet.next()) { 2109 id = resultSet.getInt("plot_plot_id"); 2110 Plot plot = plots.get(id); 2111 if (plot != null) { 2112 plots.remove(id); 2113 String alias = resultSet.getString("alias"); 2114 if (alias != null) { 2115 plot.getSettings().setAlias(alias); 2116 } 2117 String pos = resultSet.getString("position"); 2118 switch (pos.toLowerCase()) { 2119 case "": 2120 case "default": 2121 case "0,0,0": 2122 case "center": 2123 case "centre": 2124 break; 2125 default: 2126 try { 2127 plot.getSettings().setPosition(BlockLoc.fromString(pos)); 2128 } catch (Exception ignored) { 2129 } 2130 } 2131 int m = resultSet.getInt("merged"); 2132 boolean[] merged = new boolean[4]; 2133 for (int i = 0; i < 4; i++) { 2134 merged[3 - i] = (m & 1 << i) != 0; 2135 } 2136 plot.getSettings().setMerged(merged); 2137 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 2138 toDelete.add(id); 2139 } else { 2140 LOGGER.warn("Entry #{}({}) in `plot_settings` does not exist." 2141 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 2142 } 2143 } 2144 deleteRows(toDelete, this.prefix + "plot_settings", "plot_plot_id"); 2145 } 2146 } 2147 if (!plots.entrySet().isEmpty()) { 2148 createEmptySettings(new ArrayList<>(plots.keySet()), null); 2149 for (Entry<Integer, Plot> entry : plots.entrySet()) { 2150 entry.getValue().getSettings(); 2151 } 2152 } 2153 boolean invalidPlot = false; 2154 for (Entry<String, AtomicInteger> entry : noExist.entrySet()) { 2155 String worldName = entry.getKey(); 2156 invalidPlot = true; 2157 if (Settings.DEBUG) { 2158 LOGGER.info("Warning! Found {} plots in DB for non existent world: '{}'", 2159 entry.getValue().intValue(), worldName 2160 ); 2161 } 2162 } 2163 if (invalidPlot && Settings.DEBUG) { 2164 LOGGER.info("Warning! Please create the world(s) or remove the plots using the purge command"); 2165 } 2166 } catch (SQLException e) { 2167 LOGGER.error("Failed to load plots", e); 2168 } 2169 return newPlots; 2170 } 2171 2172 @Override 2173 public void setMerged(final Plot plot, final boolean[] merged) { 2174 plot.getSettings().setMerged(merged); 2175 addPlotTask(plot, new UniqueStatement("setMerged") { 2176 @Override 2177 public void set(PreparedStatement statement) throws SQLException { 2178 int hash = HashUtil.hash(merged); 2179 statement.setInt(1, hash); 2180 statement.setInt(2, getId(plot)); 2181 } 2182 2183 @Override 2184 public PreparedStatement get() throws SQLException { 2185 return SQLManager.this.connection.prepareStatement( 2186 "UPDATE `" + SQLManager.this.prefix 2187 + "plot_settings` SET `merged` = ? WHERE `plot_plot_id` = ?"); 2188 } 2189 }); 2190 } 2191 2192 @Override 2193 public CompletableFuture<Boolean> swapPlots(Plot plot1, Plot plot2) { 2194 final CompletableFuture<Boolean> future = new CompletableFuture<>(); 2195 TaskManager.runTaskAsync(() -> { 2196 final int id1 = getId(plot1); 2197 final int id2 = getId(plot2); 2198 final PlotId pos1 = plot1.getId(); 2199 final PlotId pos2 = plot2.getId(); 2200 try (final PreparedStatement preparedStatement = this.connection.prepareStatement( 2201 "UPDATE `" + SQLManager.this.prefix 2202 + "plot` SET `plot_id_x` = ?, `plot_id_z` = ? WHERE `id` = ?")) { 2203 preparedStatement.setInt(1, pos1.getX()); 2204 preparedStatement.setInt(2, pos1.getY()); 2205 preparedStatement.setInt(3, id1); 2206 preparedStatement.execute(); 2207 preparedStatement.setInt(1, pos2.getX()); 2208 preparedStatement.setInt(2, pos2.getY()); 2209 preparedStatement.setInt(3, id2); 2210 preparedStatement.execute(); 2211 } catch (final Exception e) { 2212 LOGGER.error("Failed to persist wap of {} and {}", plot1, plot2); 2213 e.printStackTrace(); 2214 future.complete(false); 2215 return; 2216 } 2217 future.complete(true); 2218 }); 2219 return future; 2220 } 2221 2222 @Override 2223 public void movePlot(final Plot original, final Plot newPlot) { 2224 addPlotTask(original, new UniqueStatement("movePlot") { 2225 @Override 2226 public void set(PreparedStatement statement) throws SQLException { 2227 statement.setInt(1, newPlot.getId().getX()); 2228 statement.setInt(2, newPlot.getId().getY()); 2229 statement.setString(3, newPlot.getArea().toString()); 2230 statement.setInt(4, getId(original)); 2231 } 2232 2233 @Override 2234 public PreparedStatement get() throws SQLException { 2235 return SQLManager.this.connection.prepareStatement( 2236 "UPDATE `" + SQLManager.this.prefix 2237 + "plot` SET `plot_id_x` = ?, `plot_id_z` = ?, `world` = ? WHERE `id` = ?"); 2238 } 2239 }); 2240 addPlotTask(newPlot, null); 2241 } 2242 2243 @Override 2244 public void setFlag(final Plot plot, final PlotFlag<?, ?> flag) { 2245 addPlotTask(plot, new UniqueStatement("setFlag") { 2246 @Override 2247 public void set(PreparedStatement statement) throws SQLException { 2248 statement.setInt(1, getId(plot)); 2249 statement.setString(2, flag.getName()); 2250 statement.setString(3, flag.toString()); 2251 statement.setString(4, flag.toString()); 2252 } 2253 2254 @Override 2255 public PreparedStatement get() throws SQLException { 2256 final String statement; 2257 if (SQLManager.this.mySQL) { 2258 statement = "INSERT INTO `" + SQLManager.this.prefix 2259 + "plot_flags`(`plot_id`, `flag`, `value`) VALUES(?, ?, ?) " 2260 + "ON DUPLICATE KEY UPDATE `value` = ?"; 2261 } else { 2262 statement = "INSERT INTO `" + SQLManager.this.prefix 2263 + "plot_flags`(`plot_id`, `flag`, `value`) VALUES(?, ?, ?) " 2264 + "ON CONFLICT(`plot_id`,`flag`) DO UPDATE SET `value` = ?"; 2265 } 2266 return SQLManager.this.connection.prepareStatement(statement); 2267 } 2268 }); 2269 } 2270 2271 @Override 2272 public void removeFlag(final Plot plot, final PlotFlag<?, ?> flag) { 2273 addPlotTask(plot, new UniqueStatement("removeFlag") { 2274 @Override 2275 public void set(PreparedStatement statement) throws SQLException { 2276 statement.setInt(1, getId(plot)); 2277 statement.setString(2, flag.getName()); 2278 } 2279 2280 @Override 2281 public PreparedStatement get() throws SQLException { 2282 return SQLManager.this.connection.prepareStatement( 2283 "DELETE FROM `" + SQLManager.this.prefix 2284 + "plot_flags` WHERE `plot_id` = ? AND `flag` = ?"); 2285 } 2286 }); 2287 } 2288 2289 @Override 2290 public void setAlias(final Plot plot, final String alias) { 2291 addPlotTask(plot, new UniqueStatement("setAlias") { 2292 @Override 2293 public void set(PreparedStatement statement) throws SQLException { 2294 statement.setString(1, alias); 2295 statement.setInt(2, getId(plot)); 2296 } 2297 2298 @Override 2299 public PreparedStatement get() throws SQLException { 2300 return SQLManager.this.connection.prepareStatement( 2301 "UPDATE `" + SQLManager.this.prefix 2302 + "plot_settings` SET `alias` = ? WHERE `plot_plot_id` = ?"); 2303 } 2304 }); 2305 } 2306 2307 /** 2308 * Purge all plots with the following database IDs 2309 */ 2310 @Override 2311 public void purgeIds(final Set<Integer> uniqueIds) { 2312 addGlobalTask(() -> { 2313 if (!uniqueIds.isEmpty()) { 2314 try { 2315 ArrayList<Integer> uniqueIdsList = new ArrayList<>(uniqueIds); 2316 int size = uniqueIdsList.size(); 2317 int packet = 990; 2318 int amount = size / packet; 2319 for (int j = 0; j <= amount; j++) { 2320 List<Integer> subList = 2321 uniqueIdsList.subList(j * packet, Math.min(size, (j + 1) * packet)); 2322 if (subList.isEmpty()) { 2323 break; 2324 } 2325 StringBuilder idstr2 = new StringBuilder(); 2326 String stmt_prefix = ""; 2327 for (Integer id : subList) { 2328 idstr2.append(stmt_prefix).append(id); 2329 stmt_prefix = " OR `id` = "; 2330 } 2331 stmt_prefix = ""; 2332 StringBuilder idstr = new StringBuilder(); 2333 for (Integer id : subList) { 2334 idstr.append(stmt_prefix).append(id); 2335 stmt_prefix = " OR `plot_plot_id` = "; 2336 } 2337 PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 2338 "DELETE FROM `" + SQLManager.this.prefix 2339 + "plot_helpers` WHERE `plot_plot_id` = " + idstr); 2340 stmt.executeUpdate(); 2341 stmt.close(); 2342 stmt = SQLManager.this.connection.prepareStatement( 2343 "DELETE FROM `" + SQLManager.this.prefix 2344 + "plot_denied` WHERE `plot_plot_id` = " + idstr); 2345 stmt.executeUpdate(); 2346 stmt.close(); 2347 stmt = SQLManager.this.connection.prepareStatement( 2348 "DELETE FROM `" + SQLManager.this.prefix 2349 + "plot_settings` WHERE `plot_plot_id` = " + idstr); 2350 stmt.executeUpdate(); 2351 stmt.close(); 2352 stmt = SQLManager.this.connection.prepareStatement( 2353 "DELETE FROM `" + SQLManager.this.prefix 2354 + "plot_trusted` WHERE `plot_plot_id` = " + idstr); 2355 stmt.executeUpdate(); 2356 stmt.close(); 2357 stmt = SQLManager.this.connection.prepareStatement( 2358 "DELETE FROM `" + SQLManager.this.prefix + "plot` WHERE `id` = " 2359 + idstr2); 2360 stmt.executeUpdate(); 2361 stmt.close(); 2362 commit(); 2363 } 2364 } catch (SQLException e) { 2365 LOGGER.error("Failed to purge plots", e); 2366 return; 2367 } 2368 } 2369 LOGGER.info("Successfully purged {} plots", uniqueIds.size()); 2370 }); 2371 } 2372 2373 @Override 2374 public void purge(final PlotArea area, final Set<PlotId> plots) { 2375 addGlobalTask(() -> { 2376 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 2377 "SELECT `id`, `plot_id_x`, `plot_id_z` FROM `" + SQLManager.this.prefix 2378 + "plot` WHERE `world` = ?")) { 2379 stmt.setString(1, area.toString()); 2380 Set<Integer> ids; 2381 try (ResultSet r = stmt.executeQuery()) { 2382 ids = new HashSet<>(); 2383 while (r.next()) { 2384 PlotId plot_id = PlotId.of(r.getInt("plot_id_x"), r.getInt("plot_id_z")); 2385 if (plots.contains(plot_id)) { 2386 ids.add(r.getInt("id")); 2387 } 2388 } 2389 } 2390 purgeIds(ids); 2391 } catch (SQLException e) { 2392 LOGGER.error("Failed to purge area '{}'", area); 2393 e.printStackTrace(); 2394 } 2395 for (Iterator<PlotId> iterator = plots.iterator(); iterator.hasNext(); ) { 2396 PlotId plotId = iterator.next(); 2397 iterator.remove(); 2398 PlotId id = PlotId.of(plotId.getX(), plotId.getY()); 2399 area.removePlot(id); 2400 } 2401 }); 2402 } 2403 2404 @Override 2405 public void setPosition(final Plot plot, final String position) { 2406 addPlotTask(plot, new UniqueStatement("setPosition") { 2407 @Override 2408 public void set(PreparedStatement statement) throws SQLException { 2409 // Please see the table creation statement. There is the default value of "default" 2410 statement.setString(1, position == null ? "DEFAULT" : position); 2411 statement.setInt(2, getId(plot)); 2412 } 2413 2414 @Override 2415 public PreparedStatement get() throws SQLException { 2416 return SQLManager.this.connection.prepareStatement( 2417 "UPDATE `" + SQLManager.this.prefix 2418 + "plot_settings` SET `position` = ? WHERE `plot_plot_id` = ?"); 2419 } 2420 }); 2421 } 2422 2423 @Override 2424 public void removeComment(final Plot plot, final PlotComment comment) { 2425 addPlotTask(plot, new UniqueStatement("removeComment") { 2426 @Override 2427 public void set(PreparedStatement statement) throws SQLException { 2428 if (plot != null) { 2429 statement.setString(1, plot.getArea().toString()); 2430 statement.setInt(2, plot.getId().hashCode()); 2431 statement.setString(3, comment.comment()); 2432 statement.setString(4, comment.inbox()); 2433 statement.setString(5, comment.senderName()); 2434 } else { 2435 statement.setString(1, comment.comment()); 2436 statement.setString(2, comment.inbox()); 2437 statement.setString(3, comment.senderName()); 2438 } 2439 } 2440 2441 @Override 2442 public PreparedStatement get() throws SQLException { 2443 if (plot != null) { 2444 return SQLManager.this.connection.prepareStatement( 2445 "DELETE FROM `" + SQLManager.this.prefix 2446 + "plot_comments` WHERE `world` = ? AND `hashcode` = ? AND `comment` = ? AND `inbox` = ? AND `sender` = ?"); 2447 } 2448 return SQLManager.this.connection.prepareStatement( 2449 "DELETE FROM `" + SQLManager.this.prefix 2450 + "plot_comments` WHERE `comment` = ? AND `inbox` = ? AND `sender` = ?"); 2451 } 2452 }); 2453 } 2454 2455 @Override 2456 public void clearInbox(final Plot plot, final String inbox) { 2457 addPlotTask(plot, new UniqueStatement("clearInbox") { 2458 @Override 2459 public void set(PreparedStatement statement) throws SQLException { 2460 if (plot != null) { 2461 statement.setString(1, plot.getArea().toString()); 2462 statement.setInt(2, plot.getId().hashCode()); 2463 statement.setString(3, inbox); 2464 } else { 2465 statement.setString(1, inbox); 2466 } 2467 } 2468 2469 @Override 2470 public PreparedStatement get() throws SQLException { 2471 if (plot != null) { 2472 return SQLManager.this.connection.prepareStatement( 2473 "DELETE FROM `" + SQLManager.this.prefix 2474 + "plot_comments` WHERE `world` = ? AND `hashcode` = ? AND `inbox` = ?"); 2475 } 2476 return SQLManager.this.connection.prepareStatement( 2477 "DELETE FROM `" + SQLManager.this.prefix + "plot_comments` `inbox` = ?"); 2478 } 2479 }); 2480 } 2481 2482 @Override 2483 public void getComments( 2484 @NonNull Plot plot, final String inbox, 2485 final RunnableVal<List<PlotComment>> whenDone 2486 ) { 2487 addPlotTask(plot, new UniqueStatement("getComments_" + plot) { 2488 @Override 2489 public void set(PreparedStatement statement) throws SQLException { 2490 if (plot != null) { 2491 statement.setString(1, plot.getArea().toString()); 2492 statement.setInt(2, plot.getId().hashCode()); 2493 statement.setString(3, inbox); 2494 } else { 2495 statement.setString(1, inbox); 2496 } 2497 } 2498 2499 @Override 2500 public PreparedStatement get() throws SQLException { 2501 if (plot != null) { 2502 return SQLManager.this.connection.prepareStatement( 2503 "SELECT * FROM `" + SQLManager.this.prefix 2504 + "plot_comments` WHERE `world` = ? AND `hashcode` = ? AND `inbox` = ?"); 2505 } 2506 return SQLManager.this.connection.prepareStatement( 2507 "SELECT * FROM `" + SQLManager.this.prefix 2508 + "plot_comments` WHERE `inbox` = ?"); 2509 } 2510 2511 @Override 2512 public void execute(PreparedStatement statement) { 2513 } 2514 2515 @Override 2516 public void addBatch(PreparedStatement statement) throws SQLException { 2517 ArrayList<PlotComment> comments = new ArrayList<>(); 2518 try (ResultSet set = statement.executeQuery()) { 2519 while (set.next()) { 2520 String sender = set.getString("sender"); 2521 String world = set.getString("world"); 2522 int hash = set.getInt("hashcode"); 2523 PlotId id; 2524 if (hash != 0) { 2525 id = PlotId.unpair(hash); 2526 } else { 2527 id = null; 2528 } 2529 String msg = set.getString("comment"); 2530 long timestamp = set.getInt("timestamp") * 1000; 2531 PlotComment comment = 2532 new PlotComment(world, id, msg, sender, inbox, timestamp); 2533 comments.add(comment); 2534 } 2535 whenDone.value = comments; 2536 } 2537 TaskManager.runTask(whenDone); 2538 } 2539 }); 2540 } 2541 2542 @Override 2543 public void setComment(final Plot plot, final PlotComment comment) { 2544 addPlotTask(plot, new UniqueStatement("setComment") { 2545 @Override 2546 public void set(PreparedStatement statement) throws SQLException { 2547 statement.setString(1, plot.getArea().toString()); 2548 statement.setInt(2, plot.getId().hashCode()); 2549 statement.setString(3, comment.comment()); 2550 statement.setString(4, comment.inbox()); 2551 statement.setInt(5, (int) (comment.timestamp() / 1000)); 2552 statement.setString(6, comment.senderName()); 2553 } 2554 2555 @Override 2556 public PreparedStatement get() throws SQLException { 2557 return SQLManager.this.connection.prepareStatement( 2558 "INSERT INTO `" + SQLManager.this.prefix 2559 + "plot_comments` (`world`, `hashcode`, `comment`, `inbox`, `timestamp`, `sender`) VALUES(?,?,?,?,?,?)"); 2560 } 2561 }); 2562 } 2563 2564 @Override 2565 public void removeTrusted(final Plot plot, final UUID uuid) { 2566 addPlotTask(plot, new UniqueStatement("removeTrusted") { 2567 @Override 2568 public void set(PreparedStatement statement) throws SQLException { 2569 statement.setInt(1, getId(plot)); 2570 statement.setString(2, uuid.toString()); 2571 } 2572 2573 @Override 2574 public PreparedStatement get() throws SQLException { 2575 return SQLManager.this.connection.prepareStatement( 2576 "DELETE FROM `" + SQLManager.this.prefix 2577 + "plot_helpers` WHERE `plot_plot_id` = ? AND `user_uuid` = ?"); 2578 } 2579 }); 2580 } 2581 2582 @Override 2583 public void removeMember(final Plot plot, final UUID uuid) { 2584 addPlotTask(plot, new UniqueStatement("removeMember") { 2585 @Override 2586 public void set(PreparedStatement statement) throws SQLException { 2587 statement.setInt(1, getId(plot)); 2588 statement.setString(2, uuid.toString()); 2589 } 2590 2591 @Override 2592 public PreparedStatement get() throws SQLException { 2593 return SQLManager.this.connection.prepareStatement( 2594 "DELETE FROM `" + SQLManager.this.prefix 2595 + "plot_trusted` WHERE `plot_plot_id` = ? AND `user_uuid` = ?"); 2596 } 2597 }); 2598 } 2599 2600 @Override 2601 public void setTrusted(final Plot plot, final UUID uuid) { 2602 addPlotTask(plot, new UniqueStatement("setTrusted") { 2603 @Override 2604 public void set(PreparedStatement statement) throws SQLException { 2605 statement.setInt(1, getId(plot)); 2606 statement.setString(2, uuid.toString()); 2607 } 2608 2609 @Override 2610 public PreparedStatement get() throws SQLException { 2611 return SQLManager.this.connection.prepareStatement( 2612 "INSERT INTO `" + SQLManager.this.prefix 2613 + "plot_helpers` (`plot_plot_id`, `user_uuid`) VALUES(?,?)"); 2614 } 2615 }); 2616 } 2617 2618 @Override 2619 public void setMember(final Plot plot, final UUID uuid) { 2620 addPlotTask(plot, new UniqueStatement("setMember") { 2621 @Override 2622 public void set(PreparedStatement statement) throws SQLException { 2623 statement.setInt(1, getId(plot)); 2624 statement.setString(2, uuid.toString()); 2625 } 2626 2627 @Override 2628 public PreparedStatement get() throws SQLException { 2629 return SQLManager.this.connection.prepareStatement( 2630 "INSERT INTO `" + SQLManager.this.prefix 2631 + "plot_trusted` (`plot_plot_id`, `user_uuid`) VALUES(?,?)"); 2632 } 2633 }); 2634 } 2635 2636 @Override 2637 public void removeDenied(final Plot plot, final UUID uuid) { 2638 addPlotTask(plot, new UniqueStatement("removeDenied") { 2639 @Override 2640 public void set(PreparedStatement statement) throws SQLException { 2641 statement.setInt(1, getId(plot)); 2642 statement.setString(2, uuid.toString()); 2643 } 2644 2645 @Override 2646 public PreparedStatement get() throws SQLException { 2647 return SQLManager.this.connection.prepareStatement( 2648 "DELETE FROM `" + SQLManager.this.prefix 2649 + "plot_denied` WHERE `plot_plot_id` = ? AND `user_uuid` = ?"); 2650 } 2651 }); 2652 } 2653 2654 @Override 2655 public void setDenied(final Plot plot, final UUID uuid) { 2656 addPlotTask(plot, new UniqueStatement("setDenied") { 2657 @Override 2658 public void set(PreparedStatement statement) throws SQLException { 2659 statement.setInt(1, getId(plot)); 2660 statement.setString(2, uuid.toString()); 2661 } 2662 2663 @Override 2664 public PreparedStatement get() throws SQLException { 2665 return SQLManager.this.connection.prepareStatement( 2666 "INSERT INTO `" + SQLManager.this.prefix 2667 + "plot_denied` (`plot_plot_id`, `user_uuid`) VALUES(?,?)"); 2668 } 2669 }); 2670 } 2671 2672 @Override 2673 public HashMap<UUID, Integer> getRatings(Plot plot) { 2674 HashMap<UUID, Integer> map = new HashMap<>(); 2675 try (PreparedStatement statement = this.connection.prepareStatement( 2676 "SELECT `rating`, `player` FROM `" + this.prefix 2677 + "plot_rating` WHERE `plot_plot_id` = ? ")) { 2678 statement.setInt(1, getId(plot)); 2679 try (ResultSet resultSet = statement.executeQuery()) { 2680 while (resultSet.next()) { 2681 UUID uuid = UUID.fromString(resultSet.getString("player")); 2682 int rating = resultSet.getInt("rating"); 2683 map.put(uuid, rating); 2684 } 2685 } 2686 } catch (SQLException e) { 2687 LOGGER.error("Failed to fetch rating for plot {}", plot.getId().toString()); 2688 e.printStackTrace(); 2689 } 2690 return map; 2691 } 2692 2693 @Override 2694 public void setRating(final Plot plot, final UUID rater, final int value) { 2695 addPlotTask(plot, new UniqueStatement("setRating") { 2696 @Override 2697 public void set(PreparedStatement statement) throws SQLException { 2698 statement.setInt(1, getId(plot)); 2699 statement.setInt(2, value); 2700 statement.setString(3, rater.toString()); 2701 } 2702 2703 @Override 2704 public PreparedStatement get() throws SQLException { 2705 return SQLManager.this.connection.prepareStatement( 2706 "INSERT INTO `" + SQLManager.this.prefix 2707 + "plot_rating` (`plot_plot_id`, `rating`, `player`) VALUES(?,?,?)"); 2708 } 2709 }); 2710 } 2711 2712 @Override 2713 public void delete(PlotCluster cluster) { 2714 final int id = getClusterId(cluster); 2715 addClusterTask(cluster, new UniqueStatement("delete_cluster_settings") { 2716 @Override 2717 public void set(PreparedStatement statement) throws SQLException { 2718 statement.setInt(1, id); 2719 } 2720 2721 @Override 2722 public PreparedStatement get() throws SQLException { 2723 return SQLManager.this.connection.prepareStatement( 2724 "DELETE FROM `" + SQLManager.this.prefix 2725 + "cluster_settings` WHERE `cluster_id` = ?"); 2726 } 2727 }); 2728 addClusterTask(cluster, new UniqueStatement("delete_cluster_helpers") { 2729 @Override 2730 public void set(PreparedStatement statement) throws SQLException { 2731 statement.setInt(1, id); 2732 } 2733 2734 @Override 2735 public PreparedStatement get() throws SQLException { 2736 return SQLManager.this.connection.prepareStatement( 2737 "DELETE FROM `" + SQLManager.this.prefix 2738 + "cluster_helpers` WHERE `cluster_id` = ?"); 2739 } 2740 }); 2741 addClusterTask(cluster, new UniqueStatement("delete_cluster_invited") { 2742 @Override 2743 public void set(PreparedStatement statement) throws SQLException { 2744 statement.setInt(1, id); 2745 } 2746 2747 @Override 2748 public PreparedStatement get() throws SQLException { 2749 return SQLManager.this.connection.prepareStatement( 2750 "DELETE FROM `" + SQLManager.this.prefix 2751 + "cluster_invited` WHERE `cluster_id` = ?"); 2752 } 2753 }); 2754 addClusterTask(cluster, new UniqueStatement("delete_cluster") { 2755 @Override 2756 public void set(PreparedStatement statement) throws SQLException { 2757 statement.setInt(1, id); 2758 } 2759 2760 @Override 2761 public PreparedStatement get() throws SQLException { 2762 return SQLManager.this.connection.prepareStatement( 2763 "DELETE FROM `" + SQLManager.this.prefix + "cluster` WHERE `id` = ?"); 2764 } 2765 }); 2766 } 2767 2768 @Override 2769 public void addPersistentMeta( 2770 final UUID uuid, final String key, final byte[] meta, 2771 final boolean replace 2772 ) { 2773 addPlayerTask(uuid, new UniqueStatement("addPersistentMeta") { 2774 @Override 2775 public void set(PreparedStatement statement) throws SQLException { 2776 if (replace) { 2777 statement.setBytes(1, meta); 2778 statement.setString(2, uuid.toString()); 2779 statement.setString(3, key); 2780 } else { 2781 statement.setString(1, uuid.toString()); 2782 statement.setString(2, key); 2783 statement.setBytes(3, meta); 2784 } 2785 } 2786 2787 @Override 2788 public PreparedStatement get() throws SQLException { 2789 if (replace) { 2790 return SQLManager.this.connection.prepareStatement( 2791 "UPDATE `" + SQLManager.this.prefix 2792 + "player_meta` SET `value` = ? WHERE `uuid` = ? AND `key` = ?"); 2793 } else { 2794 return SQLManager.this.connection.prepareStatement( 2795 "INSERT INTO `" + SQLManager.this.prefix 2796 + "player_meta`(`uuid`, `key`, `value`) VALUES(?, ? ,?)"); 2797 } 2798 } 2799 }); 2800 } 2801 2802 @Override 2803 public void removePersistentMeta(final UUID uuid, final String key) { 2804 addPlayerTask(uuid, new UniqueStatement("removePersistentMeta") { 2805 @Override 2806 public void set(PreparedStatement statement) throws SQLException { 2807 statement.setString(1, uuid.toString()); 2808 statement.setString(2, key); 2809 } 2810 2811 @Override 2812 public PreparedStatement get() throws SQLException { 2813 return SQLManager.this.connection.prepareStatement( 2814 "DELETE FROM `" + SQLManager.this.prefix 2815 + "player_meta` WHERE `uuid` = ? AND `key` = ?"); 2816 } 2817 }); 2818 } 2819 2820 @Override 2821 public void getPersistentMeta(final UUID uuid, final RunnableVal<Map<String, byte[]>> result) { 2822 addPlayerTask(uuid, new UniqueStatement("getPersistentMeta") { 2823 @Override 2824 public void set(PreparedStatement statement) throws SQLException { 2825 statement.setString(1, uuid.toString()); 2826 } 2827 2828 @Override 2829 public PreparedStatement get() throws SQLException { 2830 return SQLManager.this.connection.prepareStatement( 2831 "SELECT * FROM `" + SQLManager.this.prefix 2832 + "player_meta` WHERE `uuid` = ? ORDER BY `meta_id` ASC"); 2833 } 2834 2835 @Override 2836 public void execute(PreparedStatement statement) { 2837 } 2838 2839 @Override 2840 public void addBatch(PreparedStatement statement) throws SQLException { 2841 ResultSet resultSet = statement.executeQuery(); 2842 2843 final Map<String, byte[]> metaMap = new HashMap<>(); 2844 2845 while (resultSet.next()) { 2846 String key = resultSet.getString("key"); 2847 byte[] bytes = resultSet.getBytes("value"); 2848 metaMap.put(key, bytes); 2849 } 2850 2851 resultSet.close(); 2852 TaskManager.runTaskAsync(() -> result.run(metaMap)); 2853 } 2854 2855 }); 2856 } 2857 2858 @Override 2859 public HashMap<String, Set<PlotCluster>> getClusters() { 2860 LinkedHashMap<String, Set<PlotCluster>> newClusters = new LinkedHashMap<>(); 2861 HashMap<Integer, PlotCluster> clusters = new HashMap<>(); 2862 try { 2863 HashSet<String> areas = new HashSet<>(); 2864 if (this.worldConfiguration.contains("worlds")) { 2865 ConfigurationSection worldSection = this.worldConfiguration.getConfigurationSection("worlds"); 2866 if (worldSection != null) { 2867 for (String worldKey : worldSection.getKeys(false)) { 2868 areas.add(worldKey); 2869 ConfigurationSection areaSection = 2870 worldSection.getConfigurationSection(worldKey + ".areas"); 2871 if (areaSection != null) { 2872 for (String areaKey : areaSection.getKeys(false)) { 2873 String[] split = areaKey.split("(?<![;])-"); 2874 if (split.length == 3) { 2875 areas.add(worldKey + ';' + split[0]); 2876 } 2877 } 2878 } 2879 } 2880 } 2881 } 2882 HashMap<String, UUID> uuids = new HashMap<>(); 2883 HashMap<String, Integer> noExist = new HashMap<>(); 2884 /* 2885 * Getting clusters 2886 */ 2887 try (Statement stmt = this.connection.createStatement()) { 2888 ResultSet resultSet = 2889 stmt.executeQuery("SELECT * FROM `" + this.prefix + "cluster`"); 2890 PlotCluster cluster; 2891 String owner; 2892 UUID user; 2893 int id; 2894 while (resultSet.next()) { 2895 PlotId pos1 = 2896 PlotId.of(resultSet.getInt("pos1_x"), resultSet.getInt("pos1_z")); 2897 PlotId pos2 = 2898 PlotId.of(resultSet.getInt("pos2_x"), resultSet.getInt("pos2_z")); 2899 id = resultSet.getInt("id"); 2900 String areaid = resultSet.getString("world"); 2901 if (!areas.contains(areaid)) { 2902 noExist.merge(areaid, 1, Integer::sum); 2903 } 2904 owner = resultSet.getString("owner"); 2905 user = uuids.get(owner); 2906 if (user == null) { 2907 user = UUID.fromString(owner); 2908 uuids.put(owner, user); 2909 } 2910 cluster = new PlotCluster(null, pos1, pos2, user, id); 2911 clusters.put(id, cluster); 2912 Set<PlotCluster> set = 2913 newClusters.computeIfAbsent(areaid, k -> new HashSet<>()); 2914 set.add(cluster); 2915 } 2916 //Getting helpers 2917 resultSet = stmt.executeQuery( 2918 "SELECT `user_uuid`, `cluster_id` FROM `" + this.prefix + "cluster_helpers`"); 2919 while (resultSet.next()) { 2920 id = resultSet.getInt("cluster_id"); 2921 owner = resultSet.getString("user_uuid"); 2922 user = uuids.get(owner); 2923 if (user == null) { 2924 user = UUID.fromString(owner); 2925 uuids.put(owner, user); 2926 } 2927 cluster = clusters.get(id); 2928 if (cluster != null) { 2929 cluster.helpers.add(user); 2930 } else { 2931 LOGGER.warn("Cluster #{}({}) in cluster_helpers does not exist." 2932 + " Please create the cluster or remove this entry", id, cluster); 2933 } 2934 } 2935 // Getting invited 2936 resultSet = stmt.executeQuery( 2937 "SELECT `user_uuid`, `cluster_id` FROM `" + this.prefix + "cluster_invited`"); 2938 while (resultSet.next()) { 2939 id = resultSet.getInt("cluster_id"); 2940 owner = resultSet.getString("user_uuid"); 2941 user = uuids.get(owner); 2942 if (user == null) { 2943 user = UUID.fromString(owner); 2944 uuids.put(owner, user); 2945 } 2946 cluster = clusters.get(id); 2947 if (cluster != null) { 2948 cluster.invited.add(user); 2949 } else { 2950 LOGGER.warn("Cluster #{}({}) in cluster_helpers does not exist." 2951 + " Please create the cluster or remove this entry", id, cluster); 2952 } 2953 } 2954 resultSet = 2955 stmt.executeQuery("SELECT * FROM `" + this.prefix + "cluster_settings`"); 2956 while (resultSet.next()) { 2957 id = resultSet.getInt("cluster_id"); 2958 cluster = clusters.get(id); 2959 if (cluster != null) { 2960 String alias = resultSet.getString("alias"); 2961 if (alias != null) { 2962 cluster.settings.setAlias(alias); 2963 } 2964 String pos = resultSet.getString("position"); 2965 switch (pos.toLowerCase()) { 2966 case "": 2967 case "default": 2968 case "0,0,0": 2969 case "center": 2970 case "centre": 2971 break; 2972 default: 2973 try { 2974 BlockLoc loc = BlockLoc.fromString(pos); 2975 cluster.settings.setPosition(loc); 2976 } catch (Exception ignored) { 2977 } 2978 } 2979 int m = resultSet.getInt("merged"); 2980 boolean[] merged = new boolean[4]; 2981 for (int i = 0; i < 4; i++) { 2982 merged[3 - i] = (m & 1 << i) != 0; 2983 } 2984 cluster.settings.setMerged(merged); 2985 } else { 2986 LOGGER.warn("Cluster #{}({}) in cluster_helpers does not exist." 2987 + " Please create the cluster or remove this entry", id, cluster); 2988 } 2989 } 2990 resultSet.close(); 2991 } 2992 boolean invalidPlot = false; 2993 for (Entry<String, Integer> entry : noExist.entrySet()) { 2994 String a = entry.getKey(); 2995 invalidPlot = true; 2996 LOGGER.warn("Warning! Found {} clusters in DB for non existent area; '{}'", noExist.get(a), a); 2997 } 2998 if (invalidPlot) { 2999 LOGGER.warn("Warning! Please create the world(s) or remove the clusters using the purge command"); 3000 } 3001 } catch (SQLException e) { 3002 LOGGER.error("Failed to load clusters", e); 3003 } 3004 return newClusters; 3005 } 3006 3007 @Override 3008 public void setClusterName(final PlotCluster cluster, final String name) { 3009 addClusterTask(cluster, new UniqueStatement("setClusterName") { 3010 @Override 3011 public void set(PreparedStatement statement) throws SQLException { 3012 statement.setString(1, name); 3013 statement.setInt(2, getClusterId(cluster)); 3014 } 3015 3016 @Override 3017 public PreparedStatement get() throws SQLException { 3018 return SQLManager.this.connection.prepareStatement( 3019 "UPDATE `" + SQLManager.this.prefix 3020 + "cluster_settings` SET `alias` = ? WHERE `cluster_id` = ?"); 3021 } 3022 }); 3023 cluster.settings.setAlias(name); 3024 } 3025 3026 @Override 3027 public void removeHelper(final PlotCluster cluster, final UUID uuid) { 3028 addClusterTask(cluster, new UniqueStatement("removeHelper") { 3029 @Override 3030 public void set(PreparedStatement statement) throws SQLException { 3031 statement.setInt(1, getClusterId(cluster)); 3032 statement.setString(2, uuid.toString()); 3033 } 3034 3035 @Override 3036 public PreparedStatement get() throws SQLException { 3037 return SQLManager.this.connection.prepareStatement( 3038 "DELETE FROM `" + SQLManager.this.prefix 3039 + "cluster_helpers` WHERE `cluster_id` = ? AND `user_uuid` = ?"); 3040 } 3041 }); 3042 } 3043 3044 @Override 3045 public void setHelper(final PlotCluster cluster, final UUID uuid) { 3046 addClusterTask(cluster, new UniqueStatement("setHelper") { 3047 @Override 3048 public void set(PreparedStatement statement) throws SQLException { 3049 statement.setInt(1, getClusterId(cluster)); 3050 statement.setString(2, uuid.toString()); 3051 } 3052 3053 @Override 3054 public PreparedStatement get() throws SQLException { 3055 return SQLManager.this.connection.prepareStatement( 3056 "INSERT INTO `" + SQLManager.this.prefix 3057 + "cluster_helpers` (`cluster_id`, `user_uuid`) VALUES(?,?)"); 3058 } 3059 }); 3060 } 3061 3062 @Override 3063 public void createCluster(final PlotCluster cluster) { 3064 addClusterTask(cluster, new UniqueStatement("createCluster_" + cluster.hashCode()) { 3065 @Override 3066 public void set(PreparedStatement statement) throws SQLException { 3067 statement.setInt(1, cluster.getP1().getX()); 3068 statement.setInt(2, cluster.getP1().getY()); 3069 statement.setInt(3, cluster.getP2().getX()); 3070 statement.setInt(4, cluster.getP2().getY()); 3071 statement.setString(5, cluster.owner.toString()); 3072 statement.setString(6, cluster.area.toString()); 3073 } 3074 3075 @Override 3076 public PreparedStatement get() throws SQLException { 3077 return SQLManager.this.connection.prepareStatement( 3078 SQLManager.this.CREATE_CLUSTER, 3079 Statement.RETURN_GENERATED_KEYS 3080 ); 3081 } 3082 3083 @Override 3084 public void execute(PreparedStatement statement) { 3085 } 3086 3087 @Override 3088 public void addBatch(PreparedStatement statement) throws SQLException { 3089 statement.execute(); 3090 try (ResultSet keys = supportsGetGeneratedKeys ? statement.getGeneratedKeys() : statement.getResultSet()) { 3091 if (keys.next()) { 3092 cluster.temp = keys.getInt(1); 3093 } 3094 } 3095 } 3096 }); 3097 addClusterTask( 3098 cluster, 3099 new UniqueStatement("createCluster_settings_" + cluster.hashCode()) { 3100 @Override 3101 public void set(PreparedStatement statement) throws SQLException { 3102 statement.setInt(1, getClusterId(cluster)); 3103 statement.setString(2, cluster.settings.getAlias()); 3104 } 3105 3106 @Override 3107 public PreparedStatement get() throws SQLException { 3108 return SQLManager.this.connection.prepareStatement( 3109 "INSERT INTO `" + SQLManager.this.prefix 3110 + "cluster_settings`(`cluster_id`, `alias`) VALUES(?, ?)"); 3111 } 3112 } 3113 ); 3114 } 3115 3116 @Override 3117 public void resizeCluster(final PlotCluster current, PlotId min, PlotId max) { 3118 final PlotId pos1 = PlotId.of(current.getP1().getX(), current.getP1().getY()); 3119 final PlotId pos2 = PlotId.of(current.getP2().getX(), current.getP2().getY()); 3120 current.setP1(min); 3121 current.setP2(max); 3122 3123 addClusterTask(current, new UniqueStatement("resizeCluster") { 3124 @Override 3125 public void set(PreparedStatement statement) throws SQLException { 3126 statement.setInt(1, pos1.getX()); 3127 statement.setInt(2, pos1.getY()); 3128 statement.setInt(3, pos2.getX()); 3129 statement.setInt(4, pos2.getY()); 3130 statement.setInt(5, getClusterId(current)); 3131 } 3132 3133 @Override 3134 public PreparedStatement get() throws SQLException { 3135 return SQLManager.this.connection.prepareStatement( 3136 "UPDATE `" + SQLManager.this.prefix 3137 + "cluster` SET `pos1_x` = ?, `pos1_z` = ?, `pos2_x` = ?, `pos2_z` = ? WHERE `id` = ?"); 3138 } 3139 }); 3140 } 3141 3142 @Override 3143 public void setPosition(final PlotCluster cluster, final String position) { 3144 addClusterTask(cluster, new UniqueStatement("setPosition") { 3145 @Override 3146 public void set(PreparedStatement statement) throws SQLException { 3147 statement.setString(1, position); 3148 statement.setInt(2, getClusterId(cluster)); 3149 } 3150 3151 @Override 3152 public PreparedStatement get() throws SQLException { 3153 return SQLManager.this.connection.prepareStatement( 3154 "UPDATE `" + SQLManager.this.prefix 3155 + "cluster_settings` SET `position` = ? WHERE `cluster_id` = ?"); 3156 } 3157 }); 3158 } 3159 3160 @Override 3161 public void removeInvited(final PlotCluster cluster, final UUID uuid) { 3162 addClusterTask(cluster, new UniqueStatement("removeInvited") { 3163 @Override 3164 public void set(PreparedStatement statement) throws SQLException { 3165 statement.setInt(1, getClusterId(cluster)); 3166 statement.setString(2, uuid.toString()); 3167 } 3168 3169 @Override 3170 public PreparedStatement get() throws SQLException { 3171 return SQLManager.this.connection.prepareStatement( 3172 "DELETE FROM `" + SQLManager.this.prefix 3173 + "cluster_invited` WHERE `cluster_id` = ? AND `user_uuid` = ?"); 3174 } 3175 }); 3176 } 3177 3178 @Override 3179 public void setInvited(final PlotCluster cluster, final UUID uuid) { 3180 addClusterTask(cluster, new UniqueStatement("setInvited") { 3181 @Override 3182 public void set(PreparedStatement statement) throws SQLException { 3183 statement.setInt(1, getClusterId(cluster)); 3184 statement.setString(2, uuid.toString()); 3185 } 3186 3187 @Override 3188 public PreparedStatement get() throws SQLException { 3189 return SQLManager.this.connection.prepareStatement( 3190 "INSERT INTO `" + SQLManager.this.prefix 3191 + "cluster_invited` (`cluster_id`, `user_uuid`) VALUES(?,?)"); 3192 } 3193 }); 3194 } 3195 3196 @Override 3197 public boolean deleteTables() { 3198 try (Statement stmt = this.connection.createStatement(); 3199 PreparedStatement statement = this.connection 3200 .prepareStatement("DROP TABLE `" + this.prefix + "plot`")) { 3201 close(); 3202 this.closed = false; 3203 SQLManager.this.connection = this.database.forceConnection(); 3204 stmt.addBatch("DROP TABLE `" + this.prefix + "cluster_invited`"); 3205 stmt.addBatch("DROP TABLE `" + this.prefix + "cluster_helpers`"); 3206 stmt.addBatch("DROP TABLE `" + this.prefix + "cluster`"); 3207 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_rating`"); 3208 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_settings`"); 3209 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_comments`"); 3210 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_trusted`"); 3211 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_helpers`"); 3212 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_denied`"); 3213 stmt.executeBatch(); 3214 stmt.clearBatch(); 3215 statement.executeUpdate(); 3216 } catch (ClassNotFoundException | SQLException e) { 3217 e.printStackTrace(); 3218 3219 } 3220 return true; 3221 } 3222 3223 @SuppressWarnings({"unchecked", "unused"}) 3224 @Override 3225 public void validateAllPlots(Set<Plot> toValidate) { 3226 if (!isValid()) { 3227 reconnect(); 3228 } 3229 LOGGER.info( 3230 "All DB transactions during this session are being validated (This may take a while if corrections need to be made)"); 3231 commit(); 3232 while (true) { 3233 if (!sendBatch()) { 3234 break; 3235 } 3236 } 3237 try { 3238 if (this.connection.getAutoCommit()) { 3239 this.connection.setAutoCommit(false); 3240 } 3241 } catch (SQLException e) { 3242 e.printStackTrace(); 3243 } 3244 HashMap<String, HashMap<PlotId, Plot>> database = getPlots(); 3245 ArrayList<Plot> toCreate = new ArrayList<>(); 3246 for (Plot plot : toValidate) { 3247 if (plot.temp == -1) { 3248 continue; 3249 } 3250 if (plot.getArea() == null) { 3251 LOGGER.error("CRITICAL ERROR IN VALIDATION TASK: {}", plot); 3252 LOGGER.error("PLOT AREA CANNOT BE NULL! SKIPPING PLOT!"); 3253 LOGGER.info("Delete this entry from your database or set `database-purger: true` in the settings.yml"); 3254 continue; 3255 } 3256 if (database == null) { 3257 LOGGER.error("CRITICAL ERROR IN VALIDATION TASK!"); 3258 LOGGER.error("DATABASE VARIABLE CANNOT BE NULL! NOW ENDING VALIDATION!"); 3259 break; 3260 } 3261 HashMap<PlotId, Plot> worldPlots = database.get(plot.getArea().toString()); 3262 if (worldPlots == null) { 3263 toCreate.add(plot); 3264 continue; 3265 } 3266 Plot dataPlot = worldPlots.remove(plot.getId()); 3267 if (dataPlot == null) { 3268 toCreate.add(plot); 3269 continue; 3270 } 3271 // owner 3272 if (!plot.getOwnerAbs().equals(dataPlot.getOwnerAbs())) { 3273 setOwner(plot, plot.getOwnerAbs()); 3274 } 3275 // trusted 3276 if (!plot.getTrusted().equals(dataPlot.getTrusted())) { 3277 HashSet<UUID> toAdd = (HashSet<UUID>) plot.getTrusted().clone(); 3278 HashSet<UUID> toRemove = (HashSet<UUID>) dataPlot.getTrusted().clone(); 3279 toRemove.removeAll(plot.getTrusted()); 3280 toAdd.removeAll(dataPlot.getTrusted()); 3281 if (!toRemove.isEmpty()) { 3282 for (UUID uuid : toRemove) { 3283 removeTrusted(plot, uuid); 3284 } 3285 } 3286 if (!toAdd.isEmpty()) { 3287 for (UUID uuid : toAdd) { 3288 setTrusted(plot, uuid); 3289 } 3290 } 3291 } 3292 if (!plot.getMembers().equals(dataPlot.getMembers())) { 3293 HashSet<UUID> toAdd = (HashSet<UUID>) plot.getMembers().clone(); 3294 HashSet<UUID> toRemove = (HashSet<UUID>) dataPlot.getMembers().clone(); 3295 toRemove.removeAll(plot.getMembers()); 3296 toAdd.removeAll(dataPlot.getMembers()); 3297 if (!toRemove.isEmpty()) { 3298 for (UUID uuid : toRemove) { 3299 removeMember(plot, uuid); 3300 } 3301 } 3302 if (!toAdd.isEmpty()) { 3303 for (UUID uuid : toAdd) { 3304 setMember(plot, uuid); 3305 } 3306 } 3307 } 3308 if (!plot.getDenied().equals(dataPlot.getDenied())) { 3309 HashSet<UUID> toAdd = (HashSet<UUID>) plot.getDenied().clone(); 3310 HashSet<UUID> toRemove = (HashSet<UUID>) dataPlot.getDenied().clone(); 3311 toRemove.removeAll(plot.getDenied()); 3312 toAdd.removeAll(dataPlot.getDenied()); 3313 if (!toRemove.isEmpty()) { 3314 for (UUID uuid : toRemove) { 3315 removeDenied(plot, uuid); 3316 } 3317 } 3318 if (!toAdd.isEmpty()) { 3319 for (UUID uuid : toAdd) { 3320 setDenied(plot, uuid); 3321 } 3322 } 3323 } 3324 boolean[] pm = plot.getMerged(); 3325 boolean[] dm = dataPlot.getMerged(); 3326 if (pm[0] != dm[0] || pm[1] != dm[1]) { 3327 setMerged(dataPlot, plot.getMerged()); 3328 } 3329 Set<PlotFlag<?, ?>> pf = plot.getFlags(); 3330 Set<PlotFlag<?, ?>> df = dataPlot.getFlags(); 3331 if (!pf.isEmpty() && !df.isEmpty()) { 3332 if (pf.size() != df.size() || !StringMan 3333 .isEqual(StringMan.joinOrdered(pf, ","), StringMan.joinOrdered(df, ","))) { 3334 // setFlags(plot, pf); 3335 // TODO: Re-implement 3336 } 3337 } 3338 } 3339 3340 for (Entry<String, HashMap<PlotId, Plot>> entry : database.entrySet()) { 3341 HashMap<PlotId, Plot> map = entry.getValue(); 3342 if (!map.isEmpty()) { 3343 for (Entry<PlotId, Plot> entry2 : map.entrySet()) { 3344 // TODO implement this when sure safe" 3345 } 3346 } 3347 } 3348 commit(); 3349 } 3350 3351 @Override 3352 public void replaceWorld( 3353 final String oldWorld, final String newWorld, final PlotId min, 3354 final PlotId max 3355 ) { 3356 addGlobalTask(() -> { 3357 if (min == null) { 3358 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 3359 "UPDATE `" + SQLManager.this.prefix 3360 + "plot` SET `world` = ? WHERE `world` = ?")) { 3361 stmt.setString(1, newWorld); 3362 stmt.setString(2, oldWorld); 3363 stmt.executeUpdate(); 3364 } catch (SQLException e) { 3365 e.printStackTrace(); 3366 } 3367 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 3368 "UPDATE `" + SQLManager.this.prefix 3369 + "cluster` SET `world` = ? WHERE `world` = ?")) { 3370 stmt.setString(1, newWorld); 3371 stmt.setString(2, oldWorld); 3372 stmt.executeUpdate(); 3373 } catch (SQLException e) { 3374 e.printStackTrace(); 3375 } 3376 } else { 3377 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 3378 "UPDATE `" + SQLManager.this.prefix 3379 + "plot` SET `world` = ? WHERE `world` = ? AND `plot_id_x` BETWEEN ? AND ? AND `plot_id_z` BETWEEN ? AND ?")) { 3380 stmt.setString(1, newWorld); 3381 stmt.setString(2, oldWorld); 3382 stmt.setInt(3, min.getX()); 3383 stmt.setInt(4, max.getX()); 3384 stmt.setInt(5, min.getY()); 3385 stmt.setInt(6, max.getY()); 3386 stmt.executeUpdate(); 3387 } catch (SQLException e) { 3388 e.printStackTrace(); 3389 } 3390 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 3391 "UPDATE `" + SQLManager.this.prefix 3392 + "cluster` SET `world` = ? WHERE `world` = ? AND `pos1_x` <= ? AND `pos1_z` <= ? AND `pos2_x` >= ? AND `pos2_z` >= ?")) { 3393 stmt.setString(1, newWorld); 3394 stmt.setString(2, oldWorld); 3395 stmt.setInt(3, max.getX()); 3396 stmt.setInt(4, max.getY()); 3397 stmt.setInt(5, min.getX()); 3398 stmt.setInt(6, min.getY()); 3399 stmt.executeUpdate(); 3400 } catch (SQLException e) { 3401 e.printStackTrace(); 3402 } 3403 } 3404 }); 3405 } 3406 3407 @Override 3408 public void replaceUUID(final UUID old, final UUID now) { 3409 addGlobalTask(() -> { 3410 try (Statement stmt = SQLManager.this.connection.createStatement()) { 3411 stmt.executeUpdate( 3412 "UPDATE `" + SQLManager.this.prefix + "cluster` SET `owner` = '" + now 3413 .toString() + "' WHERE `owner` = '" + old.toString() + '\''); 3414 stmt.executeUpdate( 3415 "UPDATE `" + SQLManager.this.prefix + "cluster_helpers` SET `user_uuid` = '" 3416 + now + "' WHERE `user_uuid` = '" + old + '\''); 3417 stmt.executeUpdate( 3418 "UPDATE `" + SQLManager.this.prefix + "cluster_invited` SET `user_uuid` = '" 3419 + now + "' WHERE `user_uuid` = '" + old + '\''); 3420 stmt.executeUpdate( 3421 "UPDATE `" + SQLManager.this.prefix + "plot` SET `owner` = '" + now 3422 + "' WHERE `owner` = '" + old + '\''); 3423 stmt.executeUpdate( 3424 "UPDATE `" + SQLManager.this.prefix + "plot_denied` SET `user_uuid` = '" + now + "' WHERE `user_uuid` = '" + old + '\''); 3425 stmt.executeUpdate( 3426 "UPDATE `" + SQLManager.this.prefix + "plot_helpers` SET `user_uuid` = '" + now + "' WHERE `user_uuid` = '" + old + '\''); 3427 stmt.executeUpdate( 3428 "UPDATE `" + SQLManager.this.prefix + "plot_trusted` SET `user_uuid` = '" + now + "' WHERE `user_uuid` = '" + old + '\''); 3429 } catch (SQLException e) { 3430 e.printStackTrace(); 3431 } 3432 }); 3433 } 3434 3435 @Override 3436 public void close() { 3437 try { 3438 this.closed = true; 3439 this.connection.close(); 3440 } catch (SQLException e) { 3441 e.printStackTrace(); 3442 } 3443 } 3444 3445 private record LegacySettings( 3446 int id, 3447 PlotSettings settings 3448 ) { 3449 3450 } 3451 3452 public abstract static class UniqueStatement { 3453 3454 public final String method; 3455 3456 public UniqueStatement(String method) { 3457 this.method = method; 3458 } 3459 3460 public void addBatch(PreparedStatement statement) throws SQLException { 3461 statement.addBatch(); 3462 } 3463 3464 public void execute(PreparedStatement statement) throws SQLException { 3465 statement.executeBatch(); 3466 } 3467 3468 public abstract PreparedStatement get() throws SQLException; 3469 3470 public abstract void set(PreparedStatement statement) throws SQLException; 3471 3472 } 3473 3474 private record UUIDPair(int id, UUID uuid) { 3475 3476 } 3477 3478}