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.bukkit.listener; 020 021import com.google.inject.Inject; 022import com.plotsquared.bukkit.player.BukkitPlayer; 023import com.plotsquared.bukkit.util.BukkitUtil; 024import com.plotsquared.core.PlotSquared; 025import com.plotsquared.core.configuration.Settings; 026import com.plotsquared.core.configuration.caption.TranslatableCaption; 027import com.plotsquared.core.database.DBFunc; 028import com.plotsquared.core.location.Location; 029import com.plotsquared.core.permissions.Permission; 030import com.plotsquared.core.player.PlotPlayer; 031import com.plotsquared.core.plot.Plot; 032import com.plotsquared.core.plot.PlotArea; 033import com.plotsquared.core.plot.flag.implementations.BlockBurnFlag; 034import com.plotsquared.core.plot.flag.implementations.BlockIgnitionFlag; 035import com.plotsquared.core.plot.flag.implementations.BreakFlag; 036import com.plotsquared.core.plot.flag.implementations.CoralDryFlag; 037import com.plotsquared.core.plot.flag.implementations.CropGrowFlag; 038import com.plotsquared.core.plot.flag.implementations.DisablePhysicsFlag; 039import com.plotsquared.core.plot.flag.implementations.DoneFlag; 040import com.plotsquared.core.plot.flag.implementations.ExplosionFlag; 041import com.plotsquared.core.plot.flag.implementations.GrassGrowFlag; 042import com.plotsquared.core.plot.flag.implementations.IceFormFlag; 043import com.plotsquared.core.plot.flag.implementations.IceMeltFlag; 044import com.plotsquared.core.plot.flag.implementations.InstabreakFlag; 045import com.plotsquared.core.plot.flag.implementations.KelpGrowFlag; 046import com.plotsquared.core.plot.flag.implementations.LeafDecayFlag; 047import com.plotsquared.core.plot.flag.implementations.LiquidFlowFlag; 048import com.plotsquared.core.plot.flag.implementations.MycelGrowFlag; 049import com.plotsquared.core.plot.flag.implementations.PlaceFlag; 050import com.plotsquared.core.plot.flag.implementations.RedstoneFlag; 051import com.plotsquared.core.plot.flag.implementations.SnowFormFlag; 052import com.plotsquared.core.plot.flag.implementations.SnowMeltFlag; 053import com.plotsquared.core.plot.flag.implementations.SoilDryFlag; 054import com.plotsquared.core.plot.flag.implementations.VineGrowFlag; 055import com.plotsquared.core.plot.flag.types.BlockTypeWrapper; 056import com.plotsquared.core.plot.flag.types.BooleanFlag; 057import com.plotsquared.core.plot.world.PlotAreaManager; 058import com.plotsquared.core.util.PlotFlagUtil; 059import com.plotsquared.core.util.task.TaskManager; 060import com.plotsquared.core.util.task.TaskTime; 061import com.sk89q.worldedit.WorldEdit; 062import com.sk89q.worldedit.bukkit.BukkitAdapter; 063import com.sk89q.worldedit.world.block.BlockType; 064import net.kyori.adventure.text.Component; 065import net.kyori.adventure.text.minimessage.tag.Tag; 066import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 067import org.bukkit.Bukkit; 068import org.bukkit.GameMode; 069import org.bukkit.Material; 070import org.bukkit.block.Block; 071import org.bukkit.block.BlockFace; 072import org.bukkit.block.BlockState; 073import org.bukkit.block.data.BlockData; 074import org.bukkit.block.data.type.Dispenser; 075import org.bukkit.block.data.type.Farmland; 076import org.bukkit.entity.Entity; 077import org.bukkit.entity.Fireball; 078import org.bukkit.entity.Player; 079import org.bukkit.entity.Projectile; 080import org.bukkit.event.EventHandler; 081import org.bukkit.event.EventPriority; 082import org.bukkit.event.Listener; 083import org.bukkit.event.block.BlockBreakEvent; 084import org.bukkit.event.block.BlockBurnEvent; 085import org.bukkit.event.block.BlockDamageEvent; 086import org.bukkit.event.block.BlockDispenseEvent; 087import org.bukkit.event.block.BlockExplodeEvent; 088import org.bukkit.event.block.BlockFadeEvent; 089import org.bukkit.event.block.BlockFormEvent; 090import org.bukkit.event.block.BlockFromToEvent; 091import org.bukkit.event.block.BlockGrowEvent; 092import org.bukkit.event.block.BlockIgniteEvent; 093import org.bukkit.event.block.BlockMultiPlaceEvent; 094import org.bukkit.event.block.BlockPhysicsEvent; 095import org.bukkit.event.block.BlockPistonExtendEvent; 096import org.bukkit.event.block.BlockPistonRetractEvent; 097import org.bukkit.event.block.BlockPlaceEvent; 098import org.bukkit.event.block.BlockRedstoneEvent; 099import org.bukkit.event.block.BlockSpreadEvent; 100import org.bukkit.event.block.CauldronLevelChangeEvent; 101import org.bukkit.event.block.EntityBlockFormEvent; 102import org.bukkit.event.block.LeavesDecayEvent; 103import org.bukkit.event.block.MoistureChangeEvent; 104import org.bukkit.event.block.SpongeAbsorbEvent; 105import org.bukkit.event.world.StructureGrowEvent; 106import org.bukkit.projectiles.BlockProjectileSource; 107import org.bukkit.util.Vector; 108import org.checkerframework.checker.nullness.qual.NonNull; 109 110import java.util.Iterator; 111import java.util.List; 112import java.util.Objects; 113import java.util.Set; 114import java.util.UUID; 115import java.util.stream.Collectors; 116import java.util.stream.Stream; 117 118import static org.bukkit.Tag.CORALS; 119import static org.bukkit.Tag.CORAL_BLOCKS; 120import static org.bukkit.Tag.WALL_CORALS; 121 122@SuppressWarnings("unused") 123public class BlockEventListener implements Listener { 124 125 private static final Set<Material> PISTONS = Set.of( 126 Material.PISTON, 127 Material.STICKY_PISTON 128 ); 129 private static final Set<Material> PHYSICS_BLOCKS = Set.of( 130 Material.TURTLE_EGG, 131 Material.TURTLE_SPAWN_EGG 132 ); 133 private static final Set<Material> SNOW = Stream.of(Material.values()) // needed as Tag.SNOW isn't present in 1.16.5 134 .filter(material -> material.name().contains("SNOW")) 135 .filter(Material::isBlock) 136 .collect(Collectors.toUnmodifiableSet()); 137 138 private final PlotAreaManager plotAreaManager; 139 private final WorldEdit worldEdit; 140 141 @Inject 142 public BlockEventListener(final @NonNull PlotAreaManager plotAreaManager, final @NonNull WorldEdit worldEdit) { 143 this.plotAreaManager = plotAreaManager; 144 this.worldEdit = worldEdit; 145 } 146 147 public static void sendBlockChange(final org.bukkit.Location bloc, final BlockData data) { 148 TaskManager.runTaskLater(() -> { 149 String world = bloc.getWorld().getName(); 150 int x = bloc.getBlockX(); 151 int z = bloc.getBlockZ(); 152 int distance = Bukkit.getViewDistance() * 16; 153 154 for (final PlotPlayer<?> player : PlotSquared.platform().playerManager().getPlayers()) { 155 Location location = player.getLocation(); 156 if (location.getWorldName().equals(world)) { 157 if (16 * Math.abs(location.getX() - x) / 16 > distance || 16 * Math.abs(location.getZ() - z) / 16 > distance) { 158 continue; 159 } 160 ((BukkitPlayer) player).player.sendBlockChange(bloc, data); 161 } 162 } 163 }, TaskTime.ticks(3L)); 164 } 165 166 @EventHandler 167 public void onRedstoneEvent(BlockRedstoneEvent event) { 168 Block block = event.getBlock(); 169 Location location = BukkitUtil.adapt(block.getLocation()); 170 PlotArea area = location.getPlotArea(); 171 if (area == null) { 172 return; 173 } 174 Plot plot = location.getOwnedPlot(); 175 if (plot == null) { 176 if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, RedstoneFlag.class, false)) { 177 event.setNewCurrent(0); 178 } 179 return; 180 } 181 if (!plot.getFlag(RedstoneFlag.class)) { 182 event.setNewCurrent(0); 183 plot.debug("Redstone event was cancelled because redstone = false"); 184 return; 185 } 186 if (Settings.Redstone.DISABLE_OFFLINE) { 187 boolean disable = false; 188 if (!DBFunc.SERVER.equals(plot.getOwner())) { 189 if (plot.isMerged()) { 190 disable = true; 191 for (UUID owner : plot.getOwners()) { 192 if (PlotSquared.platform().playerManager().getPlayerIfExists(owner) != null) { 193 disable = false; 194 break; 195 } 196 } 197 } else { 198 disable = PlotSquared.platform().playerManager().getPlayerIfExists(plot.getOwnerAbs()) == null; 199 } 200 } 201 if (disable) { 202 for (UUID trusted : plot.getTrusted()) { 203 if (PlotSquared.platform().playerManager().getPlayerIfExists(trusted) != null) { 204 disable = false; 205 break; 206 } 207 } 208 if (disable) { 209 event.setNewCurrent(0); 210 plot.debug("Redstone event was cancelled because no trusted player was in the plot"); 211 return; 212 } 213 } 214 } 215 if (Settings.Redstone.DISABLE_UNOCCUPIED) { 216 for (final PlotPlayer<?> player : PlotSquared.platform().playerManager().getPlayers()) { 217 if (plot.equals(player.getCurrentPlot())) { 218 return; 219 } 220 } 221 event.setNewCurrent(0); 222 } 223 } 224 225 @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) 226 public void onPhysicsEvent(BlockPhysicsEvent event) { 227 Block block = event.getBlock(); 228 Location location = BukkitUtil.adapt(block.getLocation()); 229 PlotArea area = location.getPlotArea(); 230 if (area == null) { 231 return; 232 } 233 Plot plot = area.getOwnedPlotAbs(location); 234 if (plot == null) { 235 return; 236 } 237 if (event.getChangedType().hasGravity() && plot.getFlag(DisablePhysicsFlag.class)) { 238 event.setCancelled(true); 239 sendBlockChange(event.getBlock().getLocation(), event.getBlock().getBlockData()); 240 plot.debug("Prevented block physics and resent block change because disable-physics = true"); 241 return; 242 } 243 if (event.getChangedType() == Material.COMPARATOR) { 244 if (!plot.getFlag(RedstoneFlag.class)) { 245 event.setCancelled(true); 246 plot.debug("Prevented comparator update because redstone = false"); 247 } 248 return; 249 } 250 if (PHYSICS_BLOCKS.contains(event.getChangedType())) { 251 if (plot.getFlag(DisablePhysicsFlag.class)) { 252 event.setCancelled(true); 253 plot.debug("Prevented block physics because disable-physics = true"); 254 } 255 return; 256 } 257 if (Settings.Redstone.DETECT_INVALID_EDGE_PISTONS) { 258 if (PISTONS.contains(block.getType())) { 259 org.bukkit.block.data.Directional piston = (org.bukkit.block.data.Directional) block.getBlockData(); 260 final BlockFace facing = piston.getFacing(); 261 location = location.add(facing.getModX(), facing.getModY(), facing.getModZ()); 262 Plot newPlot = area.getOwnedPlotAbs(location); 263 if (!plot.equals(newPlot)) { 264 event.setCancelled(true); 265 plot.debug("Prevented piston update because of invalid edge piston detection"); 266 } 267 } 268 } 269 } 270 271 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 272 public void blockCreate(BlockPlaceEvent event) { 273 Location location = BukkitUtil.adapt(event.getBlock().getLocation()); 274 PlotArea area = location.getPlotArea(); 275 if (area == null) { 276 return; 277 } 278 Player player = event.getPlayer(); 279 BukkitPlayer pp = BukkitUtil.adapt(player); 280 Plot plot = area.getPlot(location); 281 if (plot != null) { 282 if (area.notifyIfOutsideBuildArea(pp, location.getY())) { 283 event.setCancelled(true); 284 pp.sendMessage( 285 TranslatableCaption.of("height.height_limit"), 286 TagResolver.builder() 287 .tag("minheight", Tag.inserting(Component.text(area.getMinBuildHeight()))) 288 .tag("maxheight", Tag.inserting(Component.text(area.getMaxBuildHeight()))) 289 .build() 290 ); 291 return; 292 } 293 if (!plot.hasOwner()) { 294 if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)) { 295 pp.sendMessage( 296 TranslatableCaption.of("permission.no_permission_event"), 297 TagResolver.resolver( 298 "node", 299 Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_UNOWNED) 300 ) 301 ); 302 event.setCancelled(true); 303 return; 304 } 305 } else if (!plot.isAdded(pp.getUUID())) { 306 List<BlockTypeWrapper> place = plot.getFlag(PlaceFlag.class); 307 if (place != null) { 308 Block block = event.getBlock(); 309 if (place.contains( 310 BlockTypeWrapper.get(BukkitAdapter.asBlockType(block.getType())))) { 311 return; 312 } 313 } 314 if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) { 315 pp.sendMessage( 316 TranslatableCaption.of("permission.no_permission_event"), 317 TagResolver.resolver( 318 "node", 319 Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_OTHER) 320 ) 321 ); 322 event.setCancelled(true); 323 plot.debug(player.getName() + " could not place " + event.getBlock().getType() 324 + " because of the place = false"); 325 return; 326 } 327 } else if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) { 328 if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) { 329 pp.sendMessage( 330 TranslatableCaption.of("done.building_restricted") 331 ); 332 event.setCancelled(true); 333 return; 334 } 335 } 336 if (plot.getFlag(DisablePhysicsFlag.class)) { 337 Block block = event.getBlockPlaced(); 338 if (block.getType().hasGravity()) { 339 sendBlockChange(block.getLocation(), block.getBlockData()); 340 plot.debug(event.getBlock().getType() 341 + " did not fall because of disable-physics = true"); 342 } 343 } 344 } else if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) { 345 pp.sendMessage( 346 TranslatableCaption.of("permission.no_permission_event"), 347 TagResolver.resolver( 348 "node", 349 Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD) 350 ) 351 ); 352 event.setCancelled(true); 353 } 354 } 355 356 @EventHandler(priority = EventPriority.LOWEST) 357 public void blockDestroy(BlockBreakEvent event) { 358 Player player = event.getPlayer(); 359 Location location = BukkitUtil.adapt(event.getBlock().getLocation()); 360 PlotArea area = location.getPlotArea(); 361 if (area == null) { 362 return; 363 } 364 Plot plot = area.getPlot(location); 365 if (plot != null) { 366 BukkitPlayer plotPlayer = BukkitUtil.adapt(player); 367 // == rather than <= as we only care about the "ground level" not being destroyed 368 if (event.getBlock().getY() == area.getMinGenHeight()) { 369 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_GROUNDLEVEL)) { 370 plotPlayer.sendMessage( 371 TranslatableCaption.of("permission.no_permission_event"), 372 TagResolver.resolver( 373 "node", 374 Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_GROUNDLEVEL) 375 ) 376 ); 377 event.setCancelled(true); 378 return; 379 } 380 } else if (area.notifyIfOutsideBuildArea(plotPlayer, location.getY())) { 381 event.setCancelled(true); 382 plotPlayer.sendMessage( 383 TranslatableCaption.of("height.height_limit"), 384 TagResolver.builder() 385 .tag("minheight", Tag.inserting(Component.text(area.getMinBuildHeight()))) 386 .tag("maxheight", Tag.inserting(Component.text(area.getMaxBuildHeight()))) 387 .build() 388 ); 389 return; 390 } 391 if (!plot.hasOwner()) { 392 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED, true)) { 393 event.setCancelled(true); 394 } 395 return; 396 } 397 if (!plot.isAdded(plotPlayer.getUUID())) { 398 List<BlockTypeWrapper> destroy = plot.getFlag(BreakFlag.class); 399 Block block = event.getBlock(); 400 final BlockType blockType = BukkitAdapter.asBlockType(block.getType()); 401 for (final BlockTypeWrapper blockTypeWrapper : destroy) { 402 if (blockTypeWrapper.accepts(blockType)) { 403 return; 404 } 405 } 406 if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_OTHER)) { 407 return; 408 } 409 plotPlayer.sendMessage( 410 TranslatableCaption.of("permission.no_permission_event"), 411 TagResolver.resolver( 412 "node", 413 Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_OTHER) 414 ) 415 ); 416 event.setCancelled(true); 417 } else if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) { 418 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) { 419 plotPlayer.sendMessage( 420 TranslatableCaption.of("done.building_restricted") 421 ); 422 event.setCancelled(true); 423 return; 424 } 425 } 426 return; 427 } 428 BukkitPlayer pp = BukkitUtil.adapt(player); 429 if (pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_ROAD)) { 430 return; 431 } 432 if (this.worldEdit != null && pp.getAttribute("worldedit")) { 433 if (player.getInventory().getItemInMainHand().getType() == Material 434 .getMaterial(this.worldEdit.getConfiguration().wandItem)) { 435 return; 436 } 437 } 438 pp.sendMessage( 439 TranslatableCaption.of("permission.no_permission_event"), 440 TagResolver.resolver( 441 "node", 442 Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_ROAD) 443 ) 444 ); 445 event.setCancelled(true); 446 } 447 448 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 449 public void onBlockSpread(BlockSpreadEvent event) { 450 Block block = event.getBlock(); 451 Location location = BukkitUtil.adapt(block.getLocation()); 452 if (location.isPlotRoad()) { 453 event.setCancelled(true); 454 return; 455 } 456 PlotArea area = location.getPlotArea(); 457 if (area == null) { 458 return; 459 } 460 Plot plot = area.getOwnedPlot(location); 461 if (plot == null) { 462 return; 463 } 464 switch (event.getSource().getType().toString()) { 465 case "GRASS_BLOCK": 466 if (!plot.getFlag(GrassGrowFlag.class)) { 467 plot.debug("Grass could not grow because grass-grow = false"); 468 event.setCancelled(true); 469 } 470 break; 471 case "MYCELIUM": 472 if (!plot.getFlag(MycelGrowFlag.class)) { 473 plot.debug("Mycelium could not grow because mycel-grow = false"); 474 event.setCancelled(true); 475 } 476 break; 477 case "WEEPING_VINES": 478 case "TWISTING_VINES": 479 case "CAVE_VINES": 480 case "VINE": 481 case "GLOW_BERRIES": 482 if (!plot.getFlag(VineGrowFlag.class)) { 483 plot.debug("Vine could not grow because vine-grow = false"); 484 event.setCancelled(true); 485 } 486 break; 487 case "KELP": 488 if (!plot.getFlag(KelpGrowFlag.class)) { 489 plot.debug("Kelp could not grow because kelp-grow = false"); 490 event.setCancelled(true); 491 } 492 case "BUDDING_AMETHYST": 493 if (!plot.getFlag(CropGrowFlag.class)) { 494 plot.debug("Amethyst clusters could not grow because crop-grow = false"); 495 event.setCancelled(true); 496 } 497 break; 498 } 499 } 500 501 @EventHandler(priority = EventPriority.HIGHEST) 502 public void onCauldronEmpty(CauldronLevelChangeEvent event) { 503 Entity entity = event.getEntity(); 504 Location location = BukkitUtil.adapt(event.getBlock().getLocation()); 505 PlotArea area = location.getPlotArea(); 506 if (area == null) { 507 return; 508 } 509 Plot plot = area.getPlot(location); 510 // TODO Add flags for specific control over cauldron changes (rain, dripstone...) 511 switch (event.getReason()) { 512 case BANNER_WASH, ARMOR_WASH, EXTINGUISH -> { 513 if (entity instanceof Player player) { 514 BukkitPlayer plotPlayer = BukkitUtil.adapt(player); 515 if (plot != null) { 516 if (!plot.hasOwner()) { 517 if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)) { 518 return; 519 } 520 } else if (!plot.isAdded(plotPlayer.getUUID())) { 521 if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) { 522 return; 523 } 524 } else { 525 return; 526 } 527 } else { 528 if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD)) { 529 return; 530 } 531 if (this.worldEdit != null && plotPlayer.getAttribute("worldedit")) { 532 if (player.getInventory().getItemInMainHand().getType() == Material 533 .getMaterial(this.worldEdit.getConfiguration().wandItem)) { 534 return; 535 } 536 } 537 } 538 } 539 if (event.getReason() == CauldronLevelChangeEvent.ChangeReason.EXTINGUISH && event.getEntity() != null) { 540 event.getEntity().setFireTicks(0); 541 } 542 // Though the players fire ticks are modified, 543 // the cauldron water level change is cancelled and the event should represent that. 544 event.setCancelled(true); 545 } 546 default -> { 547 // Bucket empty, Bucket fill, Bottle empty, Bottle fill are already handled in PlayerInteract event 548 // Evaporation or Unknown reasons do not need to be cancelled as they are considered natural causes 549 } 550 } 551 } 552 553 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 554 public void onBlockForm(BlockFormEvent event) { 555 if (event instanceof EntityBlockFormEvent) { 556 return; // handled below 557 } 558 Block block = event.getBlock(); 559 Location location = BukkitUtil.adapt(block.getLocation()); 560 if (location.isPlotRoad()) { 561 event.setCancelled(true); 562 return; 563 } 564 PlotArea area = location.getPlotArea(); 565 if (area == null) { 566 return; 567 } 568 Plot plot = area.getOwnedPlot(location); 569 if (plot == null) { 570 return; 571 } 572 if (!area.buildRangeContainsY(location.getY())) { 573 event.setCancelled(true); 574 return; 575 } 576 if (org.bukkit.Tag.SNOW.isTagged(event.getNewState().getType())) { 577 if (!plot.getFlag(SnowFormFlag.class)) { 578 plot.debug("Snow could not form because snow-form = false"); 579 event.setCancelled(true); 580 } 581 return; 582 } 583 if (org.bukkit.Tag.ICE.isTagged(event.getNewState().getType())) { 584 if (!plot.getFlag(IceFormFlag.class)) { 585 plot.debug("Ice could not form because ice-form = false"); 586 event.setCancelled(true); 587 } 588 } 589 } 590 591 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 592 public void onEntityBlockForm(EntityBlockFormEvent event) { 593 String world = event.getBlock().getWorld().getName(); 594 if (!this.plotAreaManager.hasPlotArea(world)) { 595 return; 596 } 597 Location location = BukkitUtil.adapt(event.getBlock().getLocation()); 598 PlotArea area = location.getPlotArea(); 599 if (area == null) { 600 return; 601 } 602 Plot plot = area.getOwnedPlot(location); 603 if (plot == null) { 604 event.setCancelled(true); 605 return; 606 } 607 Class<? extends BooleanFlag<?>> flag; 608 if (org.bukkit.Tag.SNOW.isTagged(event.getNewState().getType())) { 609 flag = SnowFormFlag.class; 610 } else if (org.bukkit.Tag.ICE.isTagged(event.getNewState().getType())) { 611 flag = IceFormFlag.class; 612 } else { 613 return; 614 } 615 boolean allowed = plot.getFlag(flag); 616 Entity entity = event.getEntity(); 617 if (entity instanceof Player player) { 618 BukkitPlayer plotPlayer = BukkitUtil.adapt(player); 619 if (!plot.isAdded(plotPlayer.getUUID())) { 620 if (allowed) { 621 return; // player is not added but forming <flag> is allowed 622 } 623 plot.debug(String.format( 624 "%s could not be formed because %s = false (entity is player)", 625 event.getNewState().getType(), 626 flag == SnowFormFlag.class ? "snow-form" : "ice-form" 627 )); 628 event.setCancelled(true); // player is not added and forming <flag> isn't allowed 629 } 630 return; // event is cancelled if not added and not allowed, otherwise forming <flag> is allowed 631 } 632 if (plot.hasOwner()) { 633 if (allowed) { 634 return; 635 } 636 plot.debug(String.format( 637 "%s could not be formed because %s = false (entity is not player)", 638 event.getNewState().getType(), 639 flag == SnowFormFlag.class ? "snow-form" : "ice-form" 640 )); 641 event.setCancelled(true); 642 } 643 } 644 645 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 646 public void onBlockDamage(BlockDamageEvent event) { 647 Player player = event.getPlayer(); 648 Location location = BukkitUtil.adapt(event.getBlock().getLocation()); 649 PlotArea area = location.getPlotArea(); 650 if (area == null) { 651 return; 652 } 653 if (player.getGameMode() != GameMode.SURVIVAL) { 654 return; 655 } 656 Plot plot = area.getPlot(location); 657 if (plot != null) { 658 if (plot.getFlag(InstabreakFlag.class)) { 659 Block block = event.getBlock(); 660 BlockBreakEvent call = new BlockBreakEvent(block, player); 661 Bukkit.getServer().getPluginManager().callEvent(call); 662 if (!call.isCancelled()) { 663 event.getBlock().breakNaturally(); 664 } 665 } 666 // == rather than <= as we only care about the "ground level" not being destroyed 667 if (location.getY() == area.getMinGenHeight()) { 668 event.setCancelled(true); 669 return; 670 } 671 if (!plot.hasOwner()) { 672 BukkitPlayer plotPlayer = BukkitUtil.adapt(player); 673 if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)) { 674 return; 675 } 676 event.setCancelled(true); 677 return; 678 } 679 BukkitPlayer plotPlayer = BukkitUtil.adapt(player); 680 if (!plot.isAdded(plotPlayer.getUUID())) { 681 List<BlockTypeWrapper> destroy = plot.getFlag(BreakFlag.class); 682 Block block = event.getBlock(); 683 if (destroy 684 .contains(BlockTypeWrapper.get(BukkitAdapter.asBlockType(block.getType()))) 685 || plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_OTHER)) { 686 return; 687 } 688 plot.debug(player.getName() + " could not break " + block.getType() 689 + " because it was not in the break flag"); 690 event.setCancelled(true); 691 return; 692 } 693 return; 694 } 695 BukkitPlayer plotPlayer = BukkitUtil.adapt(player); 696 if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_ROAD)) { 697 return; 698 } 699 event.setCancelled(true); 700 } 701 702 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 703 public void onFade(BlockFadeEvent event) { 704 Block block = event.getBlock(); 705 Location location = BukkitUtil.adapt(block.getLocation()); 706 PlotArea area = location.getPlotArea(); 707 if (area == null) { 708 return; 709 } 710 Plot plot = area.getOwnedPlot(location); 711 if (plot == null) { 712 event.setCancelled(true); 713 return; 714 } 715 Material blockType = block.getType(); 716 if (org.bukkit.Tag.ICE.isTagged(blockType)) { 717 if (!plot.getFlag(IceMeltFlag.class)) { 718 plot.debug("Ice could not melt because ice-melt = false"); 719 event.setCancelled(true); 720 } 721 return; 722 } 723 if (org.bukkit.Tag.SNOW.isTagged(blockType)) { 724 if (!plot.getFlag(SnowMeltFlag.class)) { 725 plot.debug("Snow could not melt because snow-melt = false"); 726 event.setCancelled(true); 727 } 728 return; 729 } 730 if (blockType == Material.FARMLAND) { 731 if (!plot.getFlag(SoilDryFlag.class)) { 732 plot.debug("Soil could not dry because soil-dry = false"); 733 event.setCancelled(true); 734 } 735 return; 736 } 737 if (CORAL_BLOCKS.isTagged(blockType) || CORALS.isTagged(blockType) || WALL_CORALS.isTagged(blockType)) { 738 if (!plot.getFlag(CoralDryFlag.class)) { 739 plot.debug("Coral could not dry because coral-dry = false"); 740 event.setCancelled(true); 741 } 742 } 743 } 744 745 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 746 public void onMoistureChange(MoistureChangeEvent event) { 747 Block block = event.getBlock(); 748 Location location = BukkitUtil.adapt(block.getLocation()); 749 PlotArea area = location.getPlotArea(); 750 751 if (area == null) { 752 return; 753 } 754 755 Plot plot = area.getOwnedPlot(location); 756 757 if (plot == null) { 758 event.setCancelled(true); 759 return; 760 } 761 762 if (block.getBlockData() instanceof Farmland farmland && event 763 .getNewState() 764 .getBlockData() instanceof Farmland newFarmland) { 765 int currentMoisture = farmland.getMoisture(); 766 int newMoisture = newFarmland.getMoisture(); 767 768 // farmland gets moisturizes 769 if (newMoisture > currentMoisture) { 770 return; 771 } 772 773 if (plot.getFlag(SoilDryFlag.class)) { 774 return; 775 } 776 777 plot.debug("Soil could not dry because soil-dry = false"); 778 event.setCancelled(true); 779 } 780 } 781 782 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 783 public void onChange(BlockFromToEvent event) { 784 Block fromBlock = event.getBlock(); 785 786 // Check liquid flow flag inside of origin plot too 787 final Location fromLocation = BukkitUtil.adapt(fromBlock.getLocation()); 788 final PlotArea fromArea = fromLocation.getPlotArea(); 789 if (fromArea != null) { 790 final Plot fromPlot = fromArea.getOwnedPlot(fromLocation); 791 if (fromPlot != null && fromPlot.getFlag(LiquidFlowFlag.class) == LiquidFlowFlag.FlowStatus.DISABLED && event 792 .getBlock() 793 .isLiquid()) { 794 fromPlot.debug("Liquid could not flow because liquid-flow = disabled"); 795 event.setCancelled(true); 796 return; 797 } 798 } 799 800 Block toBlock = event.getToBlock(); 801 Location toLocation = BukkitUtil.adapt(toBlock.getLocation()); 802 PlotArea toArea = toLocation.getPlotArea(); 803 if (toArea == null) { 804 if (fromBlock.getType() == Material.DRAGON_EGG && fromArea != null) { 805 event.setCancelled(true); 806 } 807 return; 808 } 809 if (!toArea.buildRangeContainsY(toLocation.getY())) { 810 event.setCancelled(true); 811 return; 812 } 813 Plot toPlot = toArea.getOwnedPlot(toLocation); 814 815 if (fromBlock.getType() == Material.DRAGON_EGG && fromArea != null) { 816 final Plot fromPlot = fromArea.getOwnedPlot(fromLocation); 817 818 if (fromPlot != null || toPlot != null) { 819 if ((fromPlot == null || !fromPlot.equals(toPlot)) && (toPlot == null || !toPlot.equals(fromPlot))) { 820 event.setCancelled(true); 821 return; 822 } 823 } 824 } 825 826 if (toPlot != null) { 827 if (!toArea.contains(fromLocation.getX(), fromLocation.getZ()) || !Objects.equals( 828 toPlot, 829 toArea.getOwnedPlot(fromLocation) 830 )) { 831 event.setCancelled(true); 832 return; 833 } 834 if (toPlot.getFlag(LiquidFlowFlag.class) == LiquidFlowFlag.FlowStatus.ENABLED && event.getBlock().isLiquid()) { 835 return; 836 } 837 if (toPlot.getFlag(DisablePhysicsFlag.class)) { 838 toPlot.debug(event.getBlock().getType() + " could not update because disable-physics = true"); 839 event.setCancelled(true); 840 return; 841 } 842 if (toPlot.getFlag(LiquidFlowFlag.class) == LiquidFlowFlag.FlowStatus.DISABLED && event.getBlock().isLiquid()) { 843 toPlot.debug("Liquid could not flow because liquid-flow = disabled"); 844 event.setCancelled(true); 845 } 846 } else if (!toArea.contains(fromLocation.getX(), fromLocation.getZ()) || !Objects.equals( 847 null, 848 toArea.getOwnedPlot(fromLocation) 849 )) { 850 event.setCancelled(true); 851 } else if (event.getBlock().isLiquid()) { 852 final org.bukkit.Location location = event.getBlock().getLocation(); 853 854 /* 855 X = block location 856 A-H = potential plot locations 857 Z 858 ^ 859 | A B C 860 o D X E 861 | F G H 862 v 863 <-----O-----> x 864 */ 865 if (BukkitUtil.adapt(location.clone().add(-1, 0, 1) /* A */).getPlot() != null 866 || BukkitUtil.adapt(location.clone().add(1, 0, 0) /* B */).getPlot() != null 867 || BukkitUtil.adapt(location.clone().add(1, 0, 1) /* C */).getPlot() != null 868 || BukkitUtil.adapt(location.clone().add(-1, 0, 0) /* D */).getPlot() != null 869 || BukkitUtil.adapt(location.clone().add(1, 0, 0) /* E */).getPlot() != null 870 || BukkitUtil.adapt(location.clone().add(-1, 0, -1) /* F */).getPlot() != null 871 || BukkitUtil.adapt(location.clone().add(0, 0, -1) /* G */).getPlot() != null 872 || BukkitUtil.adapt(location.clone().add(1, 0, 1) /* H */).getPlot() != null) { 873 event.setCancelled(true); 874 } 875 } 876 } 877 878 879 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 880 public void onGrow(BlockGrowEvent event) { 881 Block block = event.getBlock(); 882 Location location = BukkitUtil.adapt(block.getLocation()); 883 884 PlotArea area = location.getPlotArea(); 885 if (area == null) { 886 return; 887 } 888 889 if (!area.buildRangeContainsY(location.getY())) { 890 event.setCancelled(true); 891 return; 892 } 893 894 Plot plot = location.getOwnedPlot(); 895 if (plot == null || !plot.getFlag(CropGrowFlag.class)) { 896 if (plot != null) { 897 plot.debug("Crop grow event was cancelled because crop-grow = false"); 898 } 899 event.setCancelled(true); 900 } 901 } 902 903 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 904 public void onBlockPistonExtend(BlockPistonExtendEvent event) { 905 Block block = event.getBlock(); 906 Location location = BukkitUtil.adapt(block.getLocation()); 907 BlockFace face = event.getDirection(); 908 Vector relative = new Vector(face.getModX(), face.getModY(), face.getModZ()); 909 PlotArea area = location.getPlotArea(); 910 if (area == null) { 911 if (!this.plotAreaManager.hasPlotArea(location.getWorldName())) { 912 return; 913 } 914 for (Block block1 : event.getBlocks()) { 915 Location bloc = BukkitUtil.adapt(block1.getLocation()); 916 if (bloc.isPlotArea() || bloc 917 .add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ()) 918 .isPlotArea()) { 919 event.setCancelled(true); 920 return; 921 } 922 } 923 if (location.add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ()).isPlotArea()) { 924 // Prevent pistons from extending if they are: bordering a plot 925 // area, facing inside plot area, and not pushing any blocks 926 event.setCancelled(true); 927 } 928 return; 929 } 930 Plot plot = area.getOwnedPlot(location); 931 if (plot == null) { 932 event.setCancelled(true); 933 return; 934 } 935 for (Block block1 : event.getBlocks()) { 936 Location bloc = BukkitUtil.adapt(block1.getLocation()); 937 Location newLoc = bloc.add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ()); 938 if (!area.contains(bloc.getX(), bloc.getZ()) || !area.contains(newLoc)) { 939 event.setCancelled(true); 940 return; 941 } 942 if (!plot.equals(area.getOwnedPlot(bloc)) || !plot.equals(area.getOwnedPlot(newLoc))) { 943 event.setCancelled(true); 944 return; 945 } 946 if (!area.buildRangeContainsY(bloc.getY()) || !area.buildRangeContainsY(newLoc.getY())) { 947 event.setCancelled(true); 948 return; 949 } 950 } 951 if (!plot.equals(area.getOwnedPlot(location.add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ())))) { 952 // This branch is only necessary to prevent pistons from extending 953 // if they are: on a plot edge, facing outside the plot, and not 954 // pushing any blocks 955 event.setCancelled(true); 956 } 957 } 958 959 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 960 public void onBlockPistonRetract(BlockPistonRetractEvent event) { 961 Block block = event.getBlock(); 962 Location location = BukkitUtil.adapt(block.getLocation()); 963 BlockFace face = event.getDirection(); 964 Vector relative = new Vector(face.getModX(), face.getModY(), face.getModZ()); 965 PlotArea area = location.getPlotArea(); 966 if (area == null) { 967 if (!this.plotAreaManager.hasPlotArea(location.getWorldName())) { 968 return; 969 } 970 for (Block block1 : event.getBlocks()) { 971 Location bloc = BukkitUtil.adapt(block1.getLocation()); 972 Location newLoc = bloc.add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ()); 973 if (bloc.isPlotArea() || newLoc.isPlotArea()) { 974 event.setCancelled(true); 975 return; 976 } 977 } 978 return; 979 } 980 Plot plot = area.getOwnedPlot(location); 981 if (plot == null) { 982 event.setCancelled(true); 983 return; 984 } 985 for (Block block1 : event.getBlocks()) { 986 Location bloc = BukkitUtil.adapt(block1.getLocation()); 987 Location newLoc = bloc.add(relative.getBlockX(), relative.getBlockY(), relative.getBlockZ()); 988 if (!area.contains(bloc.getX(), bloc.getZ()) || !area.contains(newLoc)) { 989 event.setCancelled(true); 990 return; 991 } 992 if (!plot.equals(area.getOwnedPlot(bloc)) || !plot.equals(area.getOwnedPlot(newLoc))) { 993 event.setCancelled(true); 994 return; 995 } 996 if (!area.buildRangeContainsY(bloc.getY()) || !area.buildRangeContainsY(newLoc.getY())) { 997 event.setCancelled(true); 998 return; 999 } 1000 } 1001 } 1002 1003 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 1004 public void onBlockDispense(BlockDispenseEvent event) { 1005 if (!this.plotAreaManager.hasPlotArea(event.getBlock().getWorld().getName())) { 1006 return; 1007 } 1008 Material type = event.getItem().getType(); 1009 switch (type.toString()) { 1010 case "SHULKER_BOX", "WHITE_SHULKER_BOX", "ORANGE_SHULKER_BOX", "MAGENTA_SHULKER_BOX", "LIGHT_BLUE_SHULKER_BOX", 1011 "YELLOW_SHULKER_BOX", "LIME_SHULKER_BOX", "PINK_SHULKER_BOX", "GRAY_SHULKER_BOX", "LIGHT_GRAY_SHULKER_BOX", 1012 "CYAN_SHULKER_BOX", "PURPLE_SHULKER_BOX", "BLUE_SHULKER_BOX", "BROWN_SHULKER_BOX", "GREEN_SHULKER_BOX", 1013 "RED_SHULKER_BOX", "BLACK_SHULKER_BOX", "CARVED_PUMPKIN", "WITHER_SKELETON_SKULL", "FLINT_AND_STEEL", 1014 "BONE_MEAL", "SHEARS", "GLASS_BOTTLE", "GLOWSTONE", "COD_BUCKET", "PUFFERFISH_BUCKET", "SALMON_BUCKET", 1015 "TROPICAL_FISH_BUCKET", "AXOLOTL_BUCKET", "BUCKET", "WATER_BUCKET", "LAVA_BUCKET", "TADPOLE_BUCKET" -> { 1016 if (event.getBlock().getType() == Material.DROPPER) { 1017 return; 1018 } 1019 BlockFace targetFace = ((Dispenser) event.getBlock().getBlockData()).getFacing(); 1020 Location location = BukkitUtil.adapt(event.getBlock().getRelative(targetFace).getLocation()); 1021 if (location.isPlotRoad()) { 1022 event.setCancelled(true); 1023 return; 1024 } 1025 PlotArea area = location.getPlotArea(); 1026 if (area != null && !area.buildRangeContainsY(location.getY())) { 1027 event.setCancelled(true); 1028 } 1029 } 1030 } 1031 } 1032 1033 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 1034 public void onStructureGrow(StructureGrowEvent event) { 1035 if (!this.plotAreaManager.hasPlotArea(event.getWorld().getName())) { 1036 return; 1037 } 1038 List<org.bukkit.block.BlockState> blocks = event.getBlocks(); 1039 if (blocks.isEmpty()) { 1040 return; 1041 } 1042 Location location = BukkitUtil.adapt(blocks.get(0).getLocation()); 1043 PlotArea area = location.getPlotArea(); 1044 if (area == null) { 1045 for (int i = blocks.size() - 1; i >= 0; i--) { 1046 location = BukkitUtil.adapt(blocks.get(i).getLocation()); 1047 if (location.isPlotArea()) { 1048 blocks.remove(i); 1049 } 1050 } 1051 return; 1052 } else { 1053 Plot origin = area.getOwnedPlot(location); 1054 if (origin == null) { 1055 event.setCancelled(true); 1056 return; 1057 } 1058 for (int i = blocks.size() - 1; i >= 0; i--) { 1059 location = BukkitUtil.adapt(blocks.get(i).getLocation()); 1060 if (!area.contains(location.getX(), location.getZ())) { 1061 blocks.remove(i); 1062 continue; 1063 } 1064 Plot plot = area.getOwnedPlot(location); 1065 if (!Objects.equals(plot, origin)) { 1066 event.getBlocks().remove(i); 1067 continue; 1068 } 1069 if (!area.buildRangeContainsY(location.getY())) { 1070 event.getBlocks().remove(i); 1071 } 1072 } 1073 } 1074 Plot origin = area.getPlot(location); 1075 if (origin == null) { 1076 event.setCancelled(true); 1077 return; 1078 } 1079 for (int i = blocks.size() - 1; i >= 0; i--) { 1080 location = BukkitUtil.adapt(blocks.get(i).getLocation()); 1081 Plot plot = area.getOwnedPlot(location); 1082 /* 1083 * plot → the base plot of the merged area 1084 * origin → the plot where the event gets called 1085 */ 1086 1087 // Are plot and origin different AND are both plots merged 1088 if (!Objects.equals(plot, origin) && (!plot.isMerged() && !origin.isMerged())) { 1089 event.getBlocks().remove(i); 1090 } 1091 } 1092 } 1093 1094 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 1095 public void onBigBoom(BlockExplodeEvent event) { 1096 Block block = event.getBlock(); 1097 Location location = BukkitUtil.adapt(block.getLocation()); 1098 String world = location.getWorldName(); 1099 if (!this.plotAreaManager.hasPlotArea(world)) { 1100 return; 1101 } 1102 PlotArea area = location.getPlotArea(); 1103 if (area == null) { 1104 Iterator<Block> iterator = event.blockList().iterator(); 1105 while (iterator.hasNext()) { 1106 location = BukkitUtil.adapt(iterator.next().getLocation()); 1107 if (location.isPlotArea()) { 1108 iterator.remove(); 1109 } 1110 } 1111 return; 1112 } 1113 Plot plot = area.getOwnedPlot(location); 1114 if (plot == null || !plot.getFlag(ExplosionFlag.class)) { 1115 event.setCancelled(true); 1116 if (plot != null) { 1117 plot.debug("Explosion was cancelled because explosion = false"); 1118 } 1119 return; 1120 } 1121 event.blockList().removeIf(blox -> !plot.equals(area.getOwnedPlot(BukkitUtil.adapt(blox.getLocation())))); 1122 } 1123 1124 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 1125 public void onBlockBurn(BlockBurnEvent event) { 1126 Block block = event.getBlock(); 1127 Location location = BukkitUtil.adapt(block.getLocation()); 1128 1129 PlotArea area = location.getPlotArea(); 1130 if (area == null) { 1131 return; 1132 } 1133 1134 Plot plot = location.getOwnedPlot(); 1135 if (plot == null || !plot.getFlag(BlockBurnFlag.class)) { 1136 if (plot != null) { 1137 plot.debug("Block burning was cancelled because block-burn = false"); 1138 } 1139 event.setCancelled(true); 1140 } 1141 1142 } 1143 1144 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 1145 public void onBlockIgnite(BlockIgniteEvent event) { 1146 Player player = event.getPlayer(); 1147 Entity ignitingEntity = event.getIgnitingEntity(); 1148 Block block = event.getBlock(); 1149 BlockIgniteEvent.IgniteCause igniteCause = event.getCause(); 1150 Location location1 = BukkitUtil.adapt(block.getLocation()); 1151 PlotArea area = location1.getPlotArea(); 1152 if (area == null) { 1153 return; 1154 } 1155 if (igniteCause == BlockIgniteEvent.IgniteCause.LIGHTNING) { 1156 event.setCancelled(true); 1157 return; 1158 } 1159 1160 Plot plot = area.getOwnedPlot(location1); 1161 if (player != null) { 1162 BukkitPlayer pp = BukkitUtil.adapt(player); 1163 if (area.notifyIfOutsideBuildArea(pp, location1.getY())) { 1164 event.setCancelled(true); 1165 return; 1166 } 1167 if (plot == null) { 1168 if (!PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, BlockIgnitionFlag.class, true) && !pp.hasPermission( 1169 Permission.PERMISSION_ADMIN_BUILD_ROAD 1170 )) { 1171 pp.sendMessage( 1172 TranslatableCaption.of("permission.no_permission_event"), 1173 TagResolver.resolver( 1174 "node", 1175 Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD) 1176 ) 1177 ); 1178 event.setCancelled(true); 1179 } 1180 } else if (!plot.hasOwner()) { 1181 if (!PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, BlockIgnitionFlag.class, true) && !pp.hasPermission( 1182 Permission.PERMISSION_ADMIN_BUILD_UNOWNED 1183 )) { 1184 pp.sendMessage( 1185 TranslatableCaption.of("permission.no_permission_event"), 1186 TagResolver.resolver( 1187 "node", 1188 Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_UNOWNED) 1189 ) 1190 ); 1191 event.setCancelled(true); 1192 } 1193 } else if (!plot.isAdded(pp.getUUID())) { 1194 if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) { 1195 pp.sendMessage( 1196 TranslatableCaption.of("permission.no_permission_event"), 1197 TagResolver.resolver( 1198 "node", 1199 Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_OTHER) 1200 ) 1201 ); 1202 event.setCancelled(true); 1203 } 1204 } else if (!plot.getFlag(BlockIgnitionFlag.class)) { 1205 event.setCancelled(true); 1206 plot.debug("Block ignition was cancelled because block-ignition = false"); 1207 } 1208 } else { 1209 if (plot == null) { 1210 event.setCancelled(true); 1211 return; 1212 } 1213 if (ignitingEntity != null) { 1214 if (!plot.getFlag(BlockIgnitionFlag.class)) { 1215 event.setCancelled(true); 1216 plot.debug("Block ignition was cancelled because block-ignition = false"); 1217 return; 1218 } 1219 if (igniteCause == BlockIgniteEvent.IgniteCause.FIREBALL) { 1220 if (ignitingEntity instanceof Fireball) { 1221 Projectile fireball = (Projectile) ignitingEntity; 1222 Location location = null; 1223 if (fireball.getShooter() instanceof Entity shooter) { 1224 location = BukkitUtil.adapt(shooter.getLocation()); 1225 } else if (fireball.getShooter() instanceof BlockProjectileSource) { 1226 Block shooter = 1227 ((BlockProjectileSource) fireball.getShooter()).getBlock(); 1228 location = BukkitUtil.adapt(shooter.getLocation()); 1229 } 1230 if (location != null && !plot.equals(location.getPlot())) { 1231 event.setCancelled(true); 1232 } 1233 } 1234 } 1235 1236 } else if (event.getIgnitingBlock() != null) { 1237 Block ignitingBlock = event.getIgnitingBlock(); 1238 Plot plotIgnited = BukkitUtil.adapt(ignitingBlock.getLocation()).getPlot(); 1239 if (igniteCause == BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL && ( 1240 !plot.getFlag(BlockIgnitionFlag.class) || plotIgnited == null || !plotIgnited 1241 .equals(plot)) || (igniteCause == BlockIgniteEvent.IgniteCause.SPREAD 1242 || igniteCause == BlockIgniteEvent.IgniteCause.LAVA) && ( 1243 !plot.getFlag(BlockIgnitionFlag.class) || plotIgnited == null || !plotIgnited 1244 .equals(plot))) { 1245 event.setCancelled(true); 1246 } 1247 } 1248 } 1249 } 1250 1251 @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 1252 public void onLeavesDecay(LeavesDecayEvent event) { 1253 Block block = event.getBlock(); 1254 Location location = BukkitUtil.adapt(block.getLocation()); 1255 1256 PlotArea area = location.getPlotArea(); 1257 if (area == null) { 1258 return; 1259 } 1260 1261 Plot plot = location.getOwnedPlot(); 1262 if (plot == null || !plot.getFlag(LeafDecayFlag.class)) { 1263 if (plot != null) { 1264 plot.debug("Leaf decaying was cancelled because leaf-decay = false"); 1265 } 1266 event.setCancelled(true); 1267 } 1268 1269 } 1270 1271 @EventHandler(ignoreCancelled = true) 1272 public void onSpongeAbsorb(SpongeAbsorbEvent event) { 1273 Block sponge = event.getBlock(); 1274 Location location = BukkitUtil.adapt(sponge.getLocation()); 1275 PlotArea area = location.getPlotArea(); 1276 List<org.bukkit.block.BlockState> blocks = event.getBlocks(); 1277 if (area == null) { 1278 blocks.removeIf(block -> BukkitUtil.adapt(block.getLocation()).isPlotArea()); 1279 } else { 1280 Plot origin = area.getOwnedPlot(location); 1281 blocks.removeIf(block -> { 1282 Location blockLocation = BukkitUtil.adapt(block.getLocation()); 1283 if (!area.contains(blockLocation.getX(), blockLocation.getZ())) { 1284 return true; 1285 } 1286 Plot plot = area.getOwnedPlot(blockLocation); 1287 if (!Objects.equals(plot, origin)) { 1288 return true; 1289 } 1290 return !area.buildRangeContainsY(location.getY()); 1291 }); 1292 } 1293 if (blocks.isEmpty()) { 1294 // Cancel event so the sponge block doesn't turn into a wet sponge 1295 // if no water is being absorbed 1296 event.setCancelled(true); 1297 } 1298 } 1299 1300 /* 1301 * BlockMultiPlaceEvent is called unrelated to the BlockPlaceEvent itself and therefore doesn't respect the cancellation. 1302 */ 1303 @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) 1304 public void onBlockMultiPlace(BlockMultiPlaceEvent event) { 1305 // Check if the generic block place event would be cancelled 1306 blockCreate(event); 1307 if (event.isCancelled()) { 1308 return; 1309 } 1310 1311 BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer()); 1312 Location placedLocation = BukkitUtil.adapt(event.getBlockReplacedState().getLocation()); 1313 PlotArea area = placedLocation.getPlotArea(); 1314 if (area == null) { 1315 return; 1316 } 1317 Plot plot = placedLocation.getPlot(); 1318 1319 for (final BlockState state : event.getReplacedBlockStates()) { 1320 Location currentLocation = BukkitUtil.adapt(state.getLocation()); 1321 if (!pp.hasPermission( 1322 Permission.PERMISSION_ADMIN_BUILD_ROAD 1323 ) && !(Objects.equals(currentLocation.getPlot(), plot))) { 1324 pp.sendMessage( 1325 TranslatableCaption.of("permission.no_permission_event"), 1326 TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD)) 1327 ); 1328 event.setCancelled(true); 1329 break; 1330 } 1331 if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_HEIGHT_LIMIT)) { 1332 continue; 1333 } 1334 if (currentLocation.getY() >= area.getMaxBuildHeight() || currentLocation.getY() < area.getMinBuildHeight()) { 1335 pp.sendMessage( 1336 TranslatableCaption.of("height.height_limit"), 1337 TagResolver.builder() 1338 .tag("minheight", Tag.inserting(Component.text(area.getMinBuildHeight()))) 1339 .tag("maxheight", Tag.inserting(Component.text(area.getMaxBuildHeight()))) 1340 .build() 1341 ); 1342 if (area.notifyIfOutsideBuildArea(pp, currentLocation.getY())) { 1343 event.setCancelled(true); 1344 break; 1345 } 1346 } 1347 } 1348 1349 } 1350 1351}