diff --git a/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java b/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java index b483b9a9..d82beecf 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java @@ -42,7 +42,6 @@ import org.bukkit.entity.Tameable; import org.bukkit.inventory.ItemStack; -import javax.annotation.Nullable; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -320,9 +319,9 @@ public static Target createTarget(Block block) { * @param material a fallback material * @return a target */ - public static Target createTarget(@Nullable Block block, Material material) { + public static Target createTarget(Block block, Material material) { checkNotNull(material); - if (block != null) { + if (block.getType() == material) { return new MaterialTarget(block.getTypeId(), block.getData()); } else { return new MaterialTarget(material.getId(), (short) 0); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java index 72d06bd4..7b6a7fb4 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java @@ -110,23 +110,7 @@ public ApplicableRegionSet queryContains(Location location) { * @throws NullPointerException if there is no player for this query */ public boolean testPermission(Location location, Player player) { - checkNotNull(location); - checkNotNull(player); - - LocalPlayer localPlayer = plugin.wrapPlayer(player); - World world = location.getWorld(); - WorldConfiguration worldConfig = config.get(world); - - if (!worldConfig.useRegions) { - return true; - } - - if (globalManager.hasBypass(localPlayer, world)) { - return true; - } else { - RegionManager manager = globalManager.get(location.getWorld()); - return manager == null || cache.queryContains(manager, location).canBuild(localPlayer); - } + return testPermission(location, player, new StateFlag[0]); } /** @@ -151,8 +135,8 @@ public boolean testPermission(Location location, Player player) { */ public boolean testPermission(Location location, Player player, StateFlag... flags) { checkNotNull(location); - checkNotNull(flags); checkNotNull(player); + checkNotNull(flags); LocalPlayer localPlayer = plugin.wrapPlayer(player); World world = location.getWorld(); @@ -162,6 +146,10 @@ public boolean testPermission(Location location, Player player, StateFlag... fla return true; } + if (player.hasPermission("worldguard.region.bypass." + world.getName())) { + return true; + } + RegionManager manager = globalManager.get(location.getWorld()); if (manager != null) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java index a114578d..1047fa2e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java @@ -20,10 +20,14 @@ package com.sk89q.worldguard.bukkit.cause; import com.google.common.base.Joiner; +import com.sk89q.worldguard.bukkit.util.WGMetadata; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import org.bukkit.entity.TNTPrimed; import org.bukkit.entity.Tameable; import org.bukkit.entity.Vehicle; +import org.bukkit.metadata.Metadatable; import javax.annotation.Nullable; import java.util.ArrayList; @@ -43,6 +47,7 @@ */ public class Cause { + private static final String CAUSE_KEY = "worldguard.cause"; private static final Cause UNKNOWN = new Cause(Collections.emptyList()); private final List causes; @@ -77,6 +82,17 @@ public Player getPlayerRootCause() { return null; } + @Nullable + public Entity getEntityRootCause() { + for (Object object : causes) { + if (object instanceof Entity) { + return (Entity) object; + } + } + + return null; + } + @Override public String toString() { return Joiner.on(" | ").join(causes); @@ -95,12 +111,28 @@ private static void expand(List list, @Nullable Object ... element) { continue; } - if (o instanceof Projectile) { + // Add manually tracked parent causes + Object source = o; + int index = list.size(); + while (source instanceof Metadatable) { + source = WGMetadata.getIfPresent((Metadatable) source, CAUSE_KEY, Object.class); + if (source != null) { + list.add(index, source); + } + } + + if (o instanceof TNTPrimed) { + expand(list, ((TNTPrimed) o).getSource()); + list.add(o); + } else if (o instanceof Projectile) { expand(list, ((Projectile) o).getShooter()); + list.add(o); } else if (o instanceof Vehicle) { expand(list, ((Vehicle) o).getPassenger()); + list.add(o); } else if (o instanceof Tameable) { expand(list, ((Tameable) o).getOwner()); + list.add(o); } else { list.add(o); } @@ -135,4 +167,14 @@ public static Cause unknown() { return UNKNOWN; } + /** + * Add a parent cause to a {@code Metadatable} object. + * + * @param target the target + * @param parent the parent cause + */ + public static void trackParentCause(Metadatable target, Object parent) { + WGMetadata.put(target, CAUSE_KEY, parent); + } + } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/BulkEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/BulkEvent.java new file mode 100644 index 00000000..20df01b2 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/BulkEvent.java @@ -0,0 +1,38 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.event; + +/** + * A bulk event contains several affected objects in a list. + */ +public interface BulkEvent { + + /** + * Return whether the event is explicitly cancelled. + * + *

By default, bulk events will cancel itself if the number of affected + * objects drops to zero. This method returns the true cancellation + * status.

+ * + * @return true if really cancelled + */ + boolean isExplicitlyCancelled(); + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java index 6ebd57bf..bbd013af 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java @@ -19,56 +19,125 @@ package com.sk89q.worldguard.bukkit.event.block; +import com.google.common.base.Predicate; import com.sk89q.worldguard.bukkit.cause.Cause; import com.sk89q.worldguard.bukkit.event.AbstractDelegateEvent; +import com.sk89q.worldguard.bukkit.event.BulkEvent; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.Event; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; -abstract class AbstractBlockEvent extends AbstractDelegateEvent { +abstract class AbstractBlockEvent extends AbstractDelegateEvent implements BulkEvent { - private final Location target; - @Nullable - private final Block block; + private final World world; + private final List blocks; private final Material effectiveMaterial; - protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { + protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List blocks, Material effectiveMaterial) { super(originalEvent, cause); - checkNotNull(block); - this.target = block.getLocation(); - this.block = block; - this.effectiveMaterial = block.getType(); - } - - protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { - super(originalEvent, cause); - this.target = target; - this.block = null; + checkNotNull(world); + checkNotNull(blocks); + checkNotNull(effectiveMaterial); + this.world = world; + this.blocks = blocks; this.effectiveMaterial = effectiveMaterial; } - /** - * Get the target block being affected. - * - * @return a block - */ - public Location getTarget() { - return target; + protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { + this(originalEvent, cause, block.getWorld(), createList(checkNotNull(block)), block.getType()); + } + + protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + this(originalEvent, cause, target.getWorld(), createList(target.getBlock()), effectiveMaterial); + } + + private static List createList(Block block) { + List blocks = new ArrayList(); + blocks.add(block); + return blocks; + } + + @Override + public boolean isCancelled() { + return super.isCancelled() || blocks.isEmpty(); + } + + @Override + public boolean isExplicitlyCancelled() { + return super.isCancelled(); } /** - * Get the block. + * Get the world. * - * @return the block + * @return the world */ - @Nullable - public Block getBlock() { - return block; + public World getWorld() { + return world; + } + + /** + * Get the affected blocks. + * + * @return a list of affected block + */ + public List getBlocks() { + return blocks; + } + + /** + * Filter the list of affected blocks with the given predicate. If the + * predicate returns {@code false}, then the block is removed. + * + * @param predicate the predicate + * @param cancelEventOnFalse true to cancel the event and clear the block + * list once the predicate returns {@code false} + * @return true if one or more blocks were filtered out + */ + public boolean filterBlocks(Predicate predicate, boolean cancelEventOnFalse) { + boolean hasRemoval = false; + + Iterator it = blocks.iterator(); + while (it.hasNext()) { + if (!predicate.apply(it.next().getLocation())) { + hasRemoval = true; + + if (cancelEventOnFalse) { + getBlocks().clear(); + setCancelled(true); + break; + } else { + it.remove(); + } + } + } + + return hasRemoval; + } + + /** + * Filter the list of affected blocks with the given predicate. If the + * predicate returns {@code false}, then the block is removed. + * + *

This method will not fail fast and + * cancel the event the first instance that the predicate returns + * {@code false}. See {@link #filterBlocks(Predicate, boolean)} to adjust + * this behavior.

+ * + * @param predicate the predicate + * @return true if one or more blocks were filtered out + */ + public boolean filterBlocks(Predicate predicate) { + return filterBlocks(predicate, false); } /** diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java index 097eeaef..f5cc3088 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java @@ -22,16 +22,22 @@ import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import javax.annotation.Nullable; +import java.util.List; public class BreakBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); + public BreakBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List blocks, Material effectiveMaterial) { + super(originalEvent, cause, world, blocks, effectiveMaterial); + } + public BreakBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java index c6dd711d..ea8f1d49 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java @@ -22,16 +22,22 @@ import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import javax.annotation.Nullable; +import java.util.List; public class PlaceBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); + public PlaceBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List blocks, Material effectiveMaterial) { + super(originalEvent, cause, world, blocks, effectiveMaterial); + } + public PlaceBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java index 5c581196..921043ed 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java @@ -22,11 +22,13 @@ import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import javax.annotation.Nullable; +import java.util.List; /** * Fired when a block is interacted with. @@ -35,6 +37,10 @@ public class UseBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); + public UseBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List blocks, Material effectiveMaterial) { + super(originalEvent, cause, world, blocks, effectiveMaterial); + } + public UseBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java index 549981fe..3149071e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.bukkit.listener; +import com.google.common.base.Predicate; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.blacklist.event.BlockBreakBlacklistEvent; import com.sk89q.worldguard.blacklist.event.BlockDispenseBlacklistEvent; @@ -38,8 +39,8 @@ import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; import com.sk89q.worldguard.bukkit.util.Materials; +import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; import org.bukkit.entity.Player; @@ -68,75 +69,86 @@ public BlacklistListener(WorldGuardPlugin plugin) { } @EventHandler(ignoreCancelled = true) - public void onBreakBlock(BreakBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); + public void onBreakBlock(final BreakBlockEvent event) { + final Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; } - LocalPlayer localPlayer = getPlugin().wrapPlayer(player); - Block target = event.getBlock(); - WorldConfiguration wcfg = getWorldConfig(player); + final LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + final WorldConfiguration wcfg = getWorldConfig(player); // Blacklist guard if (wcfg.getBlacklist() == null) { return; } - if (!wcfg.getBlacklist().check( - new BlockBreakBlacklistEvent(localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { - event.setCancelled(true); - } else if (!wcfg.getBlacklist().check( - new ItemDestroyWithBlacklistEvent(localPlayer, toVector(event.getTarget()), createTarget(player.getItemInHand())), false, false)) { - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + if (!wcfg.getBlacklist().check( + new BlockBreakBlacklistEvent(localPlayer, toVector(target), createTarget(target.getBlock(), event.getEffectiveMaterial())), false, false)) { + return false; + } else if (!wcfg.getBlacklist().check( + new ItemDestroyWithBlacklistEvent(localPlayer, toVector(target), createTarget(player.getItemInHand())), false, false)) { + return false; + } + + return true; + } + }); } @EventHandler(ignoreCancelled = true) - public void onPlaceBlock(PlaceBlockEvent event) { + public void onPlaceBlock(final PlaceBlockEvent event) { Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; } - LocalPlayer localPlayer = getPlugin().wrapPlayer(player); - Block target = event.getBlock(); - WorldConfiguration wcfg = getWorldConfig(player); + final LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + final WorldConfiguration wcfg = getWorldConfig(player); // Blacklist guard if (wcfg.getBlacklist() == null) { return; } - if (!wcfg.getBlacklist().check(new BlockPlaceBlacklistEvent( - localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + return wcfg.getBlacklist().check(new BlockPlaceBlacklistEvent( + localPlayer, toVector(target), createTarget(target.getBlock(), event.getEffectiveMaterial())), false, false); + + } + }); } @EventHandler(ignoreCancelled = true) - public void onUseBlock(UseBlockEvent event) { + public void onUseBlock(final UseBlockEvent event) { Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; } - LocalPlayer localPlayer = getPlugin().wrapPlayer(player); - Block target = event.getBlock(); - WorldConfiguration wcfg = getWorldConfig(player); + final LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + final WorldConfiguration wcfg = getWorldConfig(player); // Blacklist guard if (wcfg.getBlacklist() == null) { return; } - if (!wcfg.getBlacklist().check(new BlockInteractBlacklistEvent( - localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + return wcfg.getBlacklist().check(new BlockInteractBlacklistEvent( + localPlayer, toVector(target), createTarget(target.getBlock(), event.getEffectiveMaterial())), false, false); + } + }); } @EventHandler(ignoreCancelled = true) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java index 9f8536c0..a2abeb54 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.bukkit.listener; +import com.google.common.base.Predicate; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; @@ -46,31 +47,36 @@ public ChestProtectionListener(WorldGuardPlugin plugin) { } @EventHandler(ignoreCancelled = true) - public void onPlaceBlock(PlaceBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); + public void onPlaceBlock(final PlaceBlockEvent event) { + final Player player = event.getCause().getPlayerRootCause(); if (player != null) { - WorldConfiguration wcfg = getWorldConfig(player); + final WorldConfiguration wcfg = getWorldConfig(player); // Early guard if (!wcfg.signChestProtection) { return; } - if (wcfg.getChestProtection().isChest(event.getEffectiveMaterial().getId()) && wcfg.isChestProtected(target.getBlock(), player)) { - player.sendMessage(ChatColor.DARK_RED + "This spot is for a chest that you don't have permission for."); - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + if (wcfg.getChestProtection().isChest(event.getEffectiveMaterial().getId()) && wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This spot is for a chest that you don't have permission for."); + return false; + } + + return true; + } + }, true); } } @EventHandler(ignoreCancelled = true) - public void onBreakBlock(BreakBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); + public void onBreakBlock(final BreakBlockEvent event) { + final Player player = event.getCause().getPlayerRootCause(); - WorldConfiguration wcfg = getWorldConfig(target.getWorld()); + final WorldConfiguration wcfg = getWorldConfig(event.getWorld()); // Early guard if (!wcfg.signChestProtection) { @@ -78,24 +84,33 @@ public void onBreakBlock(BreakBlockEvent event) { } if (player != null) { - if (wcfg.isChestProtected(target.getBlock(), player)) { - player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + if (wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); + return false; + } + + return true; + } + }, true); } else { - if (wcfg.isChestProtected(target.getBlock())) { - // No player? Deny anyway - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + return !wcfg.isChestProtected(target.getBlock()); + + } + }); } } @EventHandler(ignoreCancelled = true) - public void onUseBlock(UseBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); + public void onUseBlock(final UseBlockEvent event) { + final Player player = event.getCause().getPlayerRootCause(); - WorldConfiguration wcfg = getWorldConfig(target.getWorld()); + final WorldConfiguration wcfg = getWorldConfig(event.getWorld()); // Early guard if (!wcfg.signChestProtection) { @@ -103,15 +118,25 @@ public void onUseBlock(UseBlockEvent event) { } if (player != null) { - if (wcfg.isChestProtected(target.getBlock(), player)) { - player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + if (wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); + return false; + } + + return true; + } + }, true); } else { - if (wcfg.isChestProtected(target.getBlock())) { - // No player? Deny anyway - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + return !wcfg.isChestProtected(target.getBlock()); + + } + }); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java index 4943be13..3f527b4c 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java @@ -28,8 +28,13 @@ import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.event.Event; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import javax.annotation.Nullable; +import java.util.List; import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; @@ -50,22 +55,25 @@ public DebuggingListener(WorldGuardPlugin plugin, Logger logger) { this.logger = logger; } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onPlaceBlock(PlaceBlockEvent event) { StringBuilder builder = new StringBuilder(); builder.append("PLACE"); builder.append(" "); builder.append("").append(event.getEffectiveMaterial()); builder.append(" "); - builder.append("@").append(toBlockString(event.getTarget())); + builder.append("@").append(toBlockString(event.getBlocks())); builder.append(" "); builder.append("[").append(event.getCause()).append("]"); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onBreakBlock(BreakBlockEvent event) { StringBuilder builder = new StringBuilder(); builder.append("DIG"); @@ -74,13 +82,16 @@ public void onBreakBlock(BreakBlockEvent event) { builder.append(" "); builder.append("[").append(event.getCause()).append("]"); builder.append(" "); - builder.append("@").append(toBlockString(event.getTarget())); + builder.append("@").append(toBlockString(event.getBlocks())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onUseBlock(UseBlockEvent event) { StringBuilder builder = new StringBuilder(); builder.append("INTERACT"); @@ -89,13 +100,16 @@ public void onUseBlock(UseBlockEvent event) { builder.append(" "); builder.append("[").append(event.getCause()).append("]"); builder.append(" "); - builder.append("@").append(toBlockString(event.getTarget())); + builder.append("@").append(toBlockString(event.getBlocks())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onSpawnEntity(SpawnEntityEvent event) { StringBuilder builder = new StringBuilder(); builder.append("SPAWN"); @@ -106,11 +120,14 @@ public void onSpawnEntity(SpawnEntityEvent event) { builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onDestroyEntity(DestroyEntityEvent event) { StringBuilder builder = new StringBuilder(); builder.append("DESTROY"); @@ -121,11 +138,14 @@ public void onDestroyEntity(DestroyEntityEvent event) { builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onUseEntity(UseEntityEvent event) { StringBuilder builder = new StringBuilder(); builder.append("INTERACT"); @@ -136,11 +156,14 @@ public void onUseEntity(UseEntityEvent event) { builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onUseItem(UseItemEvent event) { StringBuilder builder = new StringBuilder(); builder.append("USE"); @@ -151,7 +174,10 @@ public void onUseItem(UseItemEvent event) { builder.append(" "); builder.append("@").append(event.getWorld().getName()); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } @@ -159,4 +185,21 @@ private static String toBlockString(Location location) { return location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); } + private static String toBlockString(List blocks) { + StringBuilder builder = new StringBuilder(); + boolean first = true; + for (Block block : blocks) { + if (!first) { + builder.append("|"); + } + builder.append(block.getX()).append(",").append(block.getY()).append(",").append(block.getZ()); + first = false; + } + return builder.toString(); + } + + private String getEventName(@Nullable Event event) { + return event != null ? event.getEventName() : "?"; + } + } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java index c4e83da4..95690988 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java @@ -31,7 +31,6 @@ import com.sk89q.worldguard.bukkit.util.Blocks; import com.sk89q.worldguard.bukkit.util.Events; import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.bukkit.util.WGMetadata; import org.bukkit.DyeColor; import org.bukkit.Material; import org.bukkit.World; @@ -67,6 +66,7 @@ import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityTameEvent; import org.bukkit.event.entity.EntityUnleashEvent; import org.bukkit.event.entity.PotionSplashEvent; @@ -101,7 +101,6 @@ public class EventAbstractionListener implements Listener { * Currently disabled as it creates a lot of new events. */ public static final boolean ABSTRACT_FROM_TO_EVENTS = false; - private static final String FALLING_SOURCE_KEY = "worldguard.fallingSource"; private final WorldGuardPlugin plugin; @@ -155,7 +154,7 @@ public void onEntityChangeBlock(EntityChangeBlockEvent event) { if (event.getTo() == Material.AIR) { // Track the source so later we can create a proper chain of causes if (entity instanceof FallingBlock) { - WGMetadata.put(entity, FALLING_SOURCE_KEY, block); + Cause.trackParentCause(entity, block); // Switch around the event Events.fireToCancel(event, new SpawnEntityEvent(event, create(block), entity)); @@ -163,23 +162,21 @@ public void onEntityChangeBlock(EntityChangeBlockEvent event) { Events.fireToCancel(event, new BreakBlockEvent(event, create(entity), event.getBlock())); } } else { - Cause cause; - - // Return the source for falling blocks - if (entity instanceof FallingBlock) { - Block source = WGMetadata.getIfPresent(entity, FALLING_SOURCE_KEY, Block.class); - cause = create(source, entity); - } else { - cause = create(entity); - } + Cause cause = create(entity); Events.fireToCancel(event, new PlaceBlockEvent(event, cause, event.getBlock().getLocation(), to)); } } } + @EventHandler + public void onEntityExplode(EntityExplodeEvent event) { + Entity entity = event.getEntity(); + + Events.fireBulkEventToCancel(event, new BreakBlockEvent(event, create(entity), event.getLocation().getWorld(), event.blockList(), Material.AIR)); + } + // TODO: Handle pistons - // TODO: Handle EntityExplodeEvent //------------------------------------------------------------------------- // Block external interaction diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index cd02b8ff..d87005ea 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -19,8 +19,12 @@ package com.sk89q.worldguard.bukkit.listener; +import com.google.common.base.Predicate; +import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.RegionQuery; +import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.cause.Cause; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; @@ -30,14 +34,20 @@ import com.sk89q.worldguard.bukkit.util.Entities; import com.sk89q.worldguard.bukkit.util.Materials; import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.StateFlag; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import javax.annotation.Nullable; + /** * Handle events that need to be processed by region protection. */ @@ -52,92 +62,196 @@ public RegionProtectionListener(WorldGuardPlugin plugin) { super(plugin); } + /** + * Tell a sender that s/he cannot do something 'here'. + * + * @param sender the sender + * @param subject the subject that the sender was blocked from touching + */ private void tellErrorMessage(CommandSender sender, Object subject) { sender.sendMessage(ChatColor.DARK_RED + "Sorry, but you are not allowed to do that here."); } + /** + * Return whether the given cause is whitelist (should be ignored). + * + * @param cause the cause + * @return true if whitelisted + */ + private boolean isWhitelisted(Cause cause) { + return false; + } + + /** + * Create a new predicate to test a state flag for each location. + * + * @param query the query + * @param flag the flag + * @return a predicate + */ + private Predicate createStateFlagPredicate(final RegionQuery query, final StateFlag flag) { + return new Predicate() { + @Override + public boolean apply(@Nullable Location location) { + return query.testState(location, null, flag); + } + }; + } + @EventHandler(ignoreCancelled = true) - public void onPlaceBlock(PlaceBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); - Material type = event.getEffectiveMaterial(); + public void onPlaceBlock(final PlaceBlockEvent event) { + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } - if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(); - boolean canPlace; + final Material type = event.getEffectiveMaterial(); - // Flint and steel, fire charge - if (type == Material.FIRE) { - canPlace = query.testPermission(target, player, DefaultFlag.LIGHTER); + final Player player; - } else { - canPlace = query.testPermission(target, player); - } + if ((player = event.getCause().getPlayerRootCause()) != null) { + final RegionQuery query = getPlugin().getRegionContainer().createQuery(); - if (!canPlace) { - tellErrorMessage(player, target); - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + boolean canPlace; + + // Flint and steel, fire charge + if (type == Material.FIRE) { + canPlace = query.testPermission(target, player, DefaultFlag.LIGHTER); + + } else { + canPlace = query.testPermission(target, player); + } + + if (!canPlace) { + tellErrorMessage(player, target); + return false; + } + + return true; + } + }); + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) - public void onBreakBlock(BreakBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); + public void onBreakBlock(final BreakBlockEvent event) { + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } - if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(); - boolean canBreak = query.testPermission(target, player); + final RegionQuery query = getPlugin().getRegionContainer().createQuery(); + ConfigurationManager globalConfig = getPlugin().getGlobalStateManager(); + WorldConfiguration config = globalConfig.get(event.getWorld()); + + final Player player; + final Entity entity; + + // ==================================================================== + // Player caused + // ==================================================================== + + if ((player = event.getCause().getPlayerRootCause()) != null) { + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + boolean canBreak = query.testPermission(target, player); + + if (!canBreak) { + tellErrorMessage(player, target); + return false; + } + + return true; + } + }); + + // ==================================================================== + // Entity caused + // ==================================================================== + + } else if ((entity = event.getCause().getEntityRootCause()) != null) { + // Creeper + if (entity instanceof Creeper) { + event.filterBlocks(createStateFlagPredicate(query, DefaultFlag.CREEPER_EXPLOSION), config.explosionFlagCancellation); + + // Enderdragon + } else if (entity instanceof EnderDragon) { + event.filterBlocks(createStateFlagPredicate(query, DefaultFlag.ENDERDRAGON_BLOCK_DAMAGE), config.explosionFlagCancellation); + + // TNT + explosive TNT carts + } else if (Entities.isTNTBased(entity)) { + event.filterBlocks(createStateFlagPredicate(query, DefaultFlag.TNT), config.explosionFlagCancellation); - if (!canBreak) { - tellErrorMessage(player, target); - event.setCancelled(true); } + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) - public void onUseBlock(UseBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); - Material type = event.getEffectiveMaterial(); + public void onUseBlock(final UseBlockEvent event) { + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } - if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(); - boolean canUse; + final Material type = event.getEffectiveMaterial(); - // Inventory blocks (CHEST_ACCESS) - if (Materials.isInventoryBlock(type)) { - canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS); + final Player player; - // Beds (SLEEP) - } else if (type == Material.BED) { - canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.SLEEP); + if ((player = event.getCause().getPlayerRootCause()) != null) { + final RegionQuery query = getPlugin().getRegionContainer().createQuery(); - // TNT (TNT) - } else if (type == Material.TNT) { - canUse = query.testPermission(target, player, DefaultFlag.TNT); + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + boolean canUse; - // Everything else - } else { - canUse = query.testPermission(target, player, DefaultFlag.USE); - } + // Inventory blocks (CHEST_ACCESS) + if (Materials.isInventoryBlock(type)) { + canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS); - if (!canUse) { - tellErrorMessage(player, target); - event.setCancelled(true); - } + // Beds (SLEEP) + } else if (type == Material.BED) { + canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.SLEEP); + + // TNT (TNT) + } else if (type == Material.TNT) { + canUse = query.testPermission(target, player, DefaultFlag.TNT); + + // Everything else + } else { + canUse = query.testPermission(target, player, DefaultFlag.USE); + } + + if (!canUse) { + tellErrorMessage(player, target); + return false; + } + + return true; + } + }); + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) public void onSpawnEntity(SpawnEntityEvent event) { - Player player = event.getCause().getPlayerRootCause(); + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } + Location target = event.getTarget(); EntityType type = event.getEffectiveType(); - if (player != null) { + Player player; + + if ((player = event.getCause().getPlayerRootCause()) != null) { RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canSpawn; @@ -151,16 +265,23 @@ public void onSpawnEntity(SpawnEntityEvent event) { tellErrorMessage(player, target); event.setCancelled(true); } + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) public void onDestroyEntity(DestroyEntityEvent event) { - Player player = event.getCause().getPlayerRootCause(); + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } + Location target = event.getTarget(); EntityType type = event.getEntity().getType(); - if (player != null) { + Player player; + + if ((player = event.getCause().getPlayerRootCause()) != null) { RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canDestroy; @@ -174,15 +295,22 @@ public void onDestroyEntity(DestroyEntityEvent event) { tellErrorMessage(player, target); event.setCancelled(true); } + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) public void onUseEntity(UseEntityEvent event) { - Player player = event.getCause().getPlayerRootCause(); + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } + Location target = event.getTarget(); - if (player != null) { + Player player; + + if ((player = event.getCause().getPlayerRootCause()) != null) { RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canUse = query.testPermission(target, player, DefaultFlag.USE); @@ -190,6 +318,8 @@ public void onUseEntity(UseEntityEvent event) { tellErrorMessage(player, target); event.setCancelled(true); } + } else { + event.setCancelled(true); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java index ad740a6e..89031adc 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java @@ -569,35 +569,11 @@ public void onEntityExplode(EntityExplodeEvent event) { event.blockList().clear(); return; } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.CREEPER_EXPLOSION)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } } else if (ent instanceof EnderDragon) { if (wcfg.blockEnderDragonBlockDamage) { event.blockList().clear(); return; } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.ENDERDRAGON_BLOCK_DAMAGE)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } } else if (ent instanceof TNTPrimed || ent instanceof ExplosiveMinecart) { if (wcfg.blockTNTExplosions) { event.setCancelled(true); @@ -607,18 +583,6 @@ public void onEntityExplode(EntityExplodeEvent event) { event.blockList().clear(); return; } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.TNT)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } } else if (ent instanceof Fireball) { if (ent instanceof WitherSkull) { if (wcfg.blockWitherSkullExplosions) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java index 765b9f0e..4704a342 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java @@ -22,7 +22,9 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Projectile; +import org.bukkit.entity.TNTPrimed; import org.bukkit.entity.Tameable; +import org.bukkit.entity.minecart.ExplosiveMinecart; import org.bukkit.projectiles.ProjectileSource; import javax.annotation.Nullable; @@ -45,11 +47,11 @@ public static boolean isTamed(@Nullable Entity entity) { /** * Return if the given entity type is TNT-based. * - * @param type the type + * @param entity the entity * @return true if TNT based */ - public static boolean isTNTBased(EntityType type) { - return type == EntityType.PRIMED_TNT || type == EntityType.MINECART_TNT; + public static boolean isTNTBased(Entity entity) { + return entity instanceof TNTPrimed || entity instanceof ExplosiveMinecart; } /** diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java index 6ad8ec73..637fe1fe 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.bukkit.util; +import com.sk89q.worldguard.bukkit.event.BulkEvent; import org.bukkit.Bukkit; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; @@ -83,4 +84,23 @@ public static boolean fireItemEventToCancel(Play return false; } + /** + * Fire the {@code eventToFire} and cancel the original if the fired event + * is explicitly cancelled. + * + * @param original the original event to potentially cancel + * @param eventToFire the event to fire to consider cancelling the original event + * @param an event that can be fired and is cancellable + * @return true if the event was fired and it caused the original event to be cancelled + */ + public static boolean fireBulkEventToCancel(Cancellable original, T eventToFire) { + Bukkit.getServer().getPluginManager().callEvent(eventToFire); + if (eventToFire.isExplicitlyCancelled()) { + original.setCancelled(true); + return true; + } + + return false; + } + }