From 528fda95ffced8472cfb07189670948d87bb3ef9 Mon Sep 17 00:00:00 2001 From: ryanhamshire Date: Tue, 16 Dec 2014 21:00:42 -0800 Subject: [PATCH] Added pet protections. Tweaked drop protections. --- .../GriefPrevention/EntityEventHandler.java | 130 ++++++++++++++---- .../GriefPrevention/GriefPrevention.java | 11 +- .../GriefPrevention/ItemStackOwnerInfo.java | 17 --- .../PendingItemProtection.java | 21 +++ .../GriefPrevention/PlayerEventHandler.java | 73 ++++++---- 5 files changed, 180 insertions(+), 72 deletions(-) delete mode 100644 src/me/ryanhamshire/GriefPrevention/ItemStackOwnerInfo.java create mode 100644 src/me/ryanhamshire/GriefPrevention/PendingItemProtection.java diff --git a/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java b/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java index 049d446..6a0fc32 100644 --- a/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java @@ -18,11 +18,14 @@ package me.ryanhamshire.GriefPrevention; +import java.util.ArrayList; import java.util.Calendar; import java.util.List; +import java.util.UUID; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Block; @@ -32,10 +35,12 @@ import org.bukkit.entity.Enderman; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Explosive; +import org.bukkit.entity.Item; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Monster; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import org.bukkit.entity.Tameable; import org.bukkit.entity.Villager; import org.bukkit.event.EventHandler; @@ -58,6 +63,7 @@ import org.bukkit.event.hanging.HangingBreakEvent.RemoveCause; import org.bukkit.event.hanging.HangingPlaceEvent; import org.bukkit.event.vehicle.VehicleDamageEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; //handles events related to entities class EntityEventHandler implements Listener @@ -181,6 +187,49 @@ class EntityEventHandler implements Listener { event.setCancelled(true); } + + //if item is on watch list, apply protection + ArrayList watchList = GriefPrevention.instance.pendingItemWatchList; + Item newItem = event.getEntity(); + Long now = null; + for(int i = 0; i < watchList.size(); i++) + { + PendingItemProtection pendingProtection = watchList.get(i); + //ignore and remove any expired pending protections + if(now == null) now = System.currentTimeMillis(); + if(pendingProtection.expirationTimestamp < now) + { + watchList.remove(i--); + continue; + } + //skip if item stack doesn't match + if(pendingProtection.itemStack.getAmount() != newItem.getItemStack().getAmount() || + pendingProtection.itemStack.getType() != newItem.getItemStack().getType()) + { + continue; + } + + //skip if new item location isn't near the expected spawn area + Location spawn = event.getLocation(); + Location expected = pendingProtection.location; + if(!spawn.getWorld().equals(expected.getWorld()) || + spawn.getX() < expected.getX() - 5 || + spawn.getX() > expected.getX() + 5 || + spawn.getZ() < expected.getZ() - 5 || + spawn.getZ() > expected.getZ() + 5 || + spawn.getY() < expected.getY() - 15 || + spawn.getY() > expected.getY() + 3) + { + continue; + } + + //otherwise, mark item with protection information + newItem.setMetadata("GP_ITEMOWNER", new FixedMetadataValue(GriefPrevention.instance, pendingProtection.owner)); + + //and remove pending protection data + watchList.remove(i); + break; + } } //when an experience bottle explodes... @@ -219,36 +268,11 @@ class EntityEventHandler implements Listener } //when an entity dies... - @EventHandler + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onEntityDeath(EntityDeathEvent event) { LivingEntity entity = event.getEntity(); - //FEATURE: lock dropped items to player who dropped them - - if(entity instanceof Player) - { - Player player = (Player)entity; - World world = entity.getWorld(); - - //decide whether or not to apply this feature to this situation (depends on the world wher it happens) - boolean isPvPWorld = GriefPrevention.instance.config_pvp_enabledWorlds.contains(world); - if((isPvPWorld && GriefPrevention.instance.config_lockDeathDropsInPvpWorlds) || - (!isPvPWorld && GriefPrevention.instance.config_lockDeathDropsInNonPvpWorlds)) - { - //mark the dropped stacks with player's UUID - List drops = event.getDrops(); - for(ItemStack stack : drops) - { - GriefPrevention.instance.itemStackOwnerMap.put(stack, new ItemStackOwnerInfo(player.getUniqueId(), player.getLocation())); - } - - //allow the player to receive a message about how to unlock any drops - PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); - playerData.receivedDropUnlockAdvertisement = false; - } - } - //don't do the rest in worlds where claims are not enabled if(!GriefPrevention.instance.claimsEnabledForWorld(entity.getWorld())) return; @@ -276,6 +300,31 @@ class EntityEventHandler implements Listener //end it, with the dieing player being the loser this.dataStore.endSiege(playerData.siegeData, null, player.getName(), true /*ended due to death*/); } + + //FEATURE: lock dropped items to player who dropped them + + World world = entity.getWorld(); + + //decide whether or not to apply this feature to this situation (depends on the world where it happens) + boolean isPvPWorld = GriefPrevention.instance.config_pvp_enabledWorlds.contains(world); + if((isPvPWorld && GriefPrevention.instance.config_lockDeathDropsInPvpWorlds) || + (!isPvPWorld && GriefPrevention.instance.config_lockDeathDropsInNonPvpWorlds)) + { + //remember information about these drops so that they can be marked when they spawn as items + long expirationTime = System.currentTimeMillis() + 3000; //now + 3 seconds + Location deathLocation = player.getLocation(); + UUID playerID = player.getUniqueId(); + List drops = event.getDrops(); + for(ItemStack stack : drops) + { + GriefPrevention.instance.pendingItemWatchList.add( + new PendingItemProtection(deathLocation, playerID, expirationTime, stack)); + } + + //allow the player to receive a message about how to unlock any drops + playerData.dropsAreUnlocked = false; + playerData.receivedDropUnlockAdvertisement = false; + } } //when an entity picks up an item @@ -527,7 +576,34 @@ class EntityEventHandler implements Listener //if the entity is an non-monster creature (remember monsters disqualified above), or a vehicle if ((subEvent.getEntity() instanceof Creature && GriefPrevention.instance.config_claims_protectCreatures)) { - Claim cachedClaim = null; + //if entity is tameable and has an owner, apply special rules + if(subEvent.getEntity() instanceof Tameable) + { + Tameable tameable = (Tameable)subEvent.getEntity(); + if(tameable.isTamed() && tameable.getOwner() != null) + { + UUID ownerID = tameable.getOwner().getUniqueId(); + + //if the player interacting is the owner, always allow + if(attacker.getUniqueId().equals(ownerID)) return; + + //otherwise disallow in non-pvp worlds + if(!GriefPrevention.instance.config_pvp_enabledWorlds.contains(subEvent.getEntity().getLocation().getWorld())) + { + OfflinePlayer owner = GriefPrevention.instance.getServer().getOfflinePlayer(ownerID); + String ownerName = owner.getName(); + if(ownerName == null) ownerName = "someone"; + String message = GriefPrevention.instance.dataStore.getMessage(Messages.NoDamageClaimedEntity, ownerName); + if(attacker.hasPermission("griefprevention.ignoreclaims")) + message += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); + GriefPrevention.sendMessage(attacker, TextMode.Err, message); + event.setCancelled(true); + return; + } + } + } + + Claim cachedClaim = null; PlayerData playerData = null; //if not a player or an explosive, allow diff --git a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java index 24aa449..c02a778 100644 --- a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java +++ b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java @@ -42,6 +42,7 @@ import org.bukkit.block.Block; import org.bukkit.command.*; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -60,9 +61,6 @@ public class GriefPrevention extends JavaPlugin //this handles data storage, like player and region data public DataStore dataStore; - //this remembers which item stacks dropped in the world belong to which players - ConcurrentHashMap itemStackOwnerMap = new ConcurrentHashMap(); - //configuration variables, loaded/saved from a config.yml //claim mode for each world @@ -2292,8 +2290,8 @@ public class GriefPrevention extends JavaPlugin PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); playerData.pvpImmune = true; - //inform the player - GriefPrevention.sendMessage(player, TextMode.Success, Messages.PvPImmunityStart); + //inform the player after he finishes respawning + GriefPrevention.sendMessage(player, TextMode.Success, Messages.PvPImmunityStart, 5L); } //checks whether players siege in a world @@ -2558,4 +2556,7 @@ public class GriefPrevention extends JavaPlugin return overrideValue; } } + + //this tracks item stacks expected to drop which will need protection + ArrayList pendingItemWatchList = new ArrayList(); } \ No newline at end of file diff --git a/src/me/ryanhamshire/GriefPrevention/ItemStackOwnerInfo.java b/src/me/ryanhamshire/GriefPrevention/ItemStackOwnerInfo.java deleted file mode 100644 index 13d33e1..0000000 --- a/src/me/ryanhamshire/GriefPrevention/ItemStackOwnerInfo.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.ryanhamshire.GriefPrevention; - -import java.util.UUID; -import org.bukkit.Location; - -//tracks an item stack's owner. ownership is limited to a locality due to problems with hash overlaps overprotecting item stacks -class ItemStackOwnerInfo -{ - public UUID ownerID; - public Location locality; - - public ItemStackOwnerInfo(UUID ownerID, Location location) - { - this.ownerID = ownerID; - this.locality = location; - } -} diff --git a/src/me/ryanhamshire/GriefPrevention/PendingItemProtection.java b/src/me/ryanhamshire/GriefPrevention/PendingItemProtection.java new file mode 100644 index 0000000..e19ecac --- /dev/null +++ b/src/me/ryanhamshire/GriefPrevention/PendingItemProtection.java @@ -0,0 +1,21 @@ +package me.ryanhamshire.GriefPrevention; + +import java.util.UUID; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; + +class PendingItemProtection +{ + public Location location; + public UUID owner; + long expirationTimestamp; + ItemStack itemStack; + + public PendingItemProtection(Location location, UUID owner, long expirationTimestamp, ItemStack itemStack) + { + this.location = location; + this.owner = owner; + this.expirationTimestamp = expirationTimestamp; + this.itemStack = itemStack; + } +} diff --git a/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java b/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java index aea963d..9a0f06a 100644 --- a/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java @@ -44,7 +44,9 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Hanging; import org.bukkit.entity.Horse; +import org.bukkit.entity.Item; import org.bukkit.entity.Player; +import org.bukkit.entity.Tameable; import org.bukkit.entity.Vehicle; import org.bukkit.entity.minecart.PoweredMinecart; import org.bukkit.entity.minecart.StorageMinecart; @@ -58,6 +60,7 @@ import org.bukkit.event.player.PlayerLoginEvent.Result; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.MetadataValue; import org.bukkit.util.BlockIterator; class PlayerEventHandler implements Listener @@ -596,7 +599,6 @@ class PlayerEventHandler implements Listener Player player = event.getPlayer(); PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()); playerData.lastSpawn = Calendar.getInstance().getTimeInMillis(); - GriefPrevention.instance.checkPvpProtectionNeeded(player); //also send him any messaged from grief prevention he would have received while dead if(playerData.messageOnRespawn != null) @@ -604,6 +606,8 @@ class PlayerEventHandler implements Listener GriefPrevention.sendMessage(player, ChatColor.RESET /*color is alrady embedded in message in this case*/, playerData.messageOnRespawn, 40L); playerData.messageOnRespawn = null; } + + GriefPrevention.instance.checkPvpProtectionNeeded(player); } //when a player dies... @@ -806,7 +810,10 @@ class PlayerEventHandler implements Listener public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) { //treat it the same as interacting with an entity in general - this.onPlayerInteractEntity((PlayerInteractEntityEvent)event); + if(event.getRightClicked().getType() == EntityType.ARMOR_STAND) + { + this.onPlayerInteractEntity((PlayerInteractEntityEvent)event); + } } //when a player interacts with an entity... @@ -834,6 +841,9 @@ class PlayerEventHandler implements Listener } PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); + + //always allow interactions when player is in ignore claims mode + if(playerData.ignoreClaims) return; //don't allow container access during pvp combat if((entity instanceof StorageMinecart || entity instanceof PoweredMinecart)) @@ -853,6 +863,30 @@ class PlayerEventHandler implements Listener } } + //if entity is tameable and has an owner, apply special rules + if(entity instanceof Tameable && !GriefPrevention.instance.config_pvp_enabledWorlds.contains(entity.getLocation().getWorld())) + { + Tameable tameable = (Tameable)entity; + if(tameable.isTamed() && tameable.getOwner() != null) + { + UUID ownerID = tameable.getOwner().getUniqueId(); + + //if the player interacting is the owner, always allow + if(player.getUniqueId().equals(ownerID)) return; + + //otherwise disallow + OfflinePlayer owner = GriefPrevention.instance.getServer().getOfflinePlayer(ownerID); + String ownerName = owner.getName(); + if(ownerName == null) ownerName = "someone"; + String message = GriefPrevention.instance.dataStore.getMessage(Messages.NoDamageClaimedEntity, ownerName); + if(player.hasPermission("griefprevention.ignoreclaims")) + message += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); + GriefPrevention.sendMessage(player, TextMode.Err, message); + event.setCancelled(true); + return; + } + } + //if the entity is a vehicle and we're preventing theft in claims if(GriefPrevention.instance.config_claims_preventTheft && entity instanceof Vehicle) { @@ -868,6 +902,7 @@ class PlayerEventHandler implements Listener { GriefPrevention.sendMessage(player, TextMode.Err, noContainersReason); event.setCancelled(true); + return; } } @@ -879,6 +914,7 @@ class PlayerEventHandler implements Listener { player.sendMessage(noAccessReason); event.setCancelled(true); + return; } } } @@ -898,6 +934,7 @@ class PlayerEventHandler implements Listener message += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); GriefPrevention.sendMessage(player, TextMode.Err, message); event.setCancelled(true); + return; } } } @@ -928,25 +965,21 @@ class PlayerEventHandler implements Listener //FEATURE: lock dropped items to player who dropped them //who owns this stack? - ItemStack stack = event.getItem().getItemStack(); - ItemStackOwnerInfo ownerInfo = GriefPrevention.instance.itemStackOwnerMap.get(stack); - if(ownerInfo != null) + Item item = event.getItem(); + List data = item.getMetadata("GP_ITEMOWNER"); + if(data != null && data.size() > 0) { + UUID ownerID = (UUID)data.get(0).value(); + //has that player unlocked his drops? - OfflinePlayer owner = GriefPrevention.instance.getServer().getOfflinePlayer(ownerInfo.ownerID); - String ownerName = GriefPrevention.lookupPlayerName(ownerInfo.ownerID); + OfflinePlayer owner = GriefPrevention.instance.getServer().getOfflinePlayer(ownerID); + String ownerName = GriefPrevention.lookupPlayerName(ownerID); if(owner.isOnline() && !player.equals(owner)) { - PlayerData playerData = this.dataStore.getPlayerData(ownerInfo.ownerID); - Location location = event.getItem().getLocation(); - - //if locked and in locality of protection, don't allow pickup - if(!playerData.dropsAreUnlocked - && location.getX() > ownerInfo.locality.getX() - 10 - && location.getX() < ownerInfo.locality.getX() + 10 - && location.getZ() < ownerInfo.locality.getZ() + 10 - && location.getZ() > ownerInfo.locality.getZ() - 10 - && location.getY() < ownerInfo.locality.getY() + 5) + PlayerData playerData = this.dataStore.getPlayerData(ownerID); + + //if locked, don't allow pickup + if(!playerData.dropsAreUnlocked) { event.setCancelled(true); @@ -959,12 +992,6 @@ class PlayerEventHandler implements Listener } } } - - //if allowed to pick up, remove from ownership map - if(!event.isCancelled()) - { - GriefPrevention.instance.itemStackOwnerMap.remove(stack); - } } //the rest of this code is specific to pvp worlds