diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 024320cac..ba0477715 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -22,5 +22,7 @@ jobs: uses: actions/setup-java@v1 with: java-version: 1.8 + - name: Gradle Wrapper Validation + uses: gradle/wrapper-validation-action@v1 - name: Test with Gradle run: ./gradlew clean build diff --git a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/listeners/PlayerEvents.java b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/listeners/PlayerEvents.java index 5a6c7be5f..3f4867aef 100644 --- a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/listeners/PlayerEvents.java +++ b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/listeners/PlayerEvents.java @@ -7,6 +7,7 @@ import com.github.intellectualsites.plotsquared.bukkit.util.UpdateUtility; import com.github.intellectualsites.plotsquared.plot.PlotSquared; import com.github.intellectualsites.plotsquared.plot.config.Captions; import com.github.intellectualsites.plotsquared.plot.config.Settings; +import com.github.intellectualsites.plotsquared.plot.database.DBFunc; import com.github.intellectualsites.plotsquared.plot.flags.implementations.AnimalAttackFlag; import com.github.intellectualsites.plotsquared.plot.flags.implementations.AnimalCapFlag; import com.github.intellectualsites.plotsquared.plot.flags.implementations.AnimalInteractFlag; @@ -485,17 +486,19 @@ import java.util.regex.Pattern; return; } if (Settings.Redstone.DISABLE_OFFLINE) { - boolean disable; - if (plot.isMerged()) { - disable = true; - for (UUID owner : plot.getOwners()) { - if (UUIDHandler.getPlayer(owner) != null) { - disable = false; - break; + boolean disable = false; + if (!plot.getOwner().equals(DBFunc.SERVER)) { + if (plot.isMerged()) { + disable = true; + for (UUID owner : plot.getOwners()) { + if (UUIDHandler.getPlayer(owner) != null) { + disable = false; + break; + } } + } else { + disable = UUIDHandler.getPlayer(plot.guessOwner()) == null; } - } else { - disable = UUIDHandler.getPlayer(plot.guessOwner()) == null; } if (disable) { for (UUID trusted : plot.getTrusted()) { diff --git a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/BukkitUtil.java b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/BukkitUtil.java index b27c7564b..6dd860099 100644 --- a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/BukkitUtil.java +++ b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/BukkitUtil.java @@ -39,6 +39,7 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -46,6 +47,14 @@ import java.util.Set; @SuppressWarnings({"unused", "WeakerAccess"}) public class BukkitUtil extends WorldUtil { + private static Method biomeSetter; + static { + try { + biomeSetter = World.class.getMethod("setBiome", Integer.class, Integer.class, Biome.class); + } catch (final Exception ignored) { + } + } + private static String lastString = null; private static World lastWorld = null; @@ -425,7 +434,16 @@ import java.util.Set; for (int x = region.getMinimumPoint().getX(); x <= region.getMaximumPoint().getX(); x++) { for (int z = region.getMinimumPoint().getZ(); z <= region.getMaximumPoint().getZ(); z++) { for (int y = 0; y < world.getMaxHeight(); y++) { - world.setBiome(x, y, z, biome); + try { + if (biomeSetter != null) { + biomeSetter.invoke(world, x, z, biome); + } else { + world.setBiome(x, y, z, biome); + } + } catch (final Exception e) { + PlotSquared.log("An error occurred setting the biome:"); + e.printStackTrace(); + } } } } diff --git a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/block/GenChunk.java b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/block/GenChunk.java index a039f7206..ec656d2ba 100644 --- a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/block/GenChunk.java +++ b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/block/GenChunk.java @@ -7,6 +7,7 @@ import com.github.intellectualsites.plotsquared.plot.object.Location; import com.github.intellectualsites.plotsquared.plot.util.MainUtil; import com.github.intellectualsites.plotsquared.plot.util.block.ScopedLocalBlockQueue; import com.github.intellectualsites.plotsquared.plot.util.world.PatternUtil; +import com.google.common.base.Preconditions; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.world.biome.BiomeType; @@ -20,9 +21,9 @@ import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.generator.ChunkGenerator.BiomeGrid; import org.bukkit.generator.ChunkGenerator.ChunkData; +import org.jetbrains.annotations.NotNull; import java.util.Arrays; -import org.jetbrains.annotations.NotNull; public class GenChunk extends ScopedLocalBlockQueue { @@ -109,7 +110,7 @@ public class GenChunk extends ScopedLocalBlockQueue { } @Override public boolean setBlock(int x, int y, int z, @NotNull Pattern pattern) { - return setBlock(x, y, z, PatternUtil.apply(pattern, x, y, z)); + return setBlock(x, y, z, PatternUtil.apply(Preconditions.checkNotNull(pattern, "Pattern may not be null"), x, y, z)); } @Override public boolean setBlock(int x, int y, int z, BlockState id) { diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Comment.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Comment.java index 98d2406a1..f4b1c4b66 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Comment.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Comment.java @@ -2,9 +2,7 @@ package com.github.intellectualsites.plotsquared.plot.commands; import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; import com.github.intellectualsites.plotsquared.plot.config.Captions; -import com.github.intellectualsites.plotsquared.plot.object.Location; import com.github.intellectualsites.plotsquared.plot.object.Plot; -import com.github.intellectualsites.plotsquared.plot.object.PlotId; import com.github.intellectualsites.plotsquared.plot.object.PlotPlayer; import com.github.intellectualsites.plotsquared.plot.object.comment.CommentInbox; import com.github.intellectualsites.plotsquared.plot.object.comment.PlotComment; @@ -14,6 +12,7 @@ import com.github.intellectualsites.plotsquared.plot.util.StringMan; import com.github.intellectualsites.plotsquared.plot.util.UUIDHandler; import java.util.Arrays; +import java.util.Locale; import java.util.Map.Entry; @CommandDeclaration(command = "comment", @@ -30,40 +29,41 @@ public class Comment extends SubCommand { StringMan.join(CommentManager.inboxes.keySet(), "|")); return false; } - CommentInbox inbox = CommentManager.inboxes.get(args[0].toLowerCase()); - if (inbox == null) { - sendMessage(player, Captions.COMMENT_SYNTAX, - StringMan.join(CommentManager.inboxes.keySet(), "|")); - return false; + + // Attempt to extract a plot out of the first argument + Plot plot = null; + if (!CommentManager.inboxes.containsKey(args[0].toLowerCase(Locale.ENGLISH))) { + plot = MainUtil.getPlotFromString(player, args[0], false); } - Location location = player.getLocation(); - PlotId id; - try { - id = PlotId.fromString(args[1]); - } catch (IllegalArgumentException ignored) { - MainUtil.sendMessage(player, Captions.NOT_VALID_PLOT_ID); - return false; - } - Plot plot = MainUtil.getPlotFromString(player, args[1], false); + int index; if (plot == null) { index = 1; - plot = location.getPlotAbs(); + plot = player.getLocation().getPlotAbs(); } else { - if (args.length < 4) { + if (args.length < 3) { sendMessage(player, Captions.COMMENT_SYNTAX, StringMan.join(CommentManager.inboxes.keySet(), "|")); return false; } index = 2; } + + CommentInbox inbox = CommentManager.inboxes.get(args[index - 1].toLowerCase()); + if (inbox == null) { + sendMessage(player, Captions.COMMENT_SYNTAX, + StringMan.join(CommentManager.inboxes.keySet(), "|")); + return false; + } + if (!inbox.canWrite(plot, player)) { sendMessage(player, Captions.NO_PERM_INBOX, ""); return false; } + String message = StringMan.join(Arrays.copyOfRange(args, index, args.length), " "); PlotComment comment = - new PlotComment(location.getWorld(), id, message, player.getName(), inbox.toString(), + new PlotComment(player.getLocation().getWorld(), plot.getId(), message, player.getName(), inbox.toString(), System.currentTimeMillis()); boolean result = inbox.addComment(plot, comment); if (!result) { diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Condense.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Condense.java index 442c0808d..5714f42b5 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Condense.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Condense.java @@ -17,6 +17,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; @CommandDeclaration(command = "condense", @@ -135,13 +136,17 @@ public class Condense extends SubCommand { } i++; final AtomicBoolean result = new AtomicBoolean(false); - result.set(origin.move(possible, () -> { - if (result.get()) { - MainUtil.sendMessage(player, - "Moving: " + origin + " -> " + possible); - TaskManager.runTaskLater(task, 1); - } - }, false)); + try { + result.set(origin.move(possible, () -> { + if (result.get()) { + MainUtil.sendMessage(player, + "Moving: " + origin + " -> " + possible); + TaskManager.runTaskLater(task, 1); + } + }, false).get()); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } if (result.get()) { break; } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Deny.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Deny.java index e062197da..912188109 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Deny.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Deny.java @@ -77,6 +77,10 @@ public class Deny extends SubCommand { handleKick(UUIDHandler.getPlayer(uuid), plot); } else { for (PlotPlayer plotPlayer : plot.getPlayersInPlot()) { + // Ignore plot-owners + if (plot.isAdded(plotPlayer.getUUID())) { + continue; + } handleKick(plotPlayer, plot); } } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Move.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Move.java index bc6f885d7..a2d4f7735 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Move.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Move.java @@ -1,5 +1,6 @@ package com.github.intellectualsites.plotsquared.plot.commands; +import com.github.intellectualsites.plotsquared.commands.Command; import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; import com.github.intellectualsites.plotsquared.plot.PlotSquared; import com.github.intellectualsites.plotsquared.plot.config.Captions; @@ -7,9 +8,13 @@ import com.github.intellectualsites.plotsquared.plot.object.Location; import com.github.intellectualsites.plotsquared.plot.object.Plot; import com.github.intellectualsites.plotsquared.plot.object.PlotArea; import com.github.intellectualsites.plotsquared.plot.object.PlotPlayer; +import com.github.intellectualsites.plotsquared.plot.object.RunnableVal2; +import com.github.intellectualsites.plotsquared.plot.object.RunnableVal3; import com.github.intellectualsites.plotsquared.plot.util.MainUtil; import com.github.intellectualsites.plotsquared.plot.util.Permissions; +import java.util.concurrent.CompletableFuture; + @CommandDeclaration(usage = "/plot move ", command = "move", description = "Move a plot", @@ -18,16 +23,19 @@ import com.github.intellectualsites.plotsquared.plot.util.Permissions; requiredType = RequiredType.PLAYER) public class Move extends SubCommand { - @Override public boolean onCommand(final PlotPlayer player, String[] args) { + @Override public CompletableFuture execute(PlotPlayer player, String[] args, + RunnableVal3 confirm, + RunnableVal2 whenDone) { Location location = player.getLocation(); Plot plot1 = location.getPlotAbs(); if (plot1 == null) { - return !MainUtil.sendMessage(player, Captions.NOT_IN_PLOT); + return CompletableFuture + .completedFuture(!MainUtil.sendMessage(player, Captions.NOT_IN_PLOT)); } if (!plot1.isOwner(player.getUUID()) && !Permissions .hasPermission(player, Captions.PERMISSION_ADMIN.getTranslated())) { MainUtil.sendMessage(player, Captions.NO_PLOT_PERMS); - return false; + return CompletableFuture.completedFuture(false); } boolean override = false; if (args.length == 2 && args[1].equalsIgnoreCase("-f")) { @@ -36,14 +44,14 @@ public class Move extends SubCommand { } if (args.length != 1) { Captions.COMMAND_SYNTAX.send(player, getUsage()); - return false; + return CompletableFuture.completedFuture(false); } PlotArea area = PlotSquared.get().getPlotAreaByString(args[0]); Plot plot2; if (area == null) { plot2 = MainUtil.getPlotFromString(player, args[0], true); if (plot2 == null) { - return false; + return CompletableFuture.completedFuture(false); } } else { plot2 = area.getPlotAbs(plot1.getId()); @@ -51,19 +59,32 @@ public class Move extends SubCommand { if (plot1.equals(plot2)) { MainUtil.sendMessage(player, Captions.NOT_VALID_PLOT_ID); MainUtil.sendMessage(player, Captions.COMMAND_SYNTAX, "/plot copy "); - return false; + return CompletableFuture.completedFuture(false); } if (!plot1.getArea().isCompatible(plot2.getArea()) && (!override || !Permissions .hasPermission(player, Captions.PERMISSION_ADMIN.getTranslated()))) { Captions.PLOTWORLD_INCOMPATIBLE.send(player); - return false; + return CompletableFuture.completedFuture(false); } - if (plot1.move(plot2, () -> MainUtil.sendMessage(player, Captions.MOVE_SUCCESS), false)) { - return true; - } else { - MainUtil.sendMessage(player, Captions.REQUIRES_UNOWNED); - return false; + if (plot1.isMerged() || plot2.isMerged()) { + Captions.MOVE_MERGED.send(player); + return CompletableFuture.completedFuture(false); } + + return plot1.move(plot2, () -> {}, false) + .thenApply(result -> { + if (result) { + MainUtil.sendMessage(player, Captions.MOVE_SUCCESS); + return true; + } else { + MainUtil.sendMessage(player, Captions.REQUIRES_UNOWNED); + return false; + } + }); + } + + @Override public boolean onCommand(final PlotPlayer player, String[] args) { + return true; } } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/SchematicCmd.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/SchematicCmd.java index 6e2bf739c..5364a24f9 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/SchematicCmd.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/SchematicCmd.java @@ -68,6 +68,10 @@ public class SchematicCmd extends SubCommand { MainUtil.sendMessage(player, Captions.TASK_IN_PROCESS); return false; } + if (plot.isMerged()) { + MainUtil.sendMessage(player, Captions.SCHEMATIC_PASTE_MERGED); + return false; + } final String location = args[1]; this.running = true; TaskManager.runTaskAsync(() -> { diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Swap.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Swap.java index d942c069f..0aaea13dc 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Swap.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/commands/Swap.java @@ -1,13 +1,18 @@ package com.github.intellectualsites.plotsquared.plot.commands; +import com.github.intellectualsites.plotsquared.commands.Command; import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; import com.github.intellectualsites.plotsquared.plot.config.Captions; import com.github.intellectualsites.plotsquared.plot.object.Location; import com.github.intellectualsites.plotsquared.plot.object.Plot; import com.github.intellectualsites.plotsquared.plot.object.PlotPlayer; +import com.github.intellectualsites.plotsquared.plot.object.RunnableVal2; +import com.github.intellectualsites.plotsquared.plot.object.RunnableVal3; import com.github.intellectualsites.plotsquared.plot.util.MainUtil; import com.github.intellectualsites.plotsquared.plot.util.Permissions; +import java.util.concurrent.CompletableFuture; + @CommandDeclaration(usage = "/plot swap ", command = "swap", description = "Swap two plots", @@ -16,43 +21,54 @@ import com.github.intellectualsites.plotsquared.plot.util.Permissions; requiredType = RequiredType.PLAYER) public class Swap extends SubCommand { - @Override public boolean onCommand(final PlotPlayer player, String[] args) { + @Override public CompletableFuture execute(PlotPlayer player, String[] args, + RunnableVal3 confirm, + RunnableVal2 whenDone) { Location location = player.getLocation(); Plot plot1 = location.getPlotAbs(); if (plot1 == null) { - return !MainUtil.sendMessage(player, Captions.NOT_IN_PLOT); + return CompletableFuture.completedFuture(!MainUtil.sendMessage(player, Captions.NOT_IN_PLOT)); } if (!plot1.isOwner(player.getUUID()) && !Permissions .hasPermission(player, Captions.PERMISSION_ADMIN.getTranslated())) { MainUtil.sendMessage(player, Captions.NO_PLOT_PERMS); - return false; + return CompletableFuture.completedFuture(false); } if (args.length != 1) { Captions.COMMAND_SYNTAX.send(player, getUsage()); - return false; + return CompletableFuture.completedFuture(false); } Plot plot2 = MainUtil.getPlotFromString(player, args[0], true); if (plot2 == null) { - return false; + return CompletableFuture.completedFuture(false); } if (plot1.equals(plot2)) { MainUtil.sendMessage(player, Captions.NOT_VALID_PLOT_ID); MainUtil.sendMessage(player, Captions.COMMAND_SYNTAX, "/plot copy "); - return false; + return CompletableFuture.completedFuture(false); } if (!plot1.getArea().isCompatible(plot2.getArea())) { Captions.PLOTWORLD_INCOMPATIBLE.send(player); - return false; + return CompletableFuture.completedFuture(false); } - if (plot1.move(plot2, new Runnable() { - @Override public void run() { - MainUtil.sendMessage(player, Captions.SWAP_SUCCESS); - } - }, true)) { - return true; - } else { - MainUtil.sendMessage(player, Captions.SWAP_OVERLAP); - return false; + if (plot1.isMerged() || plot2.isMerged()) { + Captions.SWAP_MERGED.send(player); + return CompletableFuture.completedFuture(false); } + + return plot1.move(plot2, () -> {}, true) + .thenApply(result -> { + if (result) { + MainUtil.sendMessage(player, Captions.SWAP_SUCCESS); + return true; + } else { + MainUtil.sendMessage(player, Captions.SWAP_OVERLAP); + return false; + } + }); + } + + @Override public boolean onCommand(final PlotPlayer player, String[] args) { + return true; } } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Captions.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Captions.java index a801a7cf9..7bd22112f 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Captions.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Captions.java @@ -174,6 +174,7 @@ public enum Captions implements Caption { // // MOVE_SUCCESS("$4Successfully moved plot.", "Move"), + MOVE_MERGED("$2Merged plots may not be moved. Please unmerge the plot before performing the move.", "Move"), COPY_SUCCESS("$4Successfully copied plot.", "Move"), REQUIRES_UNOWNED("$2The location specified is already occupied.", "Move"), // @@ -234,6 +235,7 @@ public enum Captions implements Caption { SWAP_DIMENSIONS("$2The proposed areas must have comparable dimensions", "Swap"), SWAP_SYNTAX("$2/plot swap ", "Swap"), SWAP_SUCCESS("$4Successfully swapped plots", "Swap"), + SWAP_MERGED("$2Merged plots may not be swapped. Please unmerge the plot before performing the swap.", "Swap"), // // INBOX_NOTIFICATION("%s unread messages. Use /plot inbox", "Comment"), @@ -318,6 +320,7 @@ public enum Captions implements Caption { SCHEMATIC_MISSING_ARG("$2You need to specify an argument. Possible values: $1save$2, $1paste $2, $1exportall$2, $1list", "Schematics"), SCHEMATIC_INVALID("$2That is not a valid schematic. Reason: $2%s", "Schematics"), SCHEMATIC_VALID("$2That is a valid schematic", "Schematics"), + SCHEMATIC_PASTE_MERGED("$2Schematics cannot be pasted onto merged plots. Please unmerge the plot before performing the paste.", "Schematics"), SCHEMATIC_PASTE_FAILED("$2Failed to paste the schematic", "Schematics"), SCHEMATIC_PASTE_SUCCESS("$4The schematic pasted successfully", "Schematics"), SCHEMATIC_LIST("$4Saved Schematics: $1%s", "Schematics"), diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Config.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Config.java index 26c5050ce..ccbe210cc 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Config.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/Config.java @@ -3,6 +3,7 @@ package com.github.intellectualsites.plotsquared.plot.config; import com.github.intellectualsites.plotsquared.configuration.MemorySection; import com.github.intellectualsites.plotsquared.configuration.file.YamlConfiguration; import com.github.intellectualsites.plotsquared.plot.PlotSquared; +import com.github.intellectualsites.plotsquared.plot.config.Settings.Enabled_Components; import com.github.intellectualsites.plotsquared.plot.util.StringMan; import java.io.File; @@ -30,7 +31,7 @@ public class Config { * @param * @return */ - public static T get(String key, Class root) { + public static T get(String key, Class root) { String[] split = key.split("\\."); Object instance = getInstance(split, root); if (instance != null) { @@ -55,7 +56,7 @@ public class Config { * @param value value * @param root */ - public static void set(String key, Object value, Class root) { + public static void set(String key, Object value, Class root) { String[] split = key.split("\\."); Object instance = getInstance(split, root); if (instance != null) { @@ -81,7 +82,7 @@ public class Config { PlotSquared.debug("Failed to set config option: " + key + ": " + value + " | " + instance); } - public static boolean load(File file, Class root) { + public static boolean load(File file, Class root) { if (!file.exists()) { return false; } @@ -102,7 +103,7 @@ public class Config { * @param file * @param root */ - public static void save(File file, Class root) { + public static void save(File file, Class root) { try { if (!file.exists()) { file.getParentFile().mkdirs(); @@ -123,7 +124,7 @@ public class Config { * @param clazz * @return */ - public static Map getFields(Class clazz) { + public static Map getFields(Class clazz) { HashMap map = new HashMap<>(); for (Field field : clazz.getFields()) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { @@ -242,7 +243,7 @@ public class Config { * @param root * @return */ - private static Field getField(String[] split, Class root) { + private static Field getField(String[] split, Class root) { Object instance = getInstance(split, root); if (instance == null) { return null; @@ -278,7 +279,7 @@ public class Config { * @param root * @return The instance or null */ - private static Object getInstance(String[] split, Class root) { + private static Object getInstance(String[] split, Class root) { try { Class clazz = root == null ? MethodHandles.lookup().lookupClass() : root; Object instance = clazz.newInstance(); @@ -286,9 +287,9 @@ public class Config { if (split.length == 1) { return instance; } - Class found = null; + Class found = null; Class[] classes = clazz.getDeclaredClasses(); - for (Class current : classes) { + for (Class current : classes) { if (current.getSimpleName().equalsIgnoreCase(toFieldName(split[0]))) { found = current; break; @@ -361,8 +362,6 @@ public class Config { * Set some field to be accessible. * * @param field - * @throws NoSuchFieldException - * @throws IllegalAccessException */ private static void setAccessible(Field field) { field.setAccessible(true); diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/AbstractDB.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/AbstractDB.java index 816814d2d..7205eee62 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/AbstractDB.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/AbstractDB.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; public interface AbstractDB { @@ -144,7 +145,7 @@ public interface AbstractDB { * @param plot1 Plot1 * @param plot2 Plot2 */ - void swapPlots(Plot plot1, Plot plot2); + CompletableFuture swapPlots(Plot plot1, Plot plot2); /** * Sets plot flag. diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/DBFunc.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/DBFunc.java index 20453d5d7..ef8af6d8e 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/DBFunc.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/DBFunc.java @@ -16,6 +16,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; /** * Database Functions @@ -58,10 +59,11 @@ public class DBFunc { } } - public static void swapPlots(Plot plot1, Plot plot2) { + public static CompletableFuture swapPlots(Plot plot1, Plot plot2) { if (dbManager != null) { - dbManager.swapPlots(plot1, plot2); + return dbManager.swapPlots(plot1, plot2); } + return CompletableFuture.completedFuture(false); } public static boolean deleteTables() { diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/SQLManager.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/SQLManager.java index 02e276b1e..544a8d9dd 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/SQLManager.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/database/SQLManager.java @@ -44,6 +44,7 @@ import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; @@ -2028,37 +2029,32 @@ import java.util.concurrent.atomic.AtomicInteger; }); } - @Override public void swapPlots(Plot plot1, Plot plot2) { - final int id1 = getId(plot1); - final int id2 = getId(plot2); - final PlotId pos1 = plot1.getId(); - final PlotId pos2 = plot2.getId(); - addPlotTask(plot1, new UniqueStatement("swapPlots") { - @Override public void set(PreparedStatement statement) throws SQLException { - statement.setInt(1, pos2.x); - statement.setInt(2, pos2.y); - statement.setInt(3, id1); - } - - @Override public PreparedStatement get() throws SQLException { - return SQLManager.this.connection.prepareStatement( - "UPDATE `" + SQLManager.this.prefix - + "plot` SET `plot_id_x` = ?, `plot_id_z` = ? WHERE `id` = ?"); - } - }); - addPlotTask(plot2, new UniqueStatement("swapPlots") { - @Override public void set(PreparedStatement statement) throws SQLException { - statement.setInt(1, pos1.x); - statement.setInt(2, pos1.y); - statement.setInt(3, id2); - } - - @Override public PreparedStatement get() throws SQLException { - return SQLManager.this.connection.prepareStatement( - "UPDATE `" + SQLManager.this.prefix - + "plot` SET `plot_id_x` = ?, `plot_id_z` = ? WHERE `id` = ?"); + @Override public CompletableFuture swapPlots(Plot plot1, Plot plot2) { + final CompletableFuture future = new CompletableFuture<>(); + TaskManager.runTaskAsync(() -> { + final int id1 = getId(plot1); + final int id2 = getId(plot2); + final PlotId pos1 = plot1.getId(); + final PlotId pos2 = plot2.getId(); + try (final PreparedStatement preparedStatement = this.connection.prepareStatement("UPDATE `" + SQLManager.this.prefix + + "plot` SET `plot_id_x` = ?, `plot_id_z` = ? WHERE `id` = ?")) { + preparedStatement.setInt(1, pos1.getX()); + preparedStatement.setInt(2, pos1.getY()); + preparedStatement.setInt(3, id1); + preparedStatement.execute(); + preparedStatement.setInt(1, pos2.getX()); + preparedStatement.setInt(2, pos2.getY()); + preparedStatement.setInt(3, id2); + preparedStatement.execute(); + } catch (final Exception e) { + PlotSquared.log(Captions.PREFIX.getTranslated() + "Failed to persist swap of " + plot1 + " and " + plot2 + "!"); + e.printStackTrace(); + future.complete(false); + return; } + future.complete(true); }); + return future; } @Override public void movePlot(final Plot original, final Plot newPlot) { diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java index a032c9c7e..e188cfc6a 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java @@ -80,7 +80,7 @@ public final class BlockBucket implements ConfigurationSerializable { if (chance == -1) chance = 1; String prefix = input.length() == 0 ? "" : ","; - input.append(prefix).append(chance).append("%").append(prefix); + input.append(prefix).append(block.toString()).append(":").append(chance); this.compiled = false; } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Plot.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Plot.java index 9483d48fa..5e4718d16 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Plot.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/Plot.java @@ -54,13 +54,16 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; /** @@ -1742,26 +1745,19 @@ public class Plot { * Swaps the settings for two plots. * * @param plot the plot to swap data with - * @param whenDone the task to run at the end of this method. - * @return + * @return Future containing the result */ - public boolean swapData(Plot plot, Runnable whenDone) { + public CompletableFuture swapData(Plot plot) { if (this.owner == null) { - if (plot == null) { - return false; + if (plot != null && plot.hasOwner()) { + plot.moveData(this, null); + return CompletableFuture.completedFuture(true); } - if (plot.hasOwner()) { - plot.moveData(this, whenDone); - return true; - } - return false; + return CompletableFuture.completedFuture(false); } - if (plot == null) { - this.moveData(plot, whenDone); - return true; - } else if (plot.getOwner() == null) { - this.moveData(plot, whenDone); - return true; + if (plot == null || plot.getOwner() == null) { + this.moveData(plot, null); + return CompletableFuture.completedFuture(true); } // Swap cached PlotId temp = new PlotId(this.getId().x, this.getId().y); @@ -1776,9 +1772,7 @@ public class Plot { this.area.addPlotAbs(this); plot.area.addPlotAbs(plot); // Swap database - DBFunc.swapPlots(plot, this); - TaskManager.runTaskLater(whenDone, 1); - return true; + return DBFunc.swapPlots(plot, this); } /** @@ -1911,10 +1905,10 @@ public class Plot { * @param whenDone A task to run when finished, or null * @return boolean if swap was successful * @see ChunkManager#swap(Location, Location, Location, Location, Runnable) to swap terrain - * @see this#swapData(Plot, Runnable) to swap plot settings - * @see this#swapData(Plot, Runnable) + * @see this#swapData(Plot) to swap plot settings + * @see this#swapData(Plot) */ - public boolean swap(Plot destination, Runnable whenDone) { + public CompletableFuture swap(Plot destination, Runnable whenDone) { return this.move(destination, whenDone, true); } @@ -1926,7 +1920,7 @@ public class Plot { * @param whenDone A task to run when done, or null * @return if the move was successful */ - public boolean move(Plot destination, Runnable whenDone) { + public CompletableFuture move(Plot destination, Runnable whenDone) { return this.move(destination, whenDone, false); } @@ -3067,7 +3061,7 @@ public class Plot { * @param allowSwap whether to swap plots * @return success */ - public boolean move(final Plot destination, final Runnable whenDone, boolean allowSwap) { + public CompletableFuture move(final Plot destination, final Runnable whenDone, boolean allowSwap) { final PlotId offset = new PlotId(destination.getId().x - this.getId().x, destination.getId().y - this.getId().y); Location db = destination.getBottomAbs(); @@ -3076,18 +3070,18 @@ public class Plot { final int offsetZ = db.getZ() - ob.getZ(); if (this.owner == null) { TaskManager.runTaskLater(whenDone, 1); - return false; + return CompletableFuture.completedFuture(false); } - boolean occupied = false; + AtomicBoolean occupied = new AtomicBoolean(false); Set plots = this.getConnectedPlots(); for (Plot plot : plots) { Plot other = plot.getRelative(destination.getArea(), offset.x, offset.y); if (other.hasOwner()) { if (!allowSwap) { TaskManager.runTaskLater(whenDone, 1); - return false; + return CompletableFuture.completedFuture(false); } - occupied = true; + occupied.set(true); } else { plot.removeSign(); } @@ -3097,59 +3091,85 @@ public class Plot { final ArrayDeque regions = new ArrayDeque<>(this.getRegions()); // move / swap data final PlotArea originArea = getArea(); - for (Plot plot : plots) { - Plot other = plot.getRelative(destination.getArea(), offset.x, offset.y); - plot.swapData(other, null); - } - // copy terrain - if (occupied) { - Runnable swap = new Runnable() { - @Override public void run() { - if (regions.isEmpty()) { - TaskManager.runTask(whenDone); - return; - } - CuboidRegion region = regions.poll(); - Location[] corners = MainUtil.getCorners(getWorldName(), region); - Location pos1 = corners[0]; - Location pos2 = corners[1]; - Location pos3 = pos1.clone().add(offsetX, 0, offsetZ); - Location pos4 = pos2.clone().add(offsetX, 0, offsetZ); - pos3.setWorld(destination.getWorldName()); - pos4.setWorld(destination.getWorldName()); - ChunkManager.manager.swap(pos1, pos2, pos3, pos4, this); + + final Iterator plotIterator = plots.iterator(); + + CompletableFuture future = null; + if (plotIterator.hasNext()) { + while (plotIterator.hasNext()) { + final Plot plot = plotIterator.next(); + final Plot other = plot.getRelative(destination.getArea(), offset.x, offset.y); + final CompletableFuture swapResult = plot.swapData(other); + if (future == null) { + future = swapResult; + } else { + future = future.thenCombine(swapResult, (fn, th) -> fn); } - }; - swap.run(); + } } else { - Runnable move = new Runnable() { - @Override public void run() { - if (regions.isEmpty()) { - Plot plot = destination.getRelative(0, 0); - for (Plot current : plot.getConnectedPlots()) { - getManager().claimPlot(current); - Plot originPlot = originArea.getPlotAbs( - new PlotId(current.id.x - offset.x, current.id.y - offset.y)); - originPlot.getManager().unClaimPlot(originPlot, null); - } - plot.setSign(); - TaskManager.runTask(whenDone); - return; - } - final Runnable task = this; - CuboidRegion region = regions.poll(); - Location[] corners = MainUtil.getCorners(getWorldName(), region); - final Location pos1 = corners[0]; - final Location pos2 = corners[1]; - Location newPos = pos1.clone().add(offsetX, 0, offsetZ); - newPos.setWorld(destination.getWorldName()); - ChunkManager.manager.copyRegion(pos1, pos2, newPos, - () -> ChunkManager.manager.regenerateRegion(pos1, pos2, false, task)); - } - }; - move.run(); + future = CompletableFuture.completedFuture(true); } - return true; + + return future.thenApply(result -> { + if (!result) { + return false; + } + // copy terrain + if (occupied.get()) { + new Runnable() { + @Override public void run() { + if (regions.isEmpty()) { + // Update signs + destination.setSign(); + Plot.this.setSign(); + // Run final tasks + TaskManager.runTask(whenDone); + } else { + CuboidRegion region = regions.poll(); + Location[] corners = MainUtil.getCorners(getWorldName(), region); + Location pos1 = corners[0]; + Location pos2 = corners[1]; + Location pos3 = pos1.clone().add(offsetX, 0, offsetZ); + Location pos4 = pos2.clone().add(offsetX, 0, offsetZ); + pos3.setWorld(destination.getWorldName()); + pos4.setWorld(destination.getWorldName()); + ChunkManager.manager.swap(pos1, pos2, pos3, pos4, this); + } + } + }.run(); + } else { + new Runnable() { + @Override public void run() { + if (regions.isEmpty()) { + Plot plot = destination.getRelative(0, 0); + Plot originPlot = originArea.getPlotAbs(new PlotId(plot.id.x - offset.x, plot.id.y - offset.y)); + final Runnable clearDone = () -> { + for (final Plot current : plot.getConnectedPlots()) { + getManager().claimPlot(current); + } + plot.setSign(); + TaskManager.runTask(whenDone); + }; + if (originPlot != null) { + originPlot.clear(false, true, clearDone); + } else { + clearDone.run(); + } + return; + } + final Runnable task = this; + CuboidRegion region = regions.poll(); + Location[] corners = MainUtil.getCorners(getWorldName(), region); + final Location pos1 = corners[0]; + final Location pos2 = corners[1]; + Location newPos = pos1.clone().add(offsetX, 0, offsetZ); + newPos.setWorld(destination.getWorldName()); + ChunkManager.manager.copyRegion(pos1, pos2, newPos, task); + } + }.run(); + } + return true; + }); } /** diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/DefaultPlotAreaManager.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/DefaultPlotAreaManager.java index c504af4fb..64e234381 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/DefaultPlotAreaManager.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/DefaultPlotAreaManager.java @@ -5,6 +5,7 @@ import com.github.intellectualsites.plotsquared.plot.object.PlotArea; import com.github.intellectualsites.plotsquared.plot.util.StringMan; import com.github.intellectualsites.plotsquared.plot.util.area.QuadMap; import com.sk89q.worldedit.regions.CuboidRegion; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Arrays; @@ -61,7 +62,7 @@ public class DefaultPlotAreaManager implements PlotAreaManager { if (areas == null) { return null; } - int y; + int z; int x; switch (areas.length) { case 1: @@ -74,9 +75,9 @@ public class DefaultPlotAreaManager implements PlotAreaManager { case 7: case 8: x = location.getX(); - y = location.getY(); + z = location.getZ(); for (PlotArea area : areas) { - if (area.contains(x, y)) { + if (area.contains(x, z)) { return area; } } @@ -139,7 +140,7 @@ public class DefaultPlotAreaManager implements PlotAreaManager { return null; } - @Override public PlotArea getPlotArea(Location location) { + @Override public PlotArea getPlotArea(@NotNull Location location) { switch (this.plotAreas.length) { case 0: return null; @@ -174,7 +175,7 @@ public class DefaultPlotAreaManager implements PlotAreaManager { return null; } int x; - int y; + int z; switch (areas.length) { case 0: PlotArea a = areas[0]; @@ -187,9 +188,9 @@ public class DefaultPlotAreaManager implements PlotAreaManager { case 7: case 8: x = location.getX(); - y = location.getY(); + z = location.getZ(); for (PlotArea area : areas) { - if (area.contains(x, y)) { + if (area.contains(x, z)) { return area; } } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/PlotAreaManager.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/PlotAreaManager.java index b34ad2787..03e881822 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/PlotAreaManager.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/PlotAreaManager.java @@ -3,11 +3,33 @@ package com.github.intellectualsites.plotsquared.plot.object.worlds; import com.github.intellectualsites.plotsquared.plot.object.Location; import com.github.intellectualsites.plotsquared.plot.object.PlotArea; import com.sk89q.worldedit.regions.CuboidRegion; +import org.jetbrains.annotations.NotNull; public interface PlotAreaManager { + + /** + * Get the plot area for a particular location. This + * method assumes that the caller already knows that + * the location belongs to a plot area, in which + * case it will return the appropriate plot area. + * + * If the location does not belong to a plot area, + * it may still return an area. + * + * @param location The location + * @return An applicable area, or null + */ PlotArea getApplicablePlotArea(Location location); - PlotArea getPlotArea(Location location); + /** + * Get the plot area, if there is any, for the given + * location. This may return null, if given location + * does not belong to a plot area. + * + * @param location The location + * @return The area, if found + */ + PlotArea getPlotArea(@NotNull Location location); PlotArea getPlotArea(String world, String id); diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/SinglePlotAreaManager.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/SinglePlotAreaManager.java index e54ab6964..73acdaa0c 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/SinglePlotAreaManager.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/worlds/SinglePlotAreaManager.java @@ -5,6 +5,7 @@ import com.github.intellectualsites.plotsquared.plot.object.PlotArea; import com.github.intellectualsites.plotsquared.plot.util.ArrayUtil; import com.github.intellectualsites.plotsquared.plot.util.SetupUtils; import com.sk89q.worldedit.regions.CuboidRegion; +import org.jetbrains.annotations.NotNull; public class SinglePlotAreaManager extends DefaultPlotAreaManager { private final SinglePlotArea[] array; @@ -82,7 +83,7 @@ public class SinglePlotAreaManager extends DefaultPlotAreaManager { return isWorld(world) || world.equals("*") ? area : super.getPlotArea(world, id); } - @Override public PlotArea getPlotArea(Location location) { + @Override public PlotArea getPlotArea(@NotNull Location location) { PlotArea found = super.getPlotArea(location); if (found != null) { return found; diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/ChunkManager.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/ChunkManager.java index c24d404f0..99f90b8e6 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/ChunkManager.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/ChunkManager.java @@ -52,7 +52,9 @@ public abstract class ChunkManager { } queue.flush(); } else { - forceChunks.put(loc, force); + if (force != null) { + forceChunks.put(loc, force); + } addChunks.put(loc, add); queue.regenChunk(loc.getX(), loc.getZ()); forceChunks.remove(loc); diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/world/PatternUtil.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/world/PatternUtil.java index d281cc4d8..ea3079bb6 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/world/PatternUtil.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/util/world/PatternUtil.java @@ -3,6 +3,7 @@ package com.github.intellectualsites.plotsquared.plot.util.world; import com.github.intellectualsites.plotsquared.commands.Command; import com.github.intellectualsites.plotsquared.plot.config.Captions; import com.github.intellectualsites.plotsquared.plot.object.PlotPlayer; +import com.google.common.base.Preconditions; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.InputParseException; @@ -18,7 +19,9 @@ import com.sk89q.worldedit.world.block.BlockType; import org.jetbrains.annotations.NotNull; public class PatternUtil { + public static BaseBlock apply(@NotNull Pattern pattern, int x, int y, int z) { + Preconditions.checkNotNull(pattern, "Pattern may not be null"); if (pattern instanceof BlockPattern || pattern instanceof RandomPattern || pattern instanceof BlockState diff --git a/Core/src/test/java/com/github/intellectualsites/plotsquared/plot/database/AbstractDBTest.java b/Core/src/test/java/com/github/intellectualsites/plotsquared/plot/database/AbstractDBTest.java index b657db5e2..878aefd1f 100644 --- a/Core/src/test/java/com/github/intellectualsites/plotsquared/plot/database/AbstractDBTest.java +++ b/Core/src/test/java/com/github/intellectualsites/plotsquared/plot/database/AbstractDBTest.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; public class AbstractDBTest implements AbstractDB { @@ -91,7 +92,8 @@ public class AbstractDBTest implements AbstractDB { @Override public void setMerged(Plot plot, boolean[] merged) { } - @Override public void swapPlots(Plot plot1, Plot plot2) { + @Override public CompletableFuture swapPlots(Plot plot1, Plot plot2) { + return CompletableFuture.completedFuture(true); } @Override public void setFlag(Plot plot, PlotFlag flag) {