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