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