From a7fcc36f9f28464b895bfe02573ab3683bd9f1ee Mon Sep 17 00:00:00 2001 From: FlorianMichael Date: Thu, 22 Jan 2026 21:40:50 +0100 Subject: [PATCH 1/4] Make sure no blocks are placed when deleting a plot Fixes https://github.com/IntellectualSites/PlotSquared/issues/4794 Signed-off-by: FlorianMichael --- .../bukkit/listener/BlockEventListener.java | 10 ++++++++++ .../java/com/plotsquared/core/command/Delete.java | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/BlockEventListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/BlockEventListener.java index c0a76c8232..82dc220bf2 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/BlockEventListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/BlockEventListener.java @@ -152,6 +152,11 @@ public void blockCreate(BlockPlaceEvent event) { BukkitPlayer pp = BukkitUtil.adapt(player); Plot plot = area.getPlot(location); if (plot != null) { + // Prevent block placement during pending deletion + if (plot.getMeta("pendingDelete") != null) { + event.setCancelled(true); + return; + } if (area.notifyIfOutsideBuildArea(pp, location.getY())) { event.setCancelled(true); return; @@ -230,6 +235,11 @@ public void blockDestroy(BlockBreakEvent event) { Plot plot = area.getPlot(location); if (plot != null) { BukkitPlayer plotPlayer = BukkitUtil.adapt(player); + // Prevent block breaking during pending deletion + if (plot.getMeta("pendingDelete") != null) { + event.setCancelled(true); + return; + } // == rather than <= as we only care about the "ground level" not being destroyed if (event.getBlock().getY() == area.getMinGenHeight()) { if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_GROUNDLEVEL)) { diff --git a/Core/src/main/java/com/plotsquared/core/command/Delete.java b/Core/src/main/java/com/plotsquared/core/command/Delete.java index d7f6c6ab9a..2f528bc9d7 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Delete.java +++ b/Core/src/main/java/com/plotsquared/core/command/Delete.java @@ -31,6 +31,7 @@ import com.plotsquared.core.util.EventDispatcher; import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.task.TaskManager; +import com.plotsquared.core.util.task.TaskTime; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; @@ -96,6 +97,10 @@ public boolean onCommand(final PlotPlayer player, String[] args) { player.sendMessage(TranslatableCaption.of("errors.wait_for_timer")); return; } + + for (Plot connectedPlot : plots) { + connectedPlot.setMeta("pendingDelete", true); + } final long start = System.currentTimeMillis(); if (Settings.Teleport.ON_DELETE) { plot.getPlayersInPlot().forEach(playerInPlot -> plot.teleportPlayer(playerInPlot, TeleportCause.COMMAND_DELETE, @@ -104,6 +109,10 @@ public boolean onCommand(final PlotPlayer player, String[] args) { )); } boolean result = plot.getPlotModificationManager().deletePlot(player, () -> { + // Clear pending delete metadata now that deletion is actually starting + for (Plot connectedPlot : plots) { + connectedPlot.deleteMeta("pendingDelete"); + } plot.removeRunning(); if (this.econHandler.isEnabled(plotArea)) { PlotExpression valueExr = plotArea.getPrices().get("sell"); @@ -135,6 +144,12 @@ public boolean onCommand(final PlotPlayer player, String[] args) { }; if (hasConfirmation(player)) { CmdConfirm.addPending(player, getCommandString() + ' ' + plot.getId(), run); + // Schedule cleanup task for when confirmation times out + TaskManager.runTaskLater(() -> { + for (Plot connectedPlot : plots) { + connectedPlot.deleteMeta("pendingDelete"); + } + }, TaskTime.seconds(Settings.Confirmation.CONFIRMATION_TIMEOUT_SECONDS + 1)); } else { TaskManager.runTask(run); } From 448c5be9e0637728eca49219b83c61c2788fdee3 Mon Sep 17 00:00:00 2001 From: Florian Reuth Date: Mon, 9 Feb 2026 20:40:26 +0100 Subject: [PATCH 2/4] Only delete meta when actually unconfirmed; Add missing meta deletion on errors Signed-off-by: Florian Reuth --- .../com/plotsquared/core/command/Delete.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/command/Delete.java b/Core/src/main/java/com/plotsquared/core/command/Delete.java index 2f528bc9d7..668f73bb95 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Delete.java +++ b/Core/src/main/java/com/plotsquared/core/command/Delete.java @@ -32,6 +32,7 @@ import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.task.TaskManager; import com.plotsquared.core.util.task.TaskTime; +import java.util.concurrent.atomic.AtomicBoolean; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; @@ -92,8 +93,14 @@ public boolean onCommand(final PlotPlayer player, String[] args) { final int currentPlots = Settings.Limit.GLOBAL ? player.getPlotCount() : player.getPlotCount(plot.getWorldName()); + + final AtomicBoolean confirmed = new AtomicBoolean(false); Runnable run = () -> { + confirmed.set(true); if (plot.getRunning() > 0) { + for (Plot connectedPlot : plots) { + connectedPlot.deleteMeta("pendingDelete"); + } player.sendMessage(TranslatableCaption.of("errors.wait_for_timer")); return; } @@ -139,15 +146,23 @@ public boolean onCommand(final PlotPlayer player, String[] args) { if (result) { plot.addRunning(); } else { + for (Plot connectedPlot : plots) { + connectedPlot.deleteMeta("pendingDelete"); + } player.sendMessage(TranslatableCaption.of("errors.wait_for_timer")); } }; if (hasConfirmation(player)) { + for (Plot connectedPlot : plots) { + connectedPlot.setMeta("pendingDelete", true); + } CmdConfirm.addPending(player, getCommandString() + ' ' + plot.getId(), run); // Schedule cleanup task for when confirmation times out TaskManager.runTaskLater(() -> { - for (Plot connectedPlot : plots) { - connectedPlot.deleteMeta("pendingDelete"); + if (!confirmed.get()) { + for (Plot connectedPlot : plots) { + connectedPlot.deleteMeta("pendingDelete"); + } } }, TaskTime.seconds(Settings.Confirmation.CONFIRMATION_TIMEOUT_SECONDS + 1)); } else { From 14a766ea680460c99f47e63407d50aa00d4d4031 Mon Sep 17 00:00:00 2001 From: Florian Reuth Date: Sat, 28 Feb 2026 23:06:21 +0100 Subject: [PATCH 3/4] Don't block between /p delete and /p confirm Signed-off-by: Florian Reuth --- .../java/com/plotsquared/core/command/Delete.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/command/Delete.java b/Core/src/main/java/com/plotsquared/core/command/Delete.java index 668f73bb95..40378ed6c5 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Delete.java +++ b/Core/src/main/java/com/plotsquared/core/command/Delete.java @@ -31,8 +31,6 @@ import com.plotsquared.core.util.EventDispatcher; import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.task.TaskTime; -import java.util.concurrent.atomic.AtomicBoolean; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; @@ -94,9 +92,7 @@ public boolean onCommand(final PlotPlayer player, String[] args) { player.getPlotCount() : player.getPlotCount(plot.getWorldName()); - final AtomicBoolean confirmed = new AtomicBoolean(false); Runnable run = () -> { - confirmed.set(true); if (plot.getRunning() > 0) { for (Plot connectedPlot : plots) { connectedPlot.deleteMeta("pendingDelete"); @@ -153,18 +149,7 @@ public boolean onCommand(final PlotPlayer player, String[] args) { } }; if (hasConfirmation(player)) { - for (Plot connectedPlot : plots) { - connectedPlot.setMeta("pendingDelete", true); - } CmdConfirm.addPending(player, getCommandString() + ' ' + plot.getId(), run); - // Schedule cleanup task for when confirmation times out - TaskManager.runTaskLater(() -> { - if (!confirmed.get()) { - for (Plot connectedPlot : plots) { - connectedPlot.deleteMeta("pendingDelete"); - } - } - }, TaskTime.seconds(Settings.Confirmation.CONFIRMATION_TIMEOUT_SECONDS + 1)); } else { TaskManager.runTask(run); } From 1b27934b4a1d097a79e6b8ec959d92e48ed8f37c Mon Sep 17 00:00:00 2001 From: Florian Reuth Date: Fri, 10 Apr 2026 16:35:09 +0200 Subject: [PATCH 4/4] Clear plot meta on error Signed-off-by: Florian Reuth --- .../com/plotsquared/core/command/Delete.java | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/command/Delete.java b/Core/src/main/java/com/plotsquared/core/command/Delete.java index 40378ed6c5..ef5823223a 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Delete.java +++ b/Core/src/main/java/com/plotsquared/core/command/Delete.java @@ -112,32 +112,40 @@ public boolean onCommand(final PlotPlayer player, String[] args) { )); } boolean result = plot.getPlotModificationManager().deletePlot(player, () -> { - // Clear pending delete metadata now that deletion is actually starting - for (Plot connectedPlot : plots) { - connectedPlot.deleteMeta("pendingDelete"); - } - plot.removeRunning(); - if (this.econHandler.isEnabled(plotArea)) { - PlotExpression valueExr = plotArea.getPrices().get("sell"); - double value = plots.size() * valueExr.evaluate(currentPlots); - if (value > 0d) { - this.econHandler.depositMoney(player, value); - player.sendMessage( - TranslatableCaption.of("economy.added_balance"), - TagResolver.resolver("money", Tag.inserting(Component.text(this.econHandler.format(value)))) - ); + try { + // Clear pending delete metadata now that deletion is actually starting + for (Plot connectedPlot : plots) { + connectedPlot.deleteMeta("pendingDelete"); + } + plot.removeRunning(); + if (this.econHandler.isEnabled(plotArea)) { + PlotExpression valueExr = plotArea.getPrices().get("sell"); + double value = plots.size() * valueExr.evaluate(currentPlots); + if (value > 0d) { + this.econHandler.depositMoney(player, value); + player.sendMessage( + TranslatableCaption.of("economy.added_balance"), + TagResolver.resolver("money", Tag.inserting(Component.text(this.econHandler.format(value)))) + ); + } + } + player.sendMessage( + TranslatableCaption.of("working.deleting_done"), + TagResolver.resolver( + "amount", + Tag.inserting(Component.text(String.valueOf(System.currentTimeMillis() - start))) + ), + TagResolver.resolver("world", Tag.inserting(Component.text(plotArea.getWorldName()))), + TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString()))) + ); + eventDispatcher.callPostDelete(plot); + } catch (Throwable e) { + // ... or if something went wrong + for (Plot connectedPlot : plots) { + connectedPlot.deleteMeta("pendingDelete"); } + throw e; } - player.sendMessage( - TranslatableCaption.of("working.deleting_done"), - TagResolver.resolver( - "amount", - Tag.inserting(Component.text(String.valueOf(System.currentTimeMillis() - start))) - ), - TagResolver.resolver("world", Tag.inserting(Component.text(plotArea.getWorldName()))), - TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString()))) - ); - eventDispatcher.callPostDelete(plot); }); if (result) { plot.addRunning();