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