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 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 (Statement statement = this.connection.createStatement()) { 1732 try (ResultSet resultSet = statement 1733 .executeQuery("SELECT * FROM `" + this.prefix + "plot_settings`")) { 1734 while (resultSet.next()) { 1735 final int id = resultSet.getInt("plot_plot_id"); 1736 final String plotFlags = resultSet.getString("flags"); 1737 if (plotFlags == null || plotFlags.isEmpty()) { 1738 continue; 1739 } 1740 flagMap.put(id, new HashMap<>()); 1741 for (String element : plotFlags.split(",")) { 1742 if (element.contains(":")) { 1743 String[] split = element.split(":"); // splits flag:value 1744 try { 1745 String flag_str = 1746 split[1].replaceAll("¯", ":").replaceAll("\u00B4", ","); 1747 flagMap.get(id).put(split[0], flag_str); 1748 } catch (Exception e) { 1749 e.printStackTrace(); 1750 } 1751 } 1752 } 1753 } 1754 } 1755 } catch (final Exception e) { 1756 LOGGER.error("Failed to load old flag values", e); 1757 return false; 1758 } 1759 LOGGER.info("Loaded {} plot flag collections...", flagMap.size()); 1760 LOGGER.info("Attempting to store these flags in the new table..."); 1761 try (final PreparedStatement preparedStatement = this.connection.prepareStatement( 1762 "INSERT INTO `" + SQLManager.this.prefix 1763 + "plot_flags`(`plot_id`, `flag`, `value`) VALUES(?, ?, ?)")) { 1764 1765 long timeStarted = System.currentTimeMillis(); 1766 int flagsProcessed = 0; 1767 int plotsProcessed = 0; 1768 1769 int totalFlags = 0; 1770 for (final Map<String, String> flags : flagMap.values()) { 1771 totalFlags += flags.size(); 1772 } 1773 1774 for (final Map.Entry<Integer, Map<String, String>> plotFlagEntry : flagMap.entrySet()) { 1775 for (final Map.Entry<String, String> flagEntry : plotFlagEntry.getValue() 1776 .entrySet()) { 1777 preparedStatement.setInt(1, plotFlagEntry.getKey()); 1778 preparedStatement.setString(2, flagEntry.getKey()); 1779 preparedStatement.setString(3, flagEntry.getValue()); 1780 preparedStatement.addBatch(); 1781 flagsProcessed += 1; 1782 } 1783 plotsProcessed += 1; 1784 1785 try { 1786 preparedStatement.executeBatch(); 1787 } catch (final Exception e) { 1788 LOGGER.error("Failed to store flag values for plot with entry ID: {}", plotFlagEntry.getKey()); 1789 e.printStackTrace(); 1790 continue; 1791 } 1792 1793 if (System.currentTimeMillis() - timeStarted >= 1000L || plotsProcessed >= flagMap 1794 .size()) { 1795 timeStarted = System.currentTimeMillis(); 1796 LOGGER.info( 1797 "... Flag conversion in progress. {}% done", 1798 String.format("%.1f", ((float) flagsProcessed / totalFlags) * 100) 1799 ); 1800 } 1801 LOGGER.info( 1802 "- Finished converting flags for plot with entry ID: {}", 1803 plotFlagEntry.getKey() 1804 ); 1805 } 1806 } catch (final Exception e) { 1807 LOGGER.error("Failed to store flag values", e); 1808 return false; 1809 } 1810 return true; 1811 } 1812 1813 /** 1814 * Load all plots, helpers, denied, trusted, and every setting from DB into a {@link HashMap}. 1815 */ 1816 @Override 1817 public HashMap<String, HashMap<PlotId, Plot>> getPlots() { 1818 HashMap<String, HashMap<PlotId, Plot>> newPlots = new HashMap<>(); 1819 HashMap<Integer, Plot> plots = new HashMap<>(); 1820 try { 1821 HashSet<String> areas = new HashSet<>(); 1822 if (this.worldConfiguration.contains("worlds")) { 1823 ConfigurationSection worldSection = this.worldConfiguration.getConfigurationSection("worlds"); 1824 if (worldSection != null) { 1825 for (String worldKey : worldSection.getKeys(false)) { 1826 areas.add(worldKey); 1827 ConfigurationSection areaSection = 1828 worldSection.getConfigurationSection(worldKey + ".areas"); 1829 if (areaSection != null) { 1830 for (String areaKey : areaSection.getKeys(false)) { 1831 String[] split = areaKey.split("(?<![;])-"); 1832 if (split.length == 3) { 1833 areas.add(worldKey + ';' + split[0]); 1834 } 1835 } 1836 } 1837 } 1838 } 1839 } 1840 HashMap<String, UUID> uuids = new HashMap<>(); 1841 HashMap<String, AtomicInteger> noExist = new HashMap<>(); 1842 1843 /* 1844 * Getting plots 1845 */ 1846 try (Statement statement = this.connection.createStatement()) { 1847 int id; 1848 String o; 1849 UUID user; 1850 try (ResultSet resultSet = statement.executeQuery( 1851 "SELECT `id`, `plot_id_x`, `plot_id_z`, `owner`, `world`, `timestamp` FROM `" 1852 + this.prefix + "plot`")) { 1853 ArrayList<Integer> toDelete = new ArrayList<>(); 1854 while (resultSet.next()) { 1855 PlotId plot_id = PlotId.of( 1856 resultSet.getInt("plot_id_x"), 1857 resultSet.getInt("plot_id_z") 1858 ); 1859 id = resultSet.getInt("id"); 1860 String areaID = resultSet.getString("world"); 1861 if (!areas.contains(areaID)) { 1862 if (Settings.Enabled_Components.DATABASE_PURGER) { 1863 toDelete.add(id); 1864 continue; 1865 } else { 1866 AtomicInteger value = noExist.get(areaID); 1867 if (value != null) { 1868 value.incrementAndGet(); 1869 } else { 1870 noExist.put(areaID, new AtomicInteger(1)); 1871 } 1872 } 1873 } 1874 o = resultSet.getString("owner"); 1875 user = uuids.get(o); 1876 if (user == null) { 1877 try { 1878 user = UUID.fromString(o); 1879 } catch (IllegalArgumentException e) { 1880 if (Settings.UUID.FORCE_LOWERCASE) { 1881 user = UUID.nameUUIDFromBytes( 1882 ("OfflinePlayer:" + o.toLowerCase()) 1883 .getBytes(Charsets.UTF_8)); 1884 } else { 1885 user = UUID.nameUUIDFromBytes( 1886 ("OfflinePlayer:" + o).getBytes(Charsets.UTF_8)); 1887 } 1888 } 1889 uuids.put(o, user); 1890 } 1891 long time; 1892 try { 1893 Timestamp timestamp = resultSet.getTimestamp("timestamp"); 1894 time = timestamp.getTime(); 1895 } catch (SQLException exception) { 1896 String parsable = resultSet.getString("timestamp"); 1897 try { 1898 time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(parsable) 1899 .getTime(); 1900 } catch (ParseException e) { 1901 LOGGER.error("Could not parse date for plot: #{}({};{}) ({})", 1902 id, areaID, plot_id, parsable 1903 ); 1904 time = System.currentTimeMillis() + id; 1905 } 1906 } 1907 Plot p = new Plot(plot_id, user, new HashSet<>(), new HashSet<>(), 1908 new HashSet<>(), "", null, null, null, 1909 new boolean[]{false, false, false, false}, time, id 1910 ); 1911 HashMap<PlotId, Plot> map = newPlots.get(areaID); 1912 if (map != null) { 1913 Plot last = map.put(p.getId(), p); 1914 if (last != null) { 1915 if (Settings.Enabled_Components.DATABASE_PURGER) { 1916 toDelete.add(last.temp); 1917 } else { 1918 LOGGER.info( 1919 "Plot #{}({}) in `{}plot` is a duplicate." 1920 + " Delete this plot or set `database-purger: true` in the settings.yml", 1921 id, 1922 last, 1923 this.prefix 1924 ); 1925 } 1926 } 1927 } else { 1928 map = new HashMap<>(); 1929 newPlots.put(areaID, map); 1930 map.put(p.getId(), p); 1931 } 1932 plots.put(id, p); 1933 } 1934 deleteRows(toDelete, this.prefix + "plot", "id"); 1935 } 1936 if (Settings.Enabled_Components.RATING_CACHE) { 1937 try (ResultSet r = statement.executeQuery( 1938 "SELECT `plot_plot_id`, `player`, `rating` FROM `" + this.prefix 1939 + "plot_rating`")) { 1940 ArrayList<Integer> toDelete = new ArrayList<>(); 1941 while (r.next()) { 1942 id = r.getInt("plot_plot_id"); 1943 o = r.getString("player"); 1944 user = uuids.get(o); 1945 if (user == null) { 1946 user = UUID.fromString(o); 1947 uuids.put(o, user); 1948 } 1949 Plot plot = plots.get(id); 1950 if (plot != null) { 1951 plot.getSettings().getRatings().put(user, r.getInt("rating")); 1952 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 1953 toDelete.add(id); 1954 } else { 1955 LOGGER.warn("Entry #{}({}) in `plot_rating` does not exist." 1956 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 1957 } 1958 } 1959 deleteRows(toDelete, this.prefix + "plot_rating", "plot_plot_id"); 1960 } 1961 } 1962 1963 /* 1964 * Getting helpers 1965 */ 1966 try (ResultSet r = statement.executeQuery( 1967 "SELECT `user_uuid`, `plot_plot_id` FROM `" + this.prefix + "plot_helpers`")) { 1968 ArrayList<Integer> toDelete = new ArrayList<>(); 1969 while (r.next()) { 1970 id = r.getInt("plot_plot_id"); 1971 o = r.getString("user_uuid"); 1972 user = uuids.get(o); 1973 if (user == null) { 1974 user = UUID.fromString(o); 1975 uuids.put(o, user); 1976 } 1977 Plot plot = plots.get(id); 1978 if (plot != null) { 1979 plot.getTrusted().add(user); 1980 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 1981 toDelete.add(id); 1982 } else { 1983 LOGGER.warn("Entry #{}({}) in `plot_helpers` does not exist." 1984 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 1985 } 1986 } 1987 deleteRows(toDelete, this.prefix + "plot_helpers", "plot_plot_id"); 1988 } 1989 1990 /* 1991 * Getting trusted 1992 */ 1993 try (ResultSet r = statement.executeQuery( 1994 "SELECT `user_uuid`, `plot_plot_id` FROM `" + this.prefix + "plot_trusted`")) { 1995 ArrayList<Integer> toDelete = new ArrayList<>(); 1996 while (r.next()) { 1997 id = r.getInt("plot_plot_id"); 1998 o = r.getString("user_uuid"); 1999 user = uuids.get(o); 2000 if (user == null) { 2001 user = UUID.fromString(o); 2002 uuids.put(o, user); 2003 } 2004 Plot plot = plots.get(id); 2005 if (plot != null) { 2006 plot.getMembers().add(user); 2007 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 2008 toDelete.add(id); 2009 } else { 2010 LOGGER.warn("Entry #{}({}) in `plot_trusted` does not exist." 2011 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 2012 } 2013 } 2014 deleteRows(toDelete, this.prefix + "plot_trusted", "plot_plot_id"); 2015 } 2016 2017 /* 2018 * Getting denied 2019 */ 2020 try (ResultSet r = statement.executeQuery( 2021 "SELECT `user_uuid`, `plot_plot_id` FROM `" + this.prefix + "plot_denied`")) { 2022 ArrayList<Integer> toDelete = new ArrayList<>(); 2023 while (r.next()) { 2024 id = r.getInt("plot_plot_id"); 2025 o = r.getString("user_uuid"); 2026 user = uuids.get(o); 2027 if (user == null) { 2028 user = UUID.fromString(o); 2029 uuids.put(o, user); 2030 } 2031 Plot plot = plots.get(id); 2032 if (plot != null) { 2033 plot.getDenied().add(user); 2034 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 2035 toDelete.add(id); 2036 } else { 2037 LOGGER.warn("Entry #{}({}) in `plot_denied` does not exist." 2038 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 2039 } 2040 } 2041 deleteRows(toDelete, this.prefix + "plot_denied", "plot_plot_id"); 2042 } 2043 2044 try (final ResultSet resultSet = statement 2045 .executeQuery("SELECT * FROM `" + this.prefix + "plot_flags`")) { 2046 BlockTypeListFlag.skipCategoryVerification = 2047 true; // allow invalid tags, as initialized lazily 2048 final ArrayList<Integer> toDelete = new ArrayList<>(); 2049 final Map<Plot, Collection<PlotFlag<?, ?>>> invalidFlags = new HashMap<>(); 2050 while (resultSet.next()) { 2051 id = resultSet.getInt("plot_id"); 2052 final String flag = resultSet.getString("flag"); 2053 String value = resultSet.getString("value"); 2054 final Plot plot = plots.get(id); 2055 if (plot != null) { 2056 final PlotFlag<?, ?> plotFlag = 2057 GlobalFlagContainer.getInstance().getFlagFromString(flag); 2058 if (plotFlag == null) { 2059 plot.getFlagContainer().addUnknownFlag(flag, value); 2060 } else { 2061 value = CaptionUtility.stripClickEvents(plotFlag, value); 2062 try { 2063 plot.getFlagContainer().addFlag(plotFlag.parse(value)); 2064 } catch (final FlagParseException e) { 2065 e.printStackTrace(); 2066 LOGGER.error("Plot with ID {} has an invalid value:", id); 2067 LOGGER.error("Failed to parse flag '{}', value '{}': {}", 2068 plotFlag.getName(), e.getValue(), e.getErrorMessage() 2069 ); 2070 if (!invalidFlags.containsKey(plot)) { 2071 invalidFlags.put(plot, new ArrayList<>()); 2072 } 2073 invalidFlags.get(plot).add(plotFlag); 2074 } 2075 } 2076 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 2077 toDelete.add(id); 2078 } else { 2079 LOGGER.warn("Entry #{}({}) in `plot_flags` does not exist." 2080 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 2081 } 2082 } 2083 BlockTypeListFlag.skipCategoryVerification = 2084 false; // don't allow invalid tags anymore 2085 if (Settings.Enabled_Components.DATABASE_PURGER) { 2086 for (final Map.Entry<Plot, Collection<PlotFlag<?, ?>>> plotFlagEntry : invalidFlags 2087 .entrySet()) { 2088 for (final PlotFlag<?, ?> flag : plotFlagEntry.getValue()) { 2089 LOGGER.info( 2090 "Plot {} has an invalid flag ({}). A fix has been attempted", 2091 plotFlagEntry.getKey(), flag.getName() 2092 ); 2093 removeFlag(plotFlagEntry.getKey(), flag); 2094 } 2095 } 2096 } 2097 deleteRows(toDelete, this.prefix + "plot_flags", "plot_id"); 2098 } 2099 2100 try (ResultSet resultSet = statement 2101 .executeQuery("SELECT * FROM `" + this.prefix + "plot_settings`")) { 2102 ArrayList<Integer> toDelete = new ArrayList<>(); 2103 while (resultSet.next()) { 2104 id = resultSet.getInt("plot_plot_id"); 2105 Plot plot = plots.get(id); 2106 if (plot != null) { 2107 plots.remove(id); 2108 String alias = resultSet.getString("alias"); 2109 if (alias != null) { 2110 plot.getSettings().setAlias(alias); 2111 } 2112 String pos = resultSet.getString("position"); 2113 switch (pos.toLowerCase()) { 2114 case "": 2115 case "default": 2116 case "0,0,0": 2117 case "center": 2118 case "centre": 2119 break; 2120 default: 2121 try { 2122 plot.getSettings().setPosition(BlockLoc.fromString(pos)); 2123 } catch (Exception ignored) { 2124 } 2125 } 2126 int m = resultSet.getInt("merged"); 2127 boolean[] merged = new boolean[4]; 2128 for (int i = 0; i < 4; i++) { 2129 merged[3 - i] = (m & 1 << i) != 0; 2130 } 2131 plot.getSettings().setMerged(merged); 2132 } else if (Settings.Enabled_Components.DATABASE_PURGER) { 2133 toDelete.add(id); 2134 } else { 2135 LOGGER.warn("Entry #{}({}) in `plot_settings` does not exist." 2136 + " Create this plot or set `database-purger: true` in settings.yml", id, plot); 2137 } 2138 } 2139 deleteRows(toDelete, this.prefix + "plot_settings", "plot_plot_id"); 2140 } 2141 } 2142 if (!plots.entrySet().isEmpty()) { 2143 createEmptySettings(new ArrayList<>(plots.keySet()), null); 2144 for (Entry<Integer, Plot> entry : plots.entrySet()) { 2145 entry.getValue().getSettings(); 2146 } 2147 } 2148 boolean invalidPlot = false; 2149 for (Entry<String, AtomicInteger> entry : noExist.entrySet()) { 2150 String worldName = entry.getKey(); 2151 invalidPlot = true; 2152 if (Settings.DEBUG) { 2153 LOGGER.info("Warning! Found {} plots in DB for non existent world: '{}'", 2154 entry.getValue().intValue(), worldName 2155 ); 2156 } 2157 } 2158 if (invalidPlot && Settings.DEBUG) { 2159 LOGGER.info("Warning! Please create the world(s) or remove the plots using the purge command"); 2160 } 2161 } catch (SQLException e) { 2162 LOGGER.error("Failed to load plots", e); 2163 } 2164 return newPlots; 2165 } 2166 2167 @Override 2168 public void setMerged(final Plot plot, final boolean[] merged) { 2169 plot.getSettings().setMerged(merged); 2170 addPlotTask(plot, new UniqueStatement("setMerged") { 2171 @Override 2172 public void set(PreparedStatement statement) throws SQLException { 2173 int hash = HashUtil.hash(merged); 2174 statement.setInt(1, hash); 2175 statement.setInt(2, getId(plot)); 2176 } 2177 2178 @Override 2179 public PreparedStatement get() throws SQLException { 2180 return SQLManager.this.connection.prepareStatement( 2181 "UPDATE `" + SQLManager.this.prefix 2182 + "plot_settings` SET `merged` = ? WHERE `plot_plot_id` = ?"); 2183 } 2184 }); 2185 } 2186 2187 @Override 2188 public CompletableFuture<Boolean> swapPlots(Plot plot1, Plot plot2) { 2189 final CompletableFuture<Boolean> future = new CompletableFuture<>(); 2190 TaskManager.runTaskAsync(() -> { 2191 final int id1 = getId(plot1); 2192 final int id2 = getId(plot2); 2193 final PlotId pos1 = plot1.getId(); 2194 final PlotId pos2 = plot2.getId(); 2195 try (final PreparedStatement preparedStatement = this.connection.prepareStatement( 2196 "UPDATE `" + SQLManager.this.prefix 2197 + "plot` SET `plot_id_x` = ?, `plot_id_z` = ? WHERE `id` = ?")) { 2198 preparedStatement.setInt(1, pos1.getX()); 2199 preparedStatement.setInt(2, pos1.getY()); 2200 preparedStatement.setInt(3, id1); 2201 preparedStatement.execute(); 2202 preparedStatement.setInt(1, pos2.getX()); 2203 preparedStatement.setInt(2, pos2.getY()); 2204 preparedStatement.setInt(3, id2); 2205 preparedStatement.execute(); 2206 } catch (final Exception e) { 2207 LOGGER.error("Failed to persist wap of {} and {}", plot1, plot2); 2208 e.printStackTrace(); 2209 future.complete(false); 2210 return; 2211 } 2212 future.complete(true); 2213 }); 2214 return future; 2215 } 2216 2217 @Override 2218 public void movePlot(final Plot original, final Plot newPlot) { 2219 addPlotTask(original, new UniqueStatement("movePlot") { 2220 @Override 2221 public void set(PreparedStatement statement) throws SQLException { 2222 statement.setInt(1, newPlot.getId().getX()); 2223 statement.setInt(2, newPlot.getId().getY()); 2224 statement.setString(3, newPlot.getArea().toString()); 2225 statement.setInt(4, getId(original)); 2226 } 2227 2228 @Override 2229 public PreparedStatement get() throws SQLException { 2230 return SQLManager.this.connection.prepareStatement( 2231 "UPDATE `" + SQLManager.this.prefix 2232 + "plot` SET `plot_id_x` = ?, `plot_id_z` = ?, `world` = ? WHERE `id` = ?"); 2233 } 2234 }); 2235 addPlotTask(newPlot, null); 2236 } 2237 2238 @Override 2239 public void setFlag(final Plot plot, final PlotFlag<?, ?> flag) { 2240 addPlotTask(plot, new UniqueStatement("setFlag") { 2241 @Override 2242 public void set(PreparedStatement statement) throws SQLException { 2243 statement.setInt(1, getId(plot)); 2244 statement.setString(2, flag.getName()); 2245 statement.setString(3, flag.toString()); 2246 statement.setString(4, flag.toString()); 2247 } 2248 2249 @Override 2250 public PreparedStatement get() throws SQLException { 2251 final String statement; 2252 if (SQLManager.this.mySQL) { 2253 statement = "INSERT INTO `" + SQLManager.this.prefix 2254 + "plot_flags`(`plot_id`, `flag`, `value`) VALUES(?, ?, ?) " 2255 + "ON DUPLICATE KEY UPDATE `value` = ?"; 2256 } else { 2257 statement = "INSERT INTO `" + SQLManager.this.prefix 2258 + "plot_flags`(`plot_id`, `flag`, `value`) VALUES(?, ?, ?) " 2259 + "ON CONFLICT(`plot_id`,`flag`) DO UPDATE SET `value` = ?"; 2260 } 2261 return SQLManager.this.connection.prepareStatement(statement); 2262 } 2263 }); 2264 } 2265 2266 @Override 2267 public void removeFlag(final Plot plot, final PlotFlag<?, ?> flag) { 2268 addPlotTask(plot, new UniqueStatement("removeFlag") { 2269 @Override 2270 public void set(PreparedStatement statement) throws SQLException { 2271 statement.setInt(1, getId(plot)); 2272 statement.setString(2, flag.getName()); 2273 } 2274 2275 @Override 2276 public PreparedStatement get() throws SQLException { 2277 return SQLManager.this.connection.prepareStatement( 2278 "DELETE FROM `" + SQLManager.this.prefix 2279 + "plot_flags` WHERE `plot_id` = ? AND `flag` = ?"); 2280 } 2281 }); 2282 } 2283 2284 @Override 2285 public void setAlias(final Plot plot, final String alias) { 2286 addPlotTask(plot, new UniqueStatement("setAlias") { 2287 @Override 2288 public void set(PreparedStatement statement) throws SQLException { 2289 statement.setString(1, alias); 2290 statement.setInt(2, getId(plot)); 2291 } 2292 2293 @Override 2294 public PreparedStatement get() throws SQLException { 2295 return SQLManager.this.connection.prepareStatement( 2296 "UPDATE `" + SQLManager.this.prefix 2297 + "plot_settings` SET `alias` = ? WHERE `plot_plot_id` = ?"); 2298 } 2299 }); 2300 } 2301 2302 /** 2303 * Purge all plots with the following database IDs 2304 */ 2305 @Override 2306 public void purgeIds(final Set<Integer> uniqueIds) { 2307 addGlobalTask(() -> { 2308 if (!uniqueIds.isEmpty()) { 2309 try { 2310 ArrayList<Integer> uniqueIdsList = new ArrayList<>(uniqueIds); 2311 int size = uniqueIdsList.size(); 2312 int packet = 990; 2313 int amount = size / packet; 2314 for (int j = 0; j <= amount; j++) { 2315 List<Integer> subList = 2316 uniqueIdsList.subList(j * packet, Math.min(size, (j + 1) * packet)); 2317 if (subList.isEmpty()) { 2318 break; 2319 } 2320 StringBuilder idstr2 = new StringBuilder(); 2321 String stmt_prefix = ""; 2322 for (Integer id : subList) { 2323 idstr2.append(stmt_prefix).append(id); 2324 stmt_prefix = " OR `id` = "; 2325 } 2326 stmt_prefix = ""; 2327 StringBuilder idstr = new StringBuilder(); 2328 for (Integer id : subList) { 2329 idstr.append(stmt_prefix).append(id); 2330 stmt_prefix = " OR `plot_plot_id` = "; 2331 } 2332 PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 2333 "DELETE FROM `" + SQLManager.this.prefix 2334 + "plot_helpers` WHERE `plot_plot_id` = " + idstr); 2335 stmt.executeUpdate(); 2336 stmt.close(); 2337 stmt = SQLManager.this.connection.prepareStatement( 2338 "DELETE FROM `" + SQLManager.this.prefix 2339 + "plot_denied` 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_settings` 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_trusted` WHERE `plot_plot_id` = " + idstr); 2350 stmt.executeUpdate(); 2351 stmt.close(); 2352 stmt = SQLManager.this.connection.prepareStatement( 2353 "DELETE FROM `" + SQLManager.this.prefix + "plot` WHERE `id` = " 2354 + idstr2); 2355 stmt.executeUpdate(); 2356 stmt.close(); 2357 commit(); 2358 } 2359 } catch (SQLException e) { 2360 LOGGER.error("Failed to purge plots", e); 2361 return; 2362 } 2363 } 2364 LOGGER.info("Successfully purged {} plots", uniqueIds.size()); 2365 }); 2366 } 2367 2368 @Override 2369 public void purge(final PlotArea area, final Set<PlotId> plots) { 2370 addGlobalTask(() -> { 2371 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 2372 "SELECT `id`, `plot_id_x`, `plot_id_z` FROM `" + SQLManager.this.prefix 2373 + "plot` WHERE `world` = ?")) { 2374 stmt.setString(1, area.toString()); 2375 Set<Integer> ids; 2376 try (ResultSet r = stmt.executeQuery()) { 2377 ids = new HashSet<>(); 2378 while (r.next()) { 2379 PlotId plot_id = PlotId.of(r.getInt("plot_id_x"), r.getInt("plot_id_z")); 2380 if (plots.contains(plot_id)) { 2381 ids.add(r.getInt("id")); 2382 } 2383 } 2384 } 2385 purgeIds(ids); 2386 } catch (SQLException e) { 2387 LOGGER.error("Failed to purge area '{}'", area); 2388 e.printStackTrace(); 2389 } 2390 for (Iterator<PlotId> iterator = plots.iterator(); iterator.hasNext(); ) { 2391 PlotId plotId = iterator.next(); 2392 iterator.remove(); 2393 PlotId id = PlotId.of(plotId.getX(), plotId.getY()); 2394 area.removePlot(id); 2395 } 2396 }); 2397 } 2398 2399 @Override 2400 public void setPosition(final Plot plot, final String position) { 2401 addPlotTask(plot, new UniqueStatement("setPosition") { 2402 @Override 2403 public void set(PreparedStatement statement) throws SQLException { 2404 // Please see the table creation statement. There is the default value of "default" 2405 statement.setString(1, position == null ? "DEFAULT" : position); 2406 statement.setInt(2, getId(plot)); 2407 } 2408 2409 @Override 2410 public PreparedStatement get() throws SQLException { 2411 return SQLManager.this.connection.prepareStatement( 2412 "UPDATE `" + SQLManager.this.prefix 2413 + "plot_settings` SET `position` = ? WHERE `plot_plot_id` = ?"); 2414 } 2415 }); 2416 } 2417 2418 @Override 2419 public void removeComment(final Plot plot, final PlotComment comment) { 2420 addPlotTask(plot, new UniqueStatement("removeComment") { 2421 @Override 2422 public void set(PreparedStatement statement) throws SQLException { 2423 if (plot != null) { 2424 statement.setString(1, plot.getArea().toString()); 2425 statement.setInt(2, plot.getId().hashCode()); 2426 statement.setString(3, comment.comment()); 2427 statement.setString(4, comment.inbox()); 2428 statement.setString(5, comment.senderName()); 2429 } else { 2430 statement.setString(1, comment.comment()); 2431 statement.setString(2, comment.inbox()); 2432 statement.setString(3, comment.senderName()); 2433 } 2434 } 2435 2436 @Override 2437 public PreparedStatement get() throws SQLException { 2438 if (plot != null) { 2439 return SQLManager.this.connection.prepareStatement( 2440 "DELETE FROM `" + SQLManager.this.prefix 2441 + "plot_comments` WHERE `world` = ? AND `hashcode` = ? AND `comment` = ? AND `inbox` = ? AND `sender` = ?"); 2442 } 2443 return SQLManager.this.connection.prepareStatement( 2444 "DELETE FROM `" + SQLManager.this.prefix 2445 + "plot_comments` WHERE `comment` = ? AND `inbox` = ? AND `sender` = ?"); 2446 } 2447 }); 2448 } 2449 2450 @Override 2451 public void clearInbox(final Plot plot, final String inbox) { 2452 addPlotTask(plot, new UniqueStatement("clearInbox") { 2453 @Override 2454 public void set(PreparedStatement statement) throws SQLException { 2455 if (plot != null) { 2456 statement.setString(1, plot.getArea().toString()); 2457 statement.setInt(2, plot.getId().hashCode()); 2458 statement.setString(3, inbox); 2459 } else { 2460 statement.setString(1, inbox); 2461 } 2462 } 2463 2464 @Override 2465 public PreparedStatement get() throws SQLException { 2466 if (plot != null) { 2467 return SQLManager.this.connection.prepareStatement( 2468 "DELETE FROM `" + SQLManager.this.prefix 2469 + "plot_comments` WHERE `world` = ? AND `hashcode` = ? AND `inbox` = ?"); 2470 } 2471 return SQLManager.this.connection.prepareStatement( 2472 "DELETE FROM `" + SQLManager.this.prefix + "plot_comments` `inbox` = ?"); 2473 } 2474 }); 2475 } 2476 2477 @Override 2478 public void getComments( 2479 @NonNull Plot plot, final String inbox, 2480 final RunnableVal<List<PlotComment>> whenDone 2481 ) { 2482 addPlotTask(plot, new UniqueStatement("getComments_" + plot) { 2483 @Override 2484 public void set(PreparedStatement statement) throws SQLException { 2485 if (plot != null) { 2486 statement.setString(1, plot.getArea().toString()); 2487 statement.setInt(2, plot.getId().hashCode()); 2488 statement.setString(3, inbox); 2489 } else { 2490 statement.setString(1, inbox); 2491 } 2492 } 2493 2494 @Override 2495 public PreparedStatement get() throws SQLException { 2496 if (plot != null) { 2497 return SQLManager.this.connection.prepareStatement( 2498 "SELECT * FROM `" + SQLManager.this.prefix 2499 + "plot_comments` WHERE `world` = ? AND `hashcode` = ? AND `inbox` = ?"); 2500 } 2501 return SQLManager.this.connection.prepareStatement( 2502 "SELECT * FROM `" + SQLManager.this.prefix 2503 + "plot_comments` WHERE `inbox` = ?"); 2504 } 2505 2506 @Override 2507 public void execute(PreparedStatement statement) { 2508 } 2509 2510 @Override 2511 public void addBatch(PreparedStatement statement) throws SQLException { 2512 ArrayList<PlotComment> comments = new ArrayList<>(); 2513 try (ResultSet set = statement.executeQuery()) { 2514 while (set.next()) { 2515 String sender = set.getString("sender"); 2516 String world = set.getString("world"); 2517 int hash = set.getInt("hashcode"); 2518 PlotId id; 2519 if (hash != 0) { 2520 id = PlotId.unpair(hash); 2521 } else { 2522 id = null; 2523 } 2524 String msg = set.getString("comment"); 2525 long timestamp = set.getInt("timestamp") * 1000; 2526 PlotComment comment = 2527 new PlotComment(world, id, msg, sender, inbox, timestamp); 2528 comments.add(comment); 2529 } 2530 whenDone.value = comments; 2531 } 2532 TaskManager.runTask(whenDone); 2533 } 2534 }); 2535 } 2536 2537 @Override 2538 public void setComment(final Plot plot, final PlotComment comment) { 2539 addPlotTask(plot, new UniqueStatement("setComment") { 2540 @Override 2541 public void set(PreparedStatement statement) throws SQLException { 2542 statement.setString(1, plot.getArea().toString()); 2543 statement.setInt(2, plot.getId().hashCode()); 2544 statement.setString(3, comment.comment()); 2545 statement.setString(4, comment.inbox()); 2546 statement.setInt(5, (int) (comment.timestamp() / 1000)); 2547 statement.setString(6, comment.senderName()); 2548 } 2549 2550 @Override 2551 public PreparedStatement get() throws SQLException { 2552 return SQLManager.this.connection.prepareStatement( 2553 "INSERT INTO `" + SQLManager.this.prefix 2554 + "plot_comments` (`world`, `hashcode`, `comment`, `inbox`, `timestamp`, `sender`) VALUES(?,?,?,?,?,?)"); 2555 } 2556 }); 2557 } 2558 2559 @Override 2560 public void removeTrusted(final Plot plot, final UUID uuid) { 2561 addPlotTask(plot, new UniqueStatement("removeTrusted") { 2562 @Override 2563 public void set(PreparedStatement statement) throws SQLException { 2564 statement.setInt(1, getId(plot)); 2565 statement.setString(2, uuid.toString()); 2566 } 2567 2568 @Override 2569 public PreparedStatement get() throws SQLException { 2570 return SQLManager.this.connection.prepareStatement( 2571 "DELETE FROM `" + SQLManager.this.prefix 2572 + "plot_helpers` WHERE `plot_plot_id` = ? AND `user_uuid` = ?"); 2573 } 2574 }); 2575 } 2576 2577 @Override 2578 public void removeMember(final Plot plot, final UUID uuid) { 2579 addPlotTask(plot, new UniqueStatement("removeMember") { 2580 @Override 2581 public void set(PreparedStatement statement) throws SQLException { 2582 statement.setInt(1, getId(plot)); 2583 statement.setString(2, uuid.toString()); 2584 } 2585 2586 @Override 2587 public PreparedStatement get() throws SQLException { 2588 return SQLManager.this.connection.prepareStatement( 2589 "DELETE FROM `" + SQLManager.this.prefix 2590 + "plot_trusted` WHERE `plot_plot_id` = ? AND `user_uuid` = ?"); 2591 } 2592 }); 2593 } 2594 2595 @Override 2596 public void setTrusted(final Plot plot, final UUID uuid) { 2597 addPlotTask(plot, new UniqueStatement("setTrusted") { 2598 @Override 2599 public void set(PreparedStatement statement) throws SQLException { 2600 statement.setInt(1, getId(plot)); 2601 statement.setString(2, uuid.toString()); 2602 } 2603 2604 @Override 2605 public PreparedStatement get() throws SQLException { 2606 return SQLManager.this.connection.prepareStatement( 2607 "INSERT INTO `" + SQLManager.this.prefix 2608 + "plot_helpers` (`plot_plot_id`, `user_uuid`) VALUES(?,?)"); 2609 } 2610 }); 2611 } 2612 2613 @Override 2614 public void setMember(final Plot plot, final UUID uuid) { 2615 addPlotTask(plot, new UniqueStatement("setMember") { 2616 @Override 2617 public void set(PreparedStatement statement) throws SQLException { 2618 statement.setInt(1, getId(plot)); 2619 statement.setString(2, uuid.toString()); 2620 } 2621 2622 @Override 2623 public PreparedStatement get() throws SQLException { 2624 return SQLManager.this.connection.prepareStatement( 2625 "INSERT INTO `" + SQLManager.this.prefix 2626 + "plot_trusted` (`plot_plot_id`, `user_uuid`) VALUES(?,?)"); 2627 } 2628 }); 2629 } 2630 2631 @Override 2632 public void removeDenied(final Plot plot, final UUID uuid) { 2633 addPlotTask(plot, new UniqueStatement("removeDenied") { 2634 @Override 2635 public void set(PreparedStatement statement) throws SQLException { 2636 statement.setInt(1, getId(plot)); 2637 statement.setString(2, uuid.toString()); 2638 } 2639 2640 @Override 2641 public PreparedStatement get() throws SQLException { 2642 return SQLManager.this.connection.prepareStatement( 2643 "DELETE FROM `" + SQLManager.this.prefix 2644 + "plot_denied` WHERE `plot_plot_id` = ? AND `user_uuid` = ?"); 2645 } 2646 }); 2647 } 2648 2649 @Override 2650 public void setDenied(final Plot plot, final UUID uuid) { 2651 addPlotTask(plot, new UniqueStatement("setDenied") { 2652 @Override 2653 public void set(PreparedStatement statement) throws SQLException { 2654 statement.setInt(1, getId(plot)); 2655 statement.setString(2, uuid.toString()); 2656 } 2657 2658 @Override 2659 public PreparedStatement get() throws SQLException { 2660 return SQLManager.this.connection.prepareStatement( 2661 "INSERT INTO `" + SQLManager.this.prefix 2662 + "plot_denied` (`plot_plot_id`, `user_uuid`) VALUES(?,?)"); 2663 } 2664 }); 2665 } 2666 2667 @Override 2668 public HashMap<UUID, Integer> getRatings(Plot plot) { 2669 HashMap<UUID, Integer> map = new HashMap<>(); 2670 try (PreparedStatement statement = this.connection.prepareStatement( 2671 "SELECT `rating`, `player` FROM `" + this.prefix 2672 + "plot_rating` WHERE `plot_plot_id` = ? ")) { 2673 statement.setInt(1, getId(plot)); 2674 try (ResultSet resultSet = statement.executeQuery()) { 2675 while (resultSet.next()) { 2676 UUID uuid = UUID.fromString(resultSet.getString("player")); 2677 int rating = resultSet.getInt("rating"); 2678 map.put(uuid, rating); 2679 } 2680 } 2681 } catch (SQLException e) { 2682 LOGGER.error("Failed to fetch rating for plot {}", plot.getId().toString()); 2683 e.printStackTrace(); 2684 } 2685 return map; 2686 } 2687 2688 @Override 2689 public void setRating(final Plot plot, final UUID rater, final int value) { 2690 addPlotTask(plot, new UniqueStatement("setRating") { 2691 @Override 2692 public void set(PreparedStatement statement) throws SQLException { 2693 statement.setInt(1, getId(plot)); 2694 statement.setInt(2, value); 2695 statement.setString(3, rater.toString()); 2696 } 2697 2698 @Override 2699 public PreparedStatement get() throws SQLException { 2700 return SQLManager.this.connection.prepareStatement( 2701 "INSERT INTO `" + SQLManager.this.prefix 2702 + "plot_rating` (`plot_plot_id`, `rating`, `player`) VALUES(?,?,?)"); 2703 } 2704 }); 2705 } 2706 2707 @Override 2708 public void delete(PlotCluster cluster) { 2709 final int id = getClusterId(cluster); 2710 addClusterTask(cluster, new UniqueStatement("delete_cluster_settings") { 2711 @Override 2712 public void set(PreparedStatement statement) throws SQLException { 2713 statement.setInt(1, id); 2714 } 2715 2716 @Override 2717 public PreparedStatement get() throws SQLException { 2718 return SQLManager.this.connection.prepareStatement( 2719 "DELETE FROM `" + SQLManager.this.prefix 2720 + "cluster_settings` WHERE `cluster_id` = ?"); 2721 } 2722 }); 2723 addClusterTask(cluster, new UniqueStatement("delete_cluster_helpers") { 2724 @Override 2725 public void set(PreparedStatement statement) throws SQLException { 2726 statement.setInt(1, id); 2727 } 2728 2729 @Override 2730 public PreparedStatement get() throws SQLException { 2731 return SQLManager.this.connection.prepareStatement( 2732 "DELETE FROM `" + SQLManager.this.prefix 2733 + "cluster_helpers` WHERE `cluster_id` = ?"); 2734 } 2735 }); 2736 addClusterTask(cluster, new UniqueStatement("delete_cluster_invited") { 2737 @Override 2738 public void set(PreparedStatement statement) throws SQLException { 2739 statement.setInt(1, id); 2740 } 2741 2742 @Override 2743 public PreparedStatement get() throws SQLException { 2744 return SQLManager.this.connection.prepareStatement( 2745 "DELETE FROM `" + SQLManager.this.prefix 2746 + "cluster_invited` WHERE `cluster_id` = ?"); 2747 } 2748 }); 2749 addClusterTask(cluster, new UniqueStatement("delete_cluster") { 2750 @Override 2751 public void set(PreparedStatement statement) throws SQLException { 2752 statement.setInt(1, id); 2753 } 2754 2755 @Override 2756 public PreparedStatement get() throws SQLException { 2757 return SQLManager.this.connection.prepareStatement( 2758 "DELETE FROM `" + SQLManager.this.prefix + "cluster` WHERE `id` = ?"); 2759 } 2760 }); 2761 } 2762 2763 @Override 2764 public void addPersistentMeta( 2765 final UUID uuid, final String key, final byte[] meta, 2766 final boolean replace 2767 ) { 2768 addPlayerTask(uuid, new UniqueStatement("addPersistentMeta") { 2769 @Override 2770 public void set(PreparedStatement statement) throws SQLException { 2771 if (replace) { 2772 statement.setBytes(1, meta); 2773 statement.setString(2, uuid.toString()); 2774 statement.setString(3, key); 2775 } else { 2776 statement.setString(1, uuid.toString()); 2777 statement.setString(2, key); 2778 statement.setBytes(3, meta); 2779 } 2780 } 2781 2782 @Override 2783 public PreparedStatement get() throws SQLException { 2784 if (replace) { 2785 return SQLManager.this.connection.prepareStatement( 2786 "UPDATE `" + SQLManager.this.prefix 2787 + "player_meta` SET `value` = ? WHERE `uuid` = ? AND `key` = ?"); 2788 } else { 2789 return SQLManager.this.connection.prepareStatement( 2790 "INSERT INTO `" + SQLManager.this.prefix 2791 + "player_meta`(`uuid`, `key`, `value`) VALUES(?, ? ,?)"); 2792 } 2793 } 2794 }); 2795 } 2796 2797 @Override 2798 public void removePersistentMeta(final UUID uuid, final String key) { 2799 addPlayerTask(uuid, new UniqueStatement("removePersistentMeta") { 2800 @Override 2801 public void set(PreparedStatement statement) throws SQLException { 2802 statement.setString(1, uuid.toString()); 2803 statement.setString(2, key); 2804 } 2805 2806 @Override 2807 public PreparedStatement get() throws SQLException { 2808 return SQLManager.this.connection.prepareStatement( 2809 "DELETE FROM `" + SQLManager.this.prefix 2810 + "player_meta` WHERE `uuid` = ? AND `key` = ?"); 2811 } 2812 }); 2813 } 2814 2815 @Override 2816 public void getPersistentMeta(final UUID uuid, final RunnableVal<Map<String, byte[]>> result) { 2817 addPlayerTask(uuid, new UniqueStatement("getPersistentMeta") { 2818 @Override 2819 public void set(PreparedStatement statement) throws SQLException { 2820 statement.setString(1, uuid.toString()); 2821 } 2822 2823 @Override 2824 public PreparedStatement get() throws SQLException { 2825 return SQLManager.this.connection.prepareStatement( 2826 "SELECT * FROM `" + SQLManager.this.prefix 2827 + "player_meta` WHERE `uuid` = ? ORDER BY `meta_id` ASC"); 2828 } 2829 2830 @Override 2831 public void execute(PreparedStatement statement) { 2832 } 2833 2834 @Override 2835 public void addBatch(PreparedStatement statement) throws SQLException { 2836 ResultSet resultSet = statement.executeQuery(); 2837 2838 final Map<String, byte[]> metaMap = new HashMap<>(); 2839 2840 while (resultSet.next()) { 2841 String key = resultSet.getString("key"); 2842 byte[] bytes = resultSet.getBytes("value"); 2843 metaMap.put(key, bytes); 2844 } 2845 2846 resultSet.close(); 2847 TaskManager.runTaskAsync(() -> result.run(metaMap)); 2848 } 2849 2850 }); 2851 } 2852 2853 @Override 2854 public HashMap<String, Set<PlotCluster>> getClusters() { 2855 LinkedHashMap<String, Set<PlotCluster>> newClusters = new LinkedHashMap<>(); 2856 HashMap<Integer, PlotCluster> clusters = new HashMap<>(); 2857 try { 2858 HashSet<String> areas = new HashSet<>(); 2859 if (this.worldConfiguration.contains("worlds")) { 2860 ConfigurationSection worldSection = this.worldConfiguration.getConfigurationSection("worlds"); 2861 if (worldSection != null) { 2862 for (String worldKey : worldSection.getKeys(false)) { 2863 areas.add(worldKey); 2864 ConfigurationSection areaSection = 2865 worldSection.getConfigurationSection(worldKey + ".areas"); 2866 if (areaSection != null) { 2867 for (String areaKey : areaSection.getKeys(false)) { 2868 String[] split = areaKey.split("(?<![;])-"); 2869 if (split.length == 3) { 2870 areas.add(worldKey + ';' + split[0]); 2871 } 2872 } 2873 } 2874 } 2875 } 2876 } 2877 HashMap<String, UUID> uuids = new HashMap<>(); 2878 HashMap<String, Integer> noExist = new HashMap<>(); 2879 /* 2880 * Getting clusters 2881 */ 2882 try (Statement stmt = this.connection.createStatement()) { 2883 ResultSet resultSet = 2884 stmt.executeQuery("SELECT * FROM `" + this.prefix + "cluster`"); 2885 PlotCluster cluster; 2886 String owner; 2887 UUID user; 2888 int id; 2889 while (resultSet.next()) { 2890 PlotId pos1 = 2891 PlotId.of(resultSet.getInt("pos1_x"), resultSet.getInt("pos1_z")); 2892 PlotId pos2 = 2893 PlotId.of(resultSet.getInt("pos2_x"), resultSet.getInt("pos2_z")); 2894 id = resultSet.getInt("id"); 2895 String areaid = resultSet.getString("world"); 2896 if (!areas.contains(areaid)) { 2897 noExist.merge(areaid, 1, Integer::sum); 2898 } 2899 owner = resultSet.getString("owner"); 2900 user = uuids.get(owner); 2901 if (user == null) { 2902 user = UUID.fromString(owner); 2903 uuids.put(owner, user); 2904 } 2905 cluster = new PlotCluster(null, pos1, pos2, user, id); 2906 clusters.put(id, cluster); 2907 Set<PlotCluster> set = 2908 newClusters.computeIfAbsent(areaid, k -> new HashSet<>()); 2909 set.add(cluster); 2910 } 2911 //Getting helpers 2912 resultSet = stmt.executeQuery( 2913 "SELECT `user_uuid`, `cluster_id` FROM `" + this.prefix + "cluster_helpers`"); 2914 while (resultSet.next()) { 2915 id = resultSet.getInt("cluster_id"); 2916 owner = resultSet.getString("user_uuid"); 2917 user = uuids.get(owner); 2918 if (user == null) { 2919 user = UUID.fromString(owner); 2920 uuids.put(owner, user); 2921 } 2922 cluster = clusters.get(id); 2923 if (cluster != null) { 2924 cluster.helpers.add(user); 2925 } else { 2926 LOGGER.warn("Cluster #{}({}) in cluster_helpers does not exist." 2927 + " Please create the cluster or remove this entry", id, cluster); 2928 } 2929 } 2930 // Getting invited 2931 resultSet = stmt.executeQuery( 2932 "SELECT `user_uuid`, `cluster_id` FROM `" + this.prefix + "cluster_invited`"); 2933 while (resultSet.next()) { 2934 id = resultSet.getInt("cluster_id"); 2935 owner = resultSet.getString("user_uuid"); 2936 user = uuids.get(owner); 2937 if (user == null) { 2938 user = UUID.fromString(owner); 2939 uuids.put(owner, user); 2940 } 2941 cluster = clusters.get(id); 2942 if (cluster != null) { 2943 cluster.invited.add(user); 2944 } else { 2945 LOGGER.warn("Cluster #{}({}) in cluster_helpers does not exist." 2946 + " Please create the cluster or remove this entry", id, cluster); 2947 } 2948 } 2949 resultSet = 2950 stmt.executeQuery("SELECT * FROM `" + this.prefix + "cluster_settings`"); 2951 while (resultSet.next()) { 2952 id = resultSet.getInt("cluster_id"); 2953 cluster = clusters.get(id); 2954 if (cluster != null) { 2955 String alias = resultSet.getString("alias"); 2956 if (alias != null) { 2957 cluster.settings.setAlias(alias); 2958 } 2959 String pos = resultSet.getString("position"); 2960 switch (pos.toLowerCase()) { 2961 case "": 2962 case "default": 2963 case "0,0,0": 2964 case "center": 2965 case "centre": 2966 break; 2967 default: 2968 try { 2969 BlockLoc loc = BlockLoc.fromString(pos); 2970 cluster.settings.setPosition(loc); 2971 } catch (Exception ignored) { 2972 } 2973 } 2974 int m = resultSet.getInt("merged"); 2975 boolean[] merged = new boolean[4]; 2976 for (int i = 0; i < 4; i++) { 2977 merged[3 - i] = (m & 1 << i) != 0; 2978 } 2979 cluster.settings.setMerged(merged); 2980 } else { 2981 LOGGER.warn("Cluster #{}({}) in cluster_helpers does not exist." 2982 + " Please create the cluster or remove this entry", id, cluster); 2983 } 2984 } 2985 resultSet.close(); 2986 } 2987 boolean invalidPlot = false; 2988 for (Entry<String, Integer> entry : noExist.entrySet()) { 2989 String a = entry.getKey(); 2990 invalidPlot = true; 2991 LOGGER.warn("Warning! Found {} clusters in DB for non existent area; '{}'", noExist.get(a), a); 2992 } 2993 if (invalidPlot) { 2994 LOGGER.warn("Warning! Please create the world(s) or remove the clusters using the purge command"); 2995 } 2996 } catch (SQLException e) { 2997 LOGGER.error("Failed to load clusters", e); 2998 } 2999 return newClusters; 3000 } 3001 3002 @Override 3003 public void setClusterName(final PlotCluster cluster, final String name) { 3004 addClusterTask(cluster, new UniqueStatement("setClusterName") { 3005 @Override 3006 public void set(PreparedStatement statement) throws SQLException { 3007 statement.setString(1, name); 3008 statement.setInt(2, getClusterId(cluster)); 3009 } 3010 3011 @Override 3012 public PreparedStatement get() throws SQLException { 3013 return SQLManager.this.connection.prepareStatement( 3014 "UPDATE `" + SQLManager.this.prefix 3015 + "cluster_settings` SET `alias` = ? WHERE `cluster_id` = ?"); 3016 } 3017 }); 3018 cluster.settings.setAlias(name); 3019 } 3020 3021 @Override 3022 public void removeHelper(final PlotCluster cluster, final UUID uuid) { 3023 addClusterTask(cluster, new UniqueStatement("removeHelper") { 3024 @Override 3025 public void set(PreparedStatement statement) throws SQLException { 3026 statement.setInt(1, getClusterId(cluster)); 3027 statement.setString(2, uuid.toString()); 3028 } 3029 3030 @Override 3031 public PreparedStatement get() throws SQLException { 3032 return SQLManager.this.connection.prepareStatement( 3033 "DELETE FROM `" + SQLManager.this.prefix 3034 + "cluster_helpers` WHERE `cluster_id` = ? AND `user_uuid` = ?"); 3035 } 3036 }); 3037 } 3038 3039 @Override 3040 public void setHelper(final PlotCluster cluster, final UUID uuid) { 3041 addClusterTask(cluster, new UniqueStatement("setHelper") { 3042 @Override 3043 public void set(PreparedStatement statement) throws SQLException { 3044 statement.setInt(1, getClusterId(cluster)); 3045 statement.setString(2, uuid.toString()); 3046 } 3047 3048 @Override 3049 public PreparedStatement get() throws SQLException { 3050 return SQLManager.this.connection.prepareStatement( 3051 "INSERT INTO `" + SQLManager.this.prefix 3052 + "cluster_helpers` (`cluster_id`, `user_uuid`) VALUES(?,?)"); 3053 } 3054 }); 3055 } 3056 3057 @Override 3058 public void createCluster(final PlotCluster cluster) { 3059 addClusterTask(cluster, new UniqueStatement("createCluster_" + cluster.hashCode()) { 3060 @Override 3061 public void set(PreparedStatement statement) throws SQLException { 3062 statement.setInt(1, cluster.getP1().getX()); 3063 statement.setInt(2, cluster.getP1().getY()); 3064 statement.setInt(3, cluster.getP2().getX()); 3065 statement.setInt(4, cluster.getP2().getY()); 3066 statement.setString(5, cluster.owner.toString()); 3067 statement.setString(6, cluster.area.toString()); 3068 } 3069 3070 @Override 3071 public PreparedStatement get() throws SQLException { 3072 return SQLManager.this.connection.prepareStatement( 3073 SQLManager.this.CREATE_CLUSTER, 3074 Statement.RETURN_GENERATED_KEYS 3075 ); 3076 } 3077 3078 @Override 3079 public void execute(PreparedStatement statement) { 3080 } 3081 3082 @Override 3083 public void addBatch(PreparedStatement statement) throws SQLException { 3084 statement.execute(); 3085 try (ResultSet keys = supportsGetGeneratedKeys ? statement.getGeneratedKeys() : statement.getResultSet()) { 3086 if (keys.next()) { 3087 cluster.temp = keys.getInt(1); 3088 } 3089 } 3090 } 3091 }); 3092 addClusterTask( 3093 cluster, 3094 new UniqueStatement("createCluster_settings_" + cluster.hashCode()) { 3095 @Override 3096 public void set(PreparedStatement statement) throws SQLException { 3097 statement.setInt(1, getClusterId(cluster)); 3098 statement.setString(2, cluster.settings.getAlias()); 3099 } 3100 3101 @Override 3102 public PreparedStatement get() throws SQLException { 3103 return SQLManager.this.connection.prepareStatement( 3104 "INSERT INTO `" + SQLManager.this.prefix 3105 + "cluster_settings`(`cluster_id`, `alias`) VALUES(?, ?)"); 3106 } 3107 } 3108 ); 3109 } 3110 3111 @Override 3112 public void resizeCluster(final PlotCluster current, PlotId min, PlotId max) { 3113 final PlotId pos1 = PlotId.of(current.getP1().getX(), current.getP1().getY()); 3114 final PlotId pos2 = PlotId.of(current.getP2().getX(), current.getP2().getY()); 3115 current.setP1(min); 3116 current.setP2(max); 3117 3118 addClusterTask(current, new UniqueStatement("resizeCluster") { 3119 @Override 3120 public void set(PreparedStatement statement) throws SQLException { 3121 statement.setInt(1, pos1.getX()); 3122 statement.setInt(2, pos1.getY()); 3123 statement.setInt(3, pos2.getX()); 3124 statement.setInt(4, pos2.getY()); 3125 statement.setInt(5, getClusterId(current)); 3126 } 3127 3128 @Override 3129 public PreparedStatement get() throws SQLException { 3130 return SQLManager.this.connection.prepareStatement( 3131 "UPDATE `" + SQLManager.this.prefix 3132 + "cluster` SET `pos1_x` = ?, `pos1_z` = ?, `pos2_x` = ?, `pos2_z` = ? WHERE `id` = ?"); 3133 } 3134 }); 3135 } 3136 3137 @Override 3138 public void setPosition(final PlotCluster cluster, final String position) { 3139 addClusterTask(cluster, new UniqueStatement("setPosition") { 3140 @Override 3141 public void set(PreparedStatement statement) throws SQLException { 3142 statement.setString(1, position); 3143 statement.setInt(2, getClusterId(cluster)); 3144 } 3145 3146 @Override 3147 public PreparedStatement get() throws SQLException { 3148 return SQLManager.this.connection.prepareStatement( 3149 "UPDATE `" + SQLManager.this.prefix 3150 + "cluster_settings` SET `position` = ? WHERE `cluster_id` = ?"); 3151 } 3152 }); 3153 } 3154 3155 @Override 3156 public void removeInvited(final PlotCluster cluster, final UUID uuid) { 3157 addClusterTask(cluster, new UniqueStatement("removeInvited") { 3158 @Override 3159 public void set(PreparedStatement statement) throws SQLException { 3160 statement.setInt(1, getClusterId(cluster)); 3161 statement.setString(2, uuid.toString()); 3162 } 3163 3164 @Override 3165 public PreparedStatement get() throws SQLException { 3166 return SQLManager.this.connection.prepareStatement( 3167 "DELETE FROM `" + SQLManager.this.prefix 3168 + "cluster_invited` WHERE `cluster_id` = ? AND `user_uuid` = ?"); 3169 } 3170 }); 3171 } 3172 3173 @Override 3174 public void setInvited(final PlotCluster cluster, final UUID uuid) { 3175 addClusterTask(cluster, new UniqueStatement("setInvited") { 3176 @Override 3177 public void set(PreparedStatement statement) throws SQLException { 3178 statement.setInt(1, getClusterId(cluster)); 3179 statement.setString(2, uuid.toString()); 3180 } 3181 3182 @Override 3183 public PreparedStatement get() throws SQLException { 3184 return SQLManager.this.connection.prepareStatement( 3185 "INSERT INTO `" + SQLManager.this.prefix 3186 + "cluster_invited` (`cluster_id`, `user_uuid`) VALUES(?,?)"); 3187 } 3188 }); 3189 } 3190 3191 @Override 3192 public boolean deleteTables() { 3193 try (Statement stmt = this.connection.createStatement(); 3194 PreparedStatement statement = this.connection 3195 .prepareStatement("DROP TABLE `" + this.prefix + "plot`")) { 3196 close(); 3197 this.closed = false; 3198 SQLManager.this.connection = this.database.forceConnection(); 3199 stmt.addBatch("DROP TABLE `" + this.prefix + "cluster_invited`"); 3200 stmt.addBatch("DROP TABLE `" + this.prefix + "cluster_helpers`"); 3201 stmt.addBatch("DROP TABLE `" + this.prefix + "cluster`"); 3202 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_rating`"); 3203 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_settings`"); 3204 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_comments`"); 3205 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_trusted`"); 3206 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_helpers`"); 3207 stmt.addBatch("DROP TABLE `" + this.prefix + "plot_denied`"); 3208 stmt.executeBatch(); 3209 stmt.clearBatch(); 3210 statement.executeUpdate(); 3211 } catch (ClassNotFoundException | SQLException e) { 3212 e.printStackTrace(); 3213 3214 } 3215 return true; 3216 } 3217 3218 @SuppressWarnings({"unchecked", "unused"}) 3219 @Override 3220 public void validateAllPlots(Set<Plot> toValidate) { 3221 if (!isValid()) { 3222 reconnect(); 3223 } 3224 LOGGER.info( 3225 "All DB transactions during this session are being validated (This may take a while if corrections need to be made)"); 3226 commit(); 3227 while (true) { 3228 if (!sendBatch()) { 3229 break; 3230 } 3231 } 3232 try { 3233 if (this.connection.getAutoCommit()) { 3234 this.connection.setAutoCommit(false); 3235 } 3236 } catch (SQLException e) { 3237 e.printStackTrace(); 3238 } 3239 HashMap<String, HashMap<PlotId, Plot>> database = getPlots(); 3240 ArrayList<Plot> toCreate = new ArrayList<>(); 3241 for (Plot plot : toValidate) { 3242 if (plot.temp == -1) { 3243 continue; 3244 } 3245 if (plot.getArea() == null) { 3246 LOGGER.error("CRITICAL ERROR IN VALIDATION TASK: {}", plot); 3247 LOGGER.error("PLOT AREA CANNOT BE NULL! SKIPPING PLOT!"); 3248 LOGGER.info("Delete this entry from your database or set `database-purger: true` in the settings.yml"); 3249 continue; 3250 } 3251 if (database == null) { 3252 LOGGER.error("CRITICAL ERROR IN VALIDATION TASK!"); 3253 LOGGER.error("DATABASE VARIABLE CANNOT BE NULL! NOW ENDING VALIDATION!"); 3254 break; 3255 } 3256 HashMap<PlotId, Plot> worldPlots = database.get(plot.getArea().toString()); 3257 if (worldPlots == null) { 3258 toCreate.add(plot); 3259 continue; 3260 } 3261 Plot dataPlot = worldPlots.remove(plot.getId()); 3262 if (dataPlot == null) { 3263 toCreate.add(plot); 3264 continue; 3265 } 3266 // owner 3267 if (!plot.getOwnerAbs().equals(dataPlot.getOwnerAbs())) { 3268 setOwner(plot, plot.getOwnerAbs()); 3269 } 3270 // trusted 3271 if (!plot.getTrusted().equals(dataPlot.getTrusted())) { 3272 HashSet<UUID> toAdd = (HashSet<UUID>) plot.getTrusted().clone(); 3273 HashSet<UUID> toRemove = (HashSet<UUID>) dataPlot.getTrusted().clone(); 3274 toRemove.removeAll(plot.getTrusted()); 3275 toAdd.removeAll(dataPlot.getTrusted()); 3276 if (!toRemove.isEmpty()) { 3277 for (UUID uuid : toRemove) { 3278 removeTrusted(plot, uuid); 3279 } 3280 } 3281 if (!toAdd.isEmpty()) { 3282 for (UUID uuid : toAdd) { 3283 setTrusted(plot, uuid); 3284 } 3285 } 3286 } 3287 if (!plot.getMembers().equals(dataPlot.getMembers())) { 3288 HashSet<UUID> toAdd = (HashSet<UUID>) plot.getMembers().clone(); 3289 HashSet<UUID> toRemove = (HashSet<UUID>) dataPlot.getMembers().clone(); 3290 toRemove.removeAll(plot.getMembers()); 3291 toAdd.removeAll(dataPlot.getMembers()); 3292 if (!toRemove.isEmpty()) { 3293 for (UUID uuid : toRemove) { 3294 removeMember(plot, uuid); 3295 } 3296 } 3297 if (!toAdd.isEmpty()) { 3298 for (UUID uuid : toAdd) { 3299 setMember(plot, uuid); 3300 } 3301 } 3302 } 3303 if (!plot.getDenied().equals(dataPlot.getDenied())) { 3304 HashSet<UUID> toAdd = (HashSet<UUID>) plot.getDenied().clone(); 3305 HashSet<UUID> toRemove = (HashSet<UUID>) dataPlot.getDenied().clone(); 3306 toRemove.removeAll(plot.getDenied()); 3307 toAdd.removeAll(dataPlot.getDenied()); 3308 if (!toRemove.isEmpty()) { 3309 for (UUID uuid : toRemove) { 3310 removeDenied(plot, uuid); 3311 } 3312 } 3313 if (!toAdd.isEmpty()) { 3314 for (UUID uuid : toAdd) { 3315 setDenied(plot, uuid); 3316 } 3317 } 3318 } 3319 boolean[] pm = plot.getMerged(); 3320 boolean[] dm = dataPlot.getMerged(); 3321 if (pm[0] != dm[0] || pm[1] != dm[1]) { 3322 setMerged(dataPlot, plot.getMerged()); 3323 } 3324 Set<PlotFlag<?, ?>> pf = plot.getFlags(); 3325 Set<PlotFlag<?, ?>> df = dataPlot.getFlags(); 3326 if (!pf.isEmpty() && !df.isEmpty()) { 3327 if (pf.size() != df.size() || !StringMan 3328 .isEqual(StringMan.joinOrdered(pf, ","), StringMan.joinOrdered(df, ","))) { 3329 // setFlags(plot, pf); 3330 // TODO: Re-implement 3331 } 3332 } 3333 } 3334 3335 for (Entry<String, HashMap<PlotId, Plot>> entry : database.entrySet()) { 3336 HashMap<PlotId, Plot> map = entry.getValue(); 3337 if (!map.isEmpty()) { 3338 for (Entry<PlotId, Plot> entry2 : map.entrySet()) { 3339 // TODO implement this when sure safe" 3340 } 3341 } 3342 } 3343 commit(); 3344 } 3345 3346 @Override 3347 public void replaceWorld( 3348 final String oldWorld, final String newWorld, final PlotId min, 3349 final PlotId max 3350 ) { 3351 addGlobalTask(() -> { 3352 if (min == null) { 3353 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 3354 "UPDATE `" + SQLManager.this.prefix 3355 + "plot` SET `world` = ? WHERE `world` = ?")) { 3356 stmt.setString(1, newWorld); 3357 stmt.setString(2, oldWorld); 3358 stmt.executeUpdate(); 3359 } catch (SQLException e) { 3360 e.printStackTrace(); 3361 } 3362 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 3363 "UPDATE `" + SQLManager.this.prefix 3364 + "cluster` SET `world` = ? WHERE `world` = ?")) { 3365 stmt.setString(1, newWorld); 3366 stmt.setString(2, oldWorld); 3367 stmt.executeUpdate(); 3368 } catch (SQLException e) { 3369 e.printStackTrace(); 3370 } 3371 } else { 3372 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 3373 "UPDATE `" + SQLManager.this.prefix 3374 + "plot` SET `world` = ? WHERE `world` = ? AND `plot_id_x` BETWEEN ? AND ? AND `plot_id_z` BETWEEN ? AND ?")) { 3375 stmt.setString(1, newWorld); 3376 stmt.setString(2, oldWorld); 3377 stmt.setInt(3, min.getX()); 3378 stmt.setInt(4, max.getX()); 3379 stmt.setInt(5, min.getY()); 3380 stmt.setInt(6, max.getY()); 3381 stmt.executeUpdate(); 3382 } catch (SQLException e) { 3383 e.printStackTrace(); 3384 } 3385 try (PreparedStatement stmt = SQLManager.this.connection.prepareStatement( 3386 "UPDATE `" + SQLManager.this.prefix 3387 + "cluster` SET `world` = ? WHERE `world` = ? AND `pos1_x` <= ? AND `pos1_z` <= ? AND `pos2_x` >= ? AND `pos2_z` >= ?")) { 3388 stmt.setString(1, newWorld); 3389 stmt.setString(2, oldWorld); 3390 stmt.setInt(3, max.getX()); 3391 stmt.setInt(4, max.getY()); 3392 stmt.setInt(5, min.getX()); 3393 stmt.setInt(6, min.getY()); 3394 stmt.executeUpdate(); 3395 } catch (SQLException e) { 3396 e.printStackTrace(); 3397 } 3398 } 3399 }); 3400 } 3401 3402 @Override 3403 public void replaceUUID(final UUID old, final UUID now) { 3404 addGlobalTask(() -> { 3405 try (Statement stmt = SQLManager.this.connection.createStatement()) { 3406 stmt.executeUpdate( 3407 "UPDATE `" + SQLManager.this.prefix + "cluster` SET `owner` = '" + now 3408 .toString() + "' WHERE `owner` = '" + old.toString() + '\''); 3409 stmt.executeUpdate( 3410 "UPDATE `" + SQLManager.this.prefix + "cluster_helpers` SET `user_uuid` = '" 3411 + now + "' WHERE `user_uuid` = '" + old + '\''); 3412 stmt.executeUpdate( 3413 "UPDATE `" + SQLManager.this.prefix + "cluster_invited` SET `user_uuid` = '" 3414 + now + "' WHERE `user_uuid` = '" + old + '\''); 3415 stmt.executeUpdate( 3416 "UPDATE `" + SQLManager.this.prefix + "plot` SET `owner` = '" + now 3417 + "' WHERE `owner` = '" + old + '\''); 3418 stmt.executeUpdate( 3419 "UPDATE `" + SQLManager.this.prefix + "plot_denied` SET `user_uuid` = '" + now + "' WHERE `user_uuid` = '" + old + '\''); 3420 stmt.executeUpdate( 3421 "UPDATE `" + SQLManager.this.prefix + "plot_helpers` SET `user_uuid` = '" + now + "' WHERE `user_uuid` = '" + old + '\''); 3422 stmt.executeUpdate( 3423 "UPDATE `" + SQLManager.this.prefix + "plot_trusted` SET `user_uuid` = '" + now + "' WHERE `user_uuid` = '" + old + '\''); 3424 } catch (SQLException e) { 3425 e.printStackTrace(); 3426 } 3427 }); 3428 } 3429 3430 @Override 3431 public void close() { 3432 try { 3433 this.closed = true; 3434 this.connection.close(); 3435 } catch (SQLException e) { 3436 e.printStackTrace(); 3437 } 3438 } 3439 3440 private record LegacySettings( 3441 int id, 3442 PlotSettings settings 3443 ) { 3444 3445 } 3446 3447 public abstract static class UniqueStatement { 3448 3449 public final String method; 3450 3451 public UniqueStatement(String method) { 3452 this.method = method; 3453 } 3454 3455 public void addBatch(PreparedStatement statement) throws SQLException { 3456 statement.addBatch(); 3457 } 3458 3459 public void execute(PreparedStatement statement) throws SQLException { 3460 statement.executeBatch(); 3461 } 3462 3463 public abstract PreparedStatement get() throws SQLException; 3464 3465 public abstract void set(PreparedStatement statement) throws SQLException; 3466 3467 } 3468 3469 private record UUIDPair(int id, UUID uuid) { 3470 3471 } 3472 3473}