Add on-equip blacklist event.

Note that due to deficiencies in the Bukkit API, the event will attempt
to be overprotective and cancel some events that may not actually result
in the player equipping an item. Maintainable PRs welcome. (i.e. would
rather not have to have a list of slot numbers for various inventories
and match them to items :upside_down:)
This commit is contained in:
wizjany 2019-06-25 18:44:20 -04:00
parent aabb0b2345
commit af3eb85962
5 changed files with 187 additions and 14 deletions

View File

@ -19,8 +19,6 @@
package com.sk89q.worldguard.bukkit.listener;
import static com.sk89q.worldguard.bukkit.BukkitUtil.createTarget;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard;
@ -31,6 +29,7 @@
import com.sk89q.worldguard.blacklist.event.ItemAcquireBlacklistEvent;
import com.sk89q.worldguard.blacklist.event.ItemDestroyWithBlacklistEvent;
import com.sk89q.worldguard.blacklist.event.ItemDropBlacklistEvent;
import com.sk89q.worldguard.blacklist.event.ItemEquipBlacklistEvent;
import com.sk89q.worldguard.blacklist.event.ItemUseBlacklistEvent;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent;
@ -49,14 +48,20 @@
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.BlockDispenseArmorEvent;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCreativeEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import static com.sk89q.worldguard.bukkit.BukkitUtil.createTarget;
/**
* Handle events that need to be processed by the blacklist.
*/
@ -89,10 +94,12 @@ public void onBreakBlock(final BreakBlockEvent event) {
event.filter(target -> {
if (!wcfg.getBlacklist().check(
new BlockBreakBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(target), createTarget(target.getBlock(), event.getEffectiveMaterial())), false, false)) {
new BlockBreakBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(target),
createTarget(target.getBlock(), event.getEffectiveMaterial())), false, false)) {
return false;
} else if (!wcfg.getBlacklist().check(
new ItemDestroyWithBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(target), createTarget(player.getItemInHand())), false, false)) {
new ItemDestroyWithBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(target),
createTarget(player.getInventory().getItemInMainHand())), false, false)) {
return false;
}
@ -161,7 +168,8 @@ public void onSpawnEntity(SpawnEntityEvent event) {
Material material = Materials.getRelatedMaterial(event.getEffectiveType());
if (material != null) {
if (!wcfg.getBlacklist().check(new ItemUseBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(event.getTarget()), createTarget(material)), false, false)) {
if (!wcfg.getBlacklist().check(new ItemUseBlacklistEvent(
localPlayer, BukkitAdapter.asBlockVector(event.getTarget()), createTarget(material)), false, false)) {
event.setCancelled(true);
}
}
@ -197,7 +205,8 @@ public void onDestroyEntity(DestroyEntityEvent event) {
Material material = Materials.getRelatedMaterial(target.getType());
if (material != null) {
// Not really a block but we only have one on-break blacklist event
if (!wcfg.getBlacklist().check(new BlockBreakBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(event.getTarget()), createTarget(material)), false, false)) {
if (!wcfg.getBlacklist().check(new BlockBreakBlacklistEvent(
localPlayer, BukkitAdapter.asBlockVector(event.getTarget()), createTarget(material)), false, false)) {
event.setCancelled(true);
}
}
@ -220,7 +229,14 @@ public void onUseItem(UseItemEvent event) {
return;
}
if (!wcfg.getBlacklist().check(new ItemUseBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(player.getLocation()), createTarget(target)), false, false)) {
if (!wcfg.getBlacklist().check(new ItemUseBlacklistEvent(
localPlayer, BukkitAdapter.asBlockVector(player.getLocation()), createTarget(target)), false, false)) {
event.setCancelled(true);
return;
}
if (Materials.isArmor(target.getType()) && !wcfg.getBlacklist().check(new ItemEquipBlacklistEvent(
localPlayer, BukkitAdapter.asBlockVector(player.getLocation()), createTarget(target)), false, false)) {
event.setCancelled(true);
}
}
@ -257,10 +273,11 @@ public void onBlockDispense(BlockDispenseEvent event) {
@EventHandler(ignoreCancelled = true)
public void onInventoryClick(InventoryClickEvent event) {
HumanEntity entity = event.getWhoClicked();
if (!(entity instanceof Player)) return;
Inventory inventory = event.getInventory();
ItemStack item = event.getCurrentItem();
if (item != null && inventory != null && inventory.getHolder() != null && entity instanceof Player) {
if (item != null && inventory.getHolder() != null) {
Player player = (Player) entity;
ConfigurationManager cfg = WorldGuard.getInstance().getPlatform().getGlobalStateManager();
WorldConfiguration wcfg = cfg.get(BukkitAdapter.adapt(entity.getWorld()));
@ -274,15 +291,67 @@ public void onInventoryClick(InventoryClickEvent event) {
event.setCurrentItem(null);
}
}
ItemStack equipped = checkEquipped(event);
if (equipped != null) {
if (wcfg.getBlacklist() != null && !wcfg.getBlacklist().check(new ItemEquipBlacklistEvent(localPlayer,
BukkitAdapter.asBlockVector(player.getLocation()), createTarget(equipped)), false, false)) {
event.setCancelled(true);
}
}
}
}
@EventHandler(ignoreCancelled = true)
public void onInventoryDrag(InventoryDragEvent event) {
HumanEntity entity = event.getWhoClicked();
if (!(entity instanceof Player)) return;
if (event.getInventory().getType() != InventoryType.PLAYER
&& event.getInventory().getType() != InventoryType.CRAFTING) return;
if (event.getRawSlots().stream().anyMatch(i -> i >= 5 && i <= 8)) { // dropped on armor slots
Player player = (Player) entity;
ConfigurationManager cfg = WorldGuard.getInstance().getPlatform().getGlobalStateManager();
WorldConfiguration wcfg = cfg.get(BukkitAdapter.adapt(entity.getWorld()));
LocalPlayer localPlayer = getPlugin().wrapPlayer(player);
if (wcfg.getBlacklist() != null && !wcfg.getBlacklist().check(new ItemEquipBlacklistEvent(localPlayer,
BukkitAdapter.asBlockVector(player.getLocation()), createTarget(event.getOldCursor())), false, false)) {
event.setCancelled(true);
}
}
}
private ItemStack checkEquipped(InventoryClickEvent event) {
final Inventory clickedInventory = event.getClickedInventory();
if (event.getSlotType() == InventoryType.SlotType.ARMOR) {
switch (event.getAction()) {
case PLACE_ONE:
case PLACE_SOME:
case PLACE_ALL:
case SWAP_WITH_CURSOR:
final ItemStack cursor = event.getCursor();
if (cursor != null) {
return cursor;
}
case HOTBAR_SWAP:
return clickedInventory == null ? null : clickedInventory.getItem(event.getHotbarButton());
default:
break;
}
} else if (clickedInventory != null && clickedInventory.getType() == InventoryType.PLAYER
&& event.getView().getTopInventory().getType() == InventoryType.PLAYER
&& event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) {
return event.getCurrentItem();
}
return null;
}
@EventHandler(ignoreCancelled = true)
public void onInventoryCreative(InventoryCreativeEvent event) {
HumanEntity entity = event.getWhoClicked();
ItemStack item = event.getCursor();
if (item != null && entity instanceof Player) {
if (item.getType() != Material.AIR && entity instanceof Player) {
Player player = (Player) entity;
ConfigurationManager cfg = WorldGuard.getInstance().getPlatform().getGlobalStateManager();
WorldConfiguration wcfg = cfg.get(BukkitAdapter.adapt(entity.getWorld()));
@ -291,7 +360,6 @@ public void onInventoryCreative(InventoryCreativeEvent event) {
if (wcfg.getBlacklist() != null && !wcfg.getBlacklist().check(
new ItemAcquireBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(entity.getLocation()), createTarget(item)), false, false)) {
event.setCancelled(true);
event.setCursor(null);
}
}
}
@ -314,4 +382,18 @@ public void onPlayerItemHeld(PlayerItemHeldEvent event) {
}
}
@EventHandler(ignoreCancelled = true)
public void onBlockDispenseArmor(BlockDispenseArmorEvent event) {
if (!(event.getTargetEntity() instanceof Player)) return;
Player player = ((Player) event.getTargetEntity());
ItemStack stack = event.getItem();
ConfigurationManager cfg = WorldGuard.getInstance().getPlatform().getGlobalStateManager();
WorldConfiguration wcfg = cfg.get(BukkitAdapter.adapt(player.getWorld()));
LocalPlayer localPlayer = getPlugin().wrapPlayer(player);
if (wcfg.getBlacklist() != null && !wcfg.getBlacklist().check(
new ItemEquipBlacklistEvent(localPlayer, BukkitAdapter.asBlockVector(player.getLocation()), createTarget(stack)), false, true)) {
event.setCancelled(true);
}
}
}

View File

@ -28,6 +28,7 @@
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashSet;
@ -36,8 +37,6 @@
import java.util.logging.Logger;
import java.util.stream.Stream;
import javax.annotation.Nullable;
/**
* Material utility class.
*/
@ -1264,4 +1263,42 @@ public static boolean hasDamageEffect(Collection<PotionEffect> effects) {
return false;
}
// should match instances of ItemArmor
/**
* Check if the material is equippable armor (i.e. that it is equipped on right-click
* not necessarily that it can be put in the armor slots)
*
* @param type material to check
* @return true if equippable armor
*/
public static boolean isArmor(Material type) {
switch (type) {
case LEATHER_HELMET:
case LEATHER_CHESTPLATE:
case LEATHER_LEGGINGS:
case LEATHER_BOOTS:
case CHAINMAIL_HELMET:
case CHAINMAIL_CHESTPLATE:
case CHAINMAIL_LEGGINGS:
case CHAINMAIL_BOOTS:
case IRON_HELMET:
case IRON_CHESTPLATE:
case IRON_LEGGINGS:
case IRON_BOOTS:
case DIAMOND_HELMET:
case DIAMOND_CHESTPLATE:
case DIAMOND_LEGGINGS:
case DIAMOND_BOOTS:
case GOLDEN_HELMET:
case GOLDEN_CHESTPLATE:
case GOLDEN_LEGGINGS:
case GOLDEN_BOOTS:
//case TURTLE_HELMET:
return true;
default:
return false;
}
}
}

View File

@ -19,6 +19,8 @@
# - on-interact (when a block in used (doors, chests, etc.))
# - on-drop (an item is being dropped from the player's inventory)
# - on-acquire (an item enters a player's inventory via some method)
# - on-equip (an item is equipped to the player's armor slots)
# NOTE: on-equip is overprotective due to deficiencies in Bukkit API
# - on-dispense (a dispenser is about to dispense an item)
#
# Actions (for events):
@ -40,7 +42,7 @@
###############################################################################
#
# For more information, see:
# http://wiki.sk89q.com/wiki/WorldGuard/Blacklist
# https://worldguard.readthedocs.io/en/latest/blacklist/
#
###############################################################################
#

View File

@ -27,13 +27,14 @@ public enum EventType {
DISPENSE(BlockDispenseBlacklistEvent.class, "on-dispense"),
DESTROY_WITH(ItemDestroyWithBlacklistEvent.class, "on-destroy-with"),
ACQUIRE(ItemAcquireBlacklistEvent.class, "on-acquire"),
EQUIP(ItemEquipBlacklistEvent.class, "on-equip"),
DROP(ItemDropBlacklistEvent.class, "on-drop"),
USE(ItemUseBlacklistEvent.class, "on-use");
private final Class<? extends BlacklistEvent> eventClass;
private final String ruleName;
private EventType(Class<? extends BlacklistEvent> eventClass, String ruleName) {
EventType(Class<? extends BlacklistEvent> eventClass, String ruleName) {
this.eventClass = eventClass;
this.ruleName = ruleName;
}

View File

@ -0,0 +1,51 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.blacklist.event;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.blacklist.target.Target;
import javax.annotation.Nullable;
public final class ItemEquipBlacklistEvent extends ItemBlacklistEvent {
/**
* Construct the object.
*
* @param player The player associated with this event
* @param position The position the event occurred at
* @param target The target of the event
*/
public ItemEquipBlacklistEvent(@Nullable LocalPlayer player, BlockVector3 position, Target target) {
super(player, position, target);
}
@Override
public String getDescription() {
return "equip";
}
@Override
public EventType getEventType() {
return EventType.EQUIP;
}
}