diff --git a/src/main/java/de/diddiz/LogBlock/EntityChange.java b/src/main/java/de/diddiz/LogBlock/EntityChange.java index 2230789..90c389b 100644 --- a/src/main/java/de/diddiz/LogBlock/EntityChange.java +++ b/src/main/java/de/diddiz/LogBlock/EntityChange.java @@ -5,11 +5,14 @@ import java.sql.SQLException; import java.util.UUID; import org.bukkit.Location; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; import de.diddiz.LogBlock.config.Config; +import de.diddiz.util.Utils; public class EntityChange implements LookupCacheElement { public static enum EntityChangeType { @@ -75,9 +78,21 @@ public class EntityChange implements LookupCacheElement { } else if (changeType == EntityChangeType.KILL) { msg.append(living ? "killed " : "destroyed "); } else if (changeType == EntityChangeType.ADDEQUIP) { - msg.append("added an item to "); + YamlConfiguration conf = Utils.deserializeYamlConfiguration(data); + ItemStack stack = conf == null ? null : conf.getItemStack("item"); + if (stack == null) { + msg.append("added an item to "); + } else { + msg.append("added " + stack.getType() + " to "); + } } else if (changeType == EntityChangeType.REMOVEEQUIP) { - msg.append("removed an item from "); + YamlConfiguration conf = Utils.deserializeYamlConfiguration(data); + ItemStack stack = conf == null ? null : conf.getItemStack("item"); + if (stack == null) { + msg.append("removed an item from "); + } else { + msg.append("removed " + stack.getType() + " from "); + } } else if (changeType == EntityChangeType.MODIFY) { msg.append("modified "); } diff --git a/src/main/java/de/diddiz/LogBlock/WorldEditor.java b/src/main/java/de/diddiz/LogBlock/WorldEditor.java index d03c58f..c4f621b 100644 --- a/src/main/java/de/diddiz/LogBlock/WorldEditor.java +++ b/src/main/java/de/diddiz/LogBlock/WorldEditor.java @@ -19,7 +19,10 @@ import org.bukkit.block.data.type.PistonHead; import org.bukkit.block.data.type.TechnicalPiston.Type; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; +import org.bukkit.entity.ItemFrame; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import de.diddiz.LogBlock.blockstate.BlockStateCodecs; @@ -201,7 +204,7 @@ public class WorldEditor implements Runnable { float yaw = (float) deserialized.getDouble("yaw"); float pitch = (float) deserialized.getDouble("pitch"); Location location = new Location(world, x, y, z, yaw, pitch); - Entity existing = Utils.loadChunksForEntity(location.getChunk(), uuid); + Entity existing = BukkitUtils.loadEntityAround(location.getChunk(), uuid); if (existing != null) { return PerformResult.NO_ACTION; } @@ -228,12 +231,60 @@ public class WorldEditor implements Runnable { float yaw = (float) deserialized.getDouble("yaw"); float pitch = (float) deserialized.getDouble("pitch"); Location location = new Location(world, x, y, z, yaw, pitch); - Entity existing = Utils.loadChunksForEntity(location.getChunk(), uuid); + Entity existing = BukkitUtils.loadEntityAround(location.getChunk(), uuid); if (existing != null) { existing.remove(); return PerformResult.SUCCESS; } return PerformResult.NO_ACTION; // the entity is not there, so we cannot do anything + } else if (changeType == (rollback ? EntityChangeType.REMOVEEQUIP : EntityChangeType.ADDEQUIP)) { + // set equip + UUID uuid = getReplacedUUID(entityId, entityUUID); + Entity existing = BukkitUtils.loadEntityAround(loc.getChunk(), uuid); + if (existing != null) { + YamlConfiguration deserialized = Utils.deserializeYamlConfiguration(data); + ItemStack item = deserialized.getItemStack("item"); + if (item != null && existing instanceof ItemFrame) { + ItemStack old = ((ItemFrame) existing).getItem(); + if (old == null || old.getType() == Material.AIR) { + ((ItemFrame) existing).setItem(item); + return PerformResult.SUCCESS; + } + } else if (item != null && existing instanceof ArmorStand) { + EquipmentSlot slot = EquipmentSlot.valueOf(deserialized.getString("slot")); + ArmorStand stand = (ArmorStand) existing; + ItemStack old = BukkitUtils.getItemInSlot(stand, slot); + if (old == null || old.getType() == Material.AIR) { + BukkitUtils.setItemInSlot(stand, slot, item); + return PerformResult.SUCCESS; + } + } + } + return PerformResult.NO_ACTION; // the entity is not there, or equip does not match + } else if (changeType == (rollback ? EntityChangeType.ADDEQUIP : EntityChangeType.REMOVEEQUIP)) { + // remove equip + UUID uuid = getReplacedUUID(entityId, entityUUID); + Entity existing = BukkitUtils.loadEntityAround(loc.getChunk(), uuid); + if (existing != null) { + YamlConfiguration deserialized = Utils.deserializeYamlConfiguration(data); + ItemStack item = deserialized.getItemStack("item"); + if (item != null && existing instanceof ItemFrame) { + ItemStack old = ((ItemFrame) existing).getItem(); + if (old != null && old.isSimilar(item)) { + ((ItemFrame) existing).setItem(null); + return PerformResult.SUCCESS; + } + } else if (item != null && existing instanceof ArmorStand) { + EquipmentSlot slot = EquipmentSlot.valueOf(deserialized.getString("slot")); + ArmorStand stand = (ArmorStand) existing; + ItemStack old = BukkitUtils.getItemInSlot(stand, slot); + if (old != null && old.isSimilar(item)) { + BukkitUtils.setItemInSlot(stand, slot, null); + return PerformResult.SUCCESS; + } + } + } + return PerformResult.NO_ACTION; // the entity is not there, or equip does not match } return PerformResult.NO_ACTION; } diff --git a/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java b/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java index 50ba6aa..ca6d9b8 100644 --- a/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java +++ b/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java @@ -3,7 +3,7 @@ package de.diddiz.LogBlock.config; import de.diddiz.LogBlock.EntityLogging; import de.diddiz.LogBlock.LogBlock; import de.diddiz.LogBlock.Logging; -import de.diddiz.util.Utils; +import de.diddiz.util.BukkitUtils; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Ambient; @@ -90,7 +90,7 @@ public class WorldConfig extends LoggingEnabledMapping { boolean monsters = false; boolean living = false; for (String type : types) { - EntityType et = Utils.matchEntityType(type); + EntityType et = BukkitUtils.matchEntityType(type); if (et != null) { logged.add(et); } else { diff --git a/src/main/java/de/diddiz/LogBlock/listeners/AdvancedEntityLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/AdvancedEntityLogging.java index 35e575a..52997fd 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/AdvancedEntityLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/AdvancedEntityLogging.java @@ -8,6 +8,7 @@ import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; import org.bukkit.entity.Hanging; import org.bukkit.entity.IronGolem; +import org.bukkit.entity.ItemFrame; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Snowman; @@ -24,6 +25,7 @@ import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.hanging.HangingBreakEvent; import org.bukkit.event.hanging.HangingPlaceEvent; +import org.bukkit.event.player.PlayerArmorStandManipulateEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; @@ -98,11 +100,26 @@ public class AdvancedEntityLogging extends LoggingListener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { ItemStack inHand = event.getHand() == EquipmentSlot.HAND ? event.getPlayer().getInventory().getItemInMainHand() : event.getPlayer().getInventory().getItemInOffHand(); - if (inHand != null) { + if (inHand != null && inHand.getType() != Material.AIR) { Material mat = inHand.getType(); if (mat.name().endsWith("_SPAWN_EGG")) { setLastSpawner(event.getPlayer(), null, true); } + + Entity entity = event.getRightClicked(); + if (entity instanceof ItemFrame) { + ItemStack oldItem = ((ItemFrame) entity).getItem(); + if (oldItem == null || oldItem.getType() == Material.AIR) { + if (Config.isLogging(entity.getWorld(), EntityLogging.MODIFY, entity)) { + Actor actor = Actor.actorFromEntity(event.getPlayer()); + YamlConfiguration data = new YamlConfiguration(); + inHand = inHand.clone(); + inHand.setAmount(1); + data.set("item", inHand); + consumer.queueEntityModification(actor, entity.getUniqueId(), entity.getType(), entity.getLocation(), EntityChange.EntityChangeType.ADDEQUIP, data); + } + } + } } } @@ -169,6 +186,54 @@ public class AdvancedEntityLogging extends LoggingListener { } } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onEntityDamage(EntityDamageEvent event) { + Entity entity = event.getEntity(); + if (entity instanceof ItemFrame) { + ItemStack oldItem = ((ItemFrame) entity).getItem(); + if (oldItem != null && oldItem.getType() != Material.AIR) { + if (Config.isLogging(entity.getWorld(), EntityLogging.MODIFY, entity)) { + Actor actor; + if (event instanceof EntityDamageByEntityEvent) { + actor = Actor.actorFromEntity(((EntityDamageByEntityEvent) event).getDamager()); + } else { + actor = new Actor(event.getCause().toString()); + } + YamlConfiguration data = new YamlConfiguration(); + data.set("item", oldItem); + consumer.queueEntityModification(actor, entity.getUniqueId(), entity.getType(), entity.getLocation(), EntityChange.EntityChangeType.REMOVEEQUIP, data); + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerArmorStandManipulate(PlayerArmorStandManipulateEvent event) { + ArmorStand entity = event.getRightClicked(); + ItemStack oldItem = event.getArmorStandItem(); + ItemStack newItem = event.getPlayerItem(); + boolean oldEmpty = oldItem == null || oldItem.getType() == Material.AIR; + boolean newEmpty = newItem == null || newItem.getType() == Material.AIR; + if ((!oldEmpty || !newEmpty) && Config.isLogging(entity.getWorld(), EntityLogging.MODIFY, entity)) { + Actor actor = Actor.actorFromEntity(event.getPlayer()); + if (!oldEmpty && !newEmpty && newItem.getAmount() > 1) { + return; + } + if (!oldEmpty) { + YamlConfiguration data = new YamlConfiguration(); + data.set("item", oldItem); + data.set("slot", event.getSlot().name()); + consumer.queueEntityModification(actor, entity.getUniqueId(), entity.getType(), entity.getLocation(), EntityChange.EntityChangeType.REMOVEEQUIP, data); + } + if (!newEmpty) { + YamlConfiguration data = new YamlConfiguration(); + data.set("item", newItem); + data.set("slot", event.getSlot().name()); + consumer.queueEntityModification(actor, entity.getUniqueId(), entity.getType(), entity.getLocation(), EntityChange.EntityChangeType.ADDEQUIP, data); + } + } + } + protected void queueEntitySpawnOrKill(Entity entity, Actor actor, EntityChange.EntityChangeType changeType) { Location location = entity.getLocation(); YamlConfiguration data = new YamlConfiguration(); diff --git a/src/main/java/de/diddiz/util/BukkitUtils.java b/src/main/java/de/diddiz/util/BukkitUtils.java index 369d136..5cf05aa 100644 --- a/src/main/java/de/diddiz/util/BukkitUtils.java +++ b/src/main/java/de/diddiz/util/BukkitUtils.java @@ -12,10 +12,12 @@ import org.bukkit.block.data.type.Slab; import org.bukkit.block.data.type.Slab.Type; import org.bukkit.block.data.type.Stairs; import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.entity.TNTPrimed; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -746,4 +748,85 @@ public class BukkitUtils { } return null; } + + public static Entity loadEntityAround(Chunk chunk, UUID uuid) { + Entity e = Bukkit.getEntity(uuid); + if (e != null) { + return e; + } + if (!chunk.isLoaded()) { + chunk.load(); + e = Bukkit.getEntity(uuid); + if (e != null) { + return e; + } + } + int chunkx = chunk.getX(); + int chunkz = chunk.getZ(); + for (int i = 0; i < 8; i++) { + int x = i < 3 ? chunkx - 1 : (i < 5 ? chunkx : chunkx + 1); + int z = i == 0 || i == 3 || i == 5 ? chunkz - 1 : (i == 1 || i == 6 ? chunkz : chunkz + 1); + if (!chunk.getWorld().isChunkLoaded(x, z)) { + chunk.getWorld().loadChunk(x, z); + e = Bukkit.getEntity(uuid); + if (e != null) { + return e; + } + } + } + return null; + } + + private static final HashMap types = new HashMap<>(); + static { + for (EntityType t : EntityType.values()) { + types.put(t.name().toLowerCase(), t); + @SuppressWarnings("deprecation") + String typeName = t.getName(); + if (typeName != null) { + types.put(typeName.toLowerCase(), t); + } + Class ec = t.getEntityClass(); + if (ec != null) { + types.put(ec.getSimpleName().toLowerCase(), t); + } + } + } + + public static EntityType matchEntityType(String typeName) { + return types.get(typeName.toLowerCase()); + } + + public static ItemStack getItemInSlot(ArmorStand stand, EquipmentSlot slot) { + if (slot == EquipmentSlot.HAND) { + return stand.getEquipment().getItemInMainHand(); + } else if (slot == EquipmentSlot.OFF_HAND) { + return stand.getEquipment().getItemInOffHand(); + } else if (slot == EquipmentSlot.FEET) { + return stand.getEquipment().getBoots(); + } else if (slot == EquipmentSlot.LEGS) { + return stand.getEquipment().getLeggings(); + } else if (slot == EquipmentSlot.CHEST) { + return stand.getEquipment().getChestplate(); + } else if (slot == EquipmentSlot.HEAD) { + return stand.getEquipment().getHelmet(); + } + return null; + } + + public static void setItemInSlot(ArmorStand stand, EquipmentSlot slot, ItemStack stack) { + if (slot == EquipmentSlot.HAND) { + stand.getEquipment().setItemInMainHand(stack); + } else if (slot == EquipmentSlot.OFF_HAND) { + stand.getEquipment().setItemInOffHand(stack); + } else if (slot == EquipmentSlot.FEET) { + stand.getEquipment().setBoots(stack); + } else if (slot == EquipmentSlot.LEGS) { + stand.getEquipment().setLeggings(stack); + } else if (slot == EquipmentSlot.CHEST) { + stand.getEquipment().setChestplate(stack); + } else if (slot == EquipmentSlot.HEAD) { + stand.getEquipment().setHelmet(stack); + } + } } diff --git a/src/main/java/de/diddiz/util/Utils.java b/src/main/java/de/diddiz/util/Utils.java index 60306ff..c4ea0f9 100644 --- a/src/main/java/de/diddiz/util/Utils.java +++ b/src/main/java/de/diddiz/util/Utils.java @@ -10,21 +10,15 @@ import java.io.OutputStreamWriter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.ZipException; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import de.diddiz.LogBlock.LogBlock; @@ -290,52 +284,4 @@ public class Utils { public static String serializeForSQL(YamlConfiguration conf) { return mysqlPrepareBytesForInsertAllowNull(serializeYamlConfiguration(conf)); } - - public static Entity loadChunksForEntity(Chunk chunk, UUID uuid) { - Entity e = Bukkit.getEntity(uuid); - if (e != null) { - return e; - } - if (!chunk.isLoaded()) { - chunk.load(); - e = Bukkit.getEntity(uuid); - if (e != null) { - return e; - } - } - int chunkx = chunk.getX(); - int chunkz = chunk.getZ(); - for (int i = 0; i < 8; i++) { - int x = i < 3 ? chunkx - 1 : (i < 5 ? chunkx : chunkx + 1); - int z = i == 0 || i == 3 || i == 5 ? chunkz - 1 : (i == 1 || i == 6 ? chunkz : chunkz + 1); - if (!chunk.getWorld().isChunkLoaded(x, z)) { - chunk.getWorld().loadChunk(x, z); - e = Bukkit.getEntity(uuid); - if (e != null) { - return e; - } - } - } - return null; - } - - private static final HashMap types = new HashMap<>(); - static { - for (EntityType t : EntityType.values()) { - types.put(t.name().toLowerCase(), t); - @SuppressWarnings("deprecation") - String typeName = t.getName(); - if (typeName != null) { - types.put(typeName.toLowerCase(), t); - } - Class ec = t.getEntityClass(); - if (ec != null) { - types.put(ec.getSimpleName().toLowerCase(), t); - } - } - } - - public static EntityType matchEntityType(String typeName) { - return types.get(typeName.toLowerCase()); - } }