Added logging for cushions (fixes #1023)

This commit is contained in:
Intelli committed 2026-09-28 22:27:54 -06:00
1 parent ac94648185
commit 7582ab4d8e
13 files changed
+325 -37

No files matched your search

@@ -32,6 +32,7 @@ import net.coreprotect.listener.entity.EntityPlaceListener;
import net.coreprotect.listener.entity.EntityChunkListener;
import net.coreprotect.listener.entity.LegacyEntityChunkListener;
import net.coreprotect.listener.entity.EntityPickupItemListener;
import net.coreprotect.listener.entity.CushionBreakListener;
import net.coreprotect.listener.entity.EntitySpawnListener;
import net.coreprotect.listener.entity.EntityTransformListener;
import net.coreprotect.listener.entity.HangingBreakByEntityListener;
@@ -69,9 +70,11 @@ import net.coreprotect.listener.world.PortalCreateListener;
import net.coreprotect.listener.world.StructureGrowListener;
import net.coreprotect.paper.listener.BlockPreDispenseListener;
import net.coreprotect.paper.listener.CopperGolemChestListener;
import net.coreprotect.paper.listener.CushionListener;
import net.coreprotect.paper.listener.FlowerPotManipulateListener;
import net.coreprotect.paper.listener.LegacyTNTPrimeListener;
import net.coreprotect.paper.listener.PaperChatListener;
import net.coreprotect.spigot.listener.CushionPlaceListener;
public final class ListenerHandler {
@@ -96,6 +99,19 @@ public final class ListenerHandler {
// Ignore registration failures to remain compatible with older servers.
}
try {
Class.forName("io.papermc.paper.event.entity.EntityBreakEvent");
pluginManager.registerEvents(new CushionListener(), plugin);
}
catch (ClassNotFoundException e) {
try {
Class.forName("org.bukkit.entity.Cushion");
pluginManager.registerEvents(new CushionPlaceListener(), plugin);
}
catch (ClassNotFoundException ignored) {
}
}
// Block Listeners
pluginManager.registerEvents(new BlockBreakListener(), plugin);
pluginManager.registerEvents(new BlockBurnListener(), plugin);
@@ -144,6 +160,7 @@ public final class ListenerHandler {
pluginManager.registerEvents(new EntityPlaceListener(), plugin);
pluginManager.registerEvents(new EntityPickupItemListener(), plugin);
pluginManager.registerEvents(new EntitySpawnListener(), plugin);
pluginManager.registerEvents(new CushionBreakListener(), plugin);
pluginManager.registerEvents(new EntityTransformListener(), plugin);
pluginManager.registerEvents(new HangingPlaceListener(), plugin);
pluginManager.registerEvents(new HangingBreakListener(), plugin);
@@ -0,0 +1,47 @@
package net.coreprotect.listener.entity;
import java.util.Locale;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.hanging.HangingBreakEvent;
import net.coreprotect.config.Config;
import net.coreprotect.consumer.Queue;
import net.coreprotect.listener.player.EntityInteractionListener;
import net.coreprotect.utility.EntitySpawnTracking;
import net.coreprotect.utility.EntityUtils;
public final class CushionBreakListener extends Queue implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onHangingBreak(HangingBreakEvent event) {
if (!event.isCancelled()) {
Entity remover = event instanceof HangingBreakByEntityEvent ? ((HangingBreakByEntityEvent) event).getRemover() : null;
logBreak(event.getEntity(), remover, event.getCause().name());
}
}
public static void logBreak(Entity entity, Entity remover, String cause) {
if (!EntitySpawnTracking.isCushion(entity) || EntitySpawnTracking.isCoreProtectRemoval(entity.getUniqueId())) {
return;
}
EntityInteractionListener.flushPendingInteractions(entity);
if (Config.getConfig(entity.getWorld()).ENTITY_KILLS) {
String user = EntityUtils.getEntityUser(remover);
if (user == null) {
user = "#" + cause.toLowerCase(Locale.ROOT);
}
Queue.queueEntityKill(user, entity.getLocation(), EntitySpawnTracking.serializeKillData(entity), entity.getType());
}
if (EntitySpawnTracking.isTrackedOrPendingIdentity(entity)) {
Queue.queueEntitySpawnRemoved(entity);
EntitySpawnTracking.clearTracking(entity.getUniqueId());
}
}
}
@@ -15,12 +15,14 @@ public final class EntityPlaceListener extends Queue implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onEntityPlace(EntityPlaceEvent event) {
Entity entity = event.getEntity();
logPlacement(event.getEntity(), event.getPlayer());
}
public static void logPlacement(Entity entity, Player player) {
if (!EntitySpawnTracking.isPlacedEntity(entity) || EntitySpawnTracking.isTracked(entity) || !Config.getConfig(entity.getWorld()).ENTITY_SPAWNS) {
return;
}
Player player = event.getPlayer();
String user = player == null ? "#dispenser" : player.getName();
EntitySpawnTracking.track(entity);
Queue.queueEntitySpawnLog(user, entity.getUniqueId(), entity.getType(), entity.getLocation());
@@ -31,6 +31,7 @@ import net.coreprotect.utility.Color;
import net.coreprotect.utility.MaterialUtils;
import net.coreprotect.utility.ErrorReporter;
import net.coreprotect.utility.LookupThrottle;
import net.coreprotect.utility.EntitySpawnTracking;
public final class HangingBreakByEntityListener extends Queue implements Listener {
@@ -38,7 +39,7 @@ public final class HangingBreakByEntityListener extends Queue implements Listene
inspectEntity(block, player, null);
}
static void inspectEntity(final BlockState block, final Player player, final UUID entityUuid) {
public static void inspectEntity(final BlockState block, final Player player, final UUID entityUuid) {
// block check
if (!player.hasPermission("coreprotect.inspect")) {
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.NO_PERMISSION));
@@ -114,7 +115,7 @@ public final class HangingBreakByEntityListener extends Queue implements Listene
if (ConfigHandler.inspecting.get(player.getName()) != null) {
if (ConfigHandler.inspecting.get(player.getName())) {
// block check
inspectItemFrame(blockEvent, player);
inspectEntity(blockEvent, player, EntitySpawnTracking.isCushion(entity) ? entity.getUniqueId() : null);
event.setCancelled(true);
inspecting = true;
}
@@ -18,9 +18,13 @@ public final class TrackedEntityRemoveListener implements Listener {
Entity entity = event.getEntity();
switch (event.getCause()) {
case UNLOAD:
return;
case DEATH:
case HIT:
return;
if (!EntitySpawnTracking.isCushion(entity)) {
return;
}
break;
case PLUGIN:
if (EntitySpawnTracking.isCoreProtectRemoval(entity.getUniqueId())) {
return;
@@ -3,9 +3,6 @@ package net.coreprotect.listener.entity;
import java.util.Locale;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -13,13 +10,13 @@ import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.vehicle.VehicleDestroyEvent;
import org.bukkit.projectiles.ProjectileSource;
import net.coreprotect.config.Config;
import net.coreprotect.consumer.Queue;
import net.coreprotect.listener.player.EntityInteractionListener;
import net.coreprotect.listener.player.InventoryChangeListener;
import net.coreprotect.utility.EntitySpawnTracking;
import net.coreprotect.utility.EntityUtils;
public final class VehicleDestroyListener extends Queue implements Listener {
@@ -53,7 +50,7 @@ public final class VehicleDestroyListener extends Queue implements Listener {
attacker = ((EntityDamageByEntityEvent) damage).getDamager();
}
String user = getEntityUser(attacker);
String user = EntityUtils.getEntityUser(attacker);
if (user != null || damage == null) {
return user;
}
@@ -75,25 +72,4 @@ public final class VehicleDestroyListener extends Queue implements Listener {
return "#" + damage.getCause().name().toLowerCase(Locale.ROOT);
}
}
private static String getEntityUser(Entity attacker) {
if (attacker instanceof Player) {
return attacker.getName();
}
if (attacker instanceof Projectile) {
ProjectileSource shooter = ((Projectile) attacker).getShooter();
if (shooter instanceof Player) {
return ((Player) shooter).getName();
}
if (shooter instanceof Entity) {
attacker = (Entity) shooter;
}
}
if (attacker == null) {
return null;
}
EntityType type = attacker.getType();
return type == null ? null : "#" + type.name().toLowerCase(Locale.ROOT);
}
}
@@ -0,0 +1,59 @@
package net.coreprotect.paper.listener;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPlaceEvent;
import io.papermc.paper.event.entity.EntityBreakByEntityEvent;
import io.papermc.paper.event.entity.EntityBreakEvent;
import net.coreprotect.config.ConfigHandler;
import net.coreprotect.listener.entity.CushionBreakListener;
import net.coreprotect.listener.entity.HangingBreakByEntityListener;
import net.coreprotect.utility.EntitySpawnTracking;
public final class CushionListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onEntityPlace(EntityPlaceEvent event) {
Player player = event.getPlayer();
if (EntitySpawnTracking.isCushion(event.getEntity()) && player != null
&& Boolean.TRUE.equals(ConfigHandler.inspecting.get(player.getName()))) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onEntityInspect(EntityBreakEvent event) {
if (!EntitySpawnTracking.isCushion(event.getEntity()) || !(event instanceof EntityBreakByEntityEvent)) {
return;
}
Entity remover = ((EntityBreakByEntityEvent) event).getRemover();
if (!(remover instanceof Player) || !Boolean.TRUE.equals(ConfigHandler.inspecting.get(remover.getName()))) {
return;
}
Entity entity = event.getEntity();
event.setCancelled(true);
HangingBreakByEntityListener.inspectEntity(entity.getLocation().getBlock().getState(), (Player) remover, entity.getUniqueId());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onEntityBreak(EntityBreakEvent event) {
if (event.isCancelled()) {
return;
}
Entity remover = null;
if (event instanceof EntityBreakByEntityEvent) {
EntityBreakByEntityEvent entityEvent = (EntityBreakByEntityEvent) event;
remover = entityEvent.getDamageSource().getCausingEntity();
if (remover == null) {
remover = entityEvent.getRemover();
}
}
CushionBreakListener.logBreak(event.getEntity(), remover, event.getCause().name());
}
}
@@ -3,7 +3,9 @@ package net.coreprotect.spigot;
import java.util.List;
import java.util.regex.Matcher;
import org.bukkit.DyeColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Villager;
import net.coreprotect.bukkit.BukkitAdapter;
@@ -38,6 +40,15 @@ public class SpigotAdapter implements SpigotInterface {
SpigotAdapter.ADAPTER = new SpigotHandler();
}
@Override
public DyeColor getCushionColor(Entity entity) {
return null;
}
@Override
public void setCushionColor(Entity entity, DyeColor color) {
}
@Override
public void addHoverComponent(Object message, String[] data) {
return;
@@ -5,8 +5,10 @@ import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import org.bukkit.DyeColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Villager;
import net.coreprotect.config.Config;
@@ -31,6 +33,33 @@ public class SpigotHandler extends SpigotAdapter implements SpigotInterface {
Color.DARK_AQUA = SpigotHandler.DARK_AQUA.toString();
}
@Override
public DyeColor getCushionColor(Entity entity) {
try {
Object handle = entity.getClass().getMethod("getHandle").invoke(entity);
Object color = handle.getClass().getMethod("getColor").invoke(handle);
int id = ((Number) color.getClass().getMethod("getId").invoke(color)).intValue();
return DyeColor.getByWoolData((byte) id);
}
catch (ReflectiveOperationException e) {
ErrorReporter.report(e);
return null;
}
}
@Override
public void setCushionColor(Entity entity, DyeColor color) {
try {
Object handle = entity.getClass().getMethod("getHandle").invoke(entity);
Class<?> colorClass = handle.getClass().getMethod("getColor").getReturnType();
Object value = colorClass.getMethod("byId", int.class).invoke(null, (int) color.getWoolData());
handle.getClass().getMethod("setColor", colorClass).invoke(handle, value);
}
catch (ReflectiveOperationException e) {
ErrorReporter.report(e);
}
}
@Override
public void addHoverComponent(Object message, String[] data) {
try {
@@ -2,11 +2,17 @@ package net.coreprotect.spigot;
import java.util.List;
import org.bukkit.DyeColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Villager;
public interface SpigotInterface {
public DyeColor getCushionColor(Entity entity);
public void setCushionColor(Entity entity, DyeColor color);
public void addHoverComponent(Object message, String[] data);
public void setHoverEvent(Object message, String text);
@@ -0,0 +1,85 @@
package net.coreprotect.spigot.listener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.GameEvent;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.world.GenericGameEvent;
import net.coreprotect.CoreProtect;
import net.coreprotect.config.Config;
import net.coreprotect.config.ConfigHandler;
import net.coreprotect.listener.entity.EntityPlaceListener;
import net.coreprotect.thread.Scheduler;
import net.coreprotect.utility.EntitySpawnTracking;
public final class CushionPlaceListener implements Listener {
private final Map<Location, List<EntitySpawnEvent>> pendingSpawns = new HashMap<>();
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getItem() != null
&& event.getItem().getType().name().endsWith("_CUSHION")
&& Boolean.TRUE.equals(ConfigHandler.inspecting.get(event.getPlayer().getName()))) {
event.setUseItemInHand(Event.Result.DENY);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onEntitySpawn(EntitySpawnEvent event) {
Entity entity = event.getEntity();
if (event.isAsynchronous() || event.isCancelled() || !EntitySpawnTracking.isCushion(entity)
|| EntitySpawnTracking.isTracked(entity) || !Config.getConfig(entity.getWorld()).ENTITY_SPAWNS) {
return;
}
Location location = entity.getLocation().getBlock().getLocation();
List<EntitySpawnEvent> spawns = pendingSpawns.computeIfAbsent(location, key -> new ArrayList<>());
spawns.add(event);
Scheduler.runTask(CoreProtect.getInstance(), () -> {
spawns.remove(event);
if (spawns.isEmpty()) {
pendingSpawns.remove(location, spawns);
}
});
}
@EventHandler(priority = EventPriority.MONITOR)
public void onGameEvent(GenericGameEvent event) {
if (event.isAsynchronous() || event.getEvent() != GameEvent.ENTITY_PLACE || !(event.getEntity() instanceof Player)) {
return;
}
List<EntitySpawnEvent> spawns = pendingSpawns.remove(event.getLocation().getBlock().getLocation());
if (spawns == null) {
return;
}
Entity placed = null;
for (EntitySpawnEvent spawn : spawns) {
Entity entity = spawn.getEntity();
if (spawn.isCancelled() || !entity.isValid()) {
continue;
}
if (placed != null) {
return;
}
placed = entity;
}
if (placed != null) {
EntityPlaceListener.logPlacement(placed, (Player) event.getEntity());
}
}
}
@@ -18,6 +18,7 @@ import java.util.concurrent.atomic.AtomicLong;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.DyeColor;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.TreeSpecies;
@@ -33,6 +34,7 @@ import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Colorable;
import org.bukkit.persistence.PersistentDataType;
import net.coreprotect.CoreProtect;
@@ -42,6 +44,7 @@ import net.coreprotect.listener.player.InventoryChangeListener;
import net.coreprotect.model.entity.EntityInteractionOrigin;
import net.coreprotect.model.entity.EntitySpawnData;
import net.coreprotect.paper.PaperAdapter;
import net.coreprotect.spigot.SpigotAdapter;
import net.coreprotect.thread.Scheduler;
public final class EntitySpawnTracking {
@@ -74,7 +77,11 @@ public final class EntitySpawnTracking {
}
public static boolean isPlacedEntity(Entity entity) {
return entity instanceof Boat || entity instanceof Minecart;
return entity instanceof Boat || entity instanceof Minecart || isCushion(entity);
}
public static boolean isCushion(Entity entity) {
return entity != null && entity.getType() != null && entity.getType().name().equals("CUSHION");
}
public static boolean isPlacedEntityType(EntityType type) {
@@ -83,7 +90,7 @@ public final class EntitySpawnTracking {
}
Class<? extends Entity> entityClass = type.getEntityClass();
return entityClass != null && (Boat.class.isAssignableFrom(entityClass) || Minecart.class.isAssignableFrom(entityClass));
return type.name().equals("CUSHION") || (entityClass != null && (Boat.class.isAssignableFrom(entityClass) || Minecart.class.isAssignableFrom(entityClass)));
}
public static Set<Integer> getPlacedEntityTypeIds() {
@@ -229,7 +236,7 @@ public final class EntitySpawnTracking {
}
public static boolean isEligibleInteractionEntity(Entity entity) {
return entity instanceof LivingEntity && !(entity instanceof Player) && !(entity instanceof ArmorStand);
return (entity instanceof LivingEntity && !(entity instanceof Player) && !(entity instanceof ArmorStand)) || isCushion(entity);
}
public static void confirmDatabaseIdentity(UUID uuid, Location location) {
@@ -988,12 +995,16 @@ public final class EntitySpawnTracking {
state.add(entity.getCustomName());
state.add(entity.isCustomNameVisible());
String boatType = null;
String variant = null;
if (entity instanceof Boat) {
TreeSpecies woodType = ((Boat) entity).getWoodType();
boatType = woodType == null ? null : woodType.name();
variant = woodType == null ? null : woodType.name();
}
state.add(boatType);
else if (isCushion(entity)) {
DyeColor color = entity instanceof Colorable ? ((Colorable) entity).getColor() : SpigotAdapter.ADAPTER.getCushionColor(entity);
variant = color == null ? null : color.name();
}
state.add(variant);
List<Object> inventoryData = null;
if (entity instanceof InventoryHolder) {
@@ -1020,6 +1031,10 @@ public final class EntitySpawnTracking {
}
}
state.add(equipmentData);
if (isCushion(entity)) {
Location location = entity.getLocation();
state.add(Arrays.asList(location.getYaw(), location.getPitch()));
}
trimTrailingNulls(state);
return state;
}
@@ -1100,6 +1115,15 @@ public final class EntitySpawnTracking {
catch (Exception ignored) {
}
}
else if (isCushion(entity) && state.size() > 2 && state.get(2) instanceof String) {
DyeColor color = DyeColor.valueOf((String) state.get(2));
if (entity instanceof Colorable) {
((Colorable) entity).setColor(color);
}
else {
SpigotAdapter.ADAPTER.setCushionColor(entity, color);
}
}
if (entity instanceof InventoryHolder && state.size() > 3 && state.get(3) instanceof List<?>) {
Inventory inventory = ((InventoryHolder) entity).getInventory();
List<?> inventoryData = (List<?>) state.get(3);
@@ -1121,6 +1145,12 @@ public final class EntitySpawnTracking {
equipment.setHelmet(getItem(equipmentData, 5));
}
}
if (isCushion(entity) && state.size() > 5 && state.get(5) instanceof List<?>) {
List<?> rotation = (List<?>) state.get(5);
if (rotation.size() == 2 && rotation.get(0) instanceof Number && rotation.get(1) instanceof Number) {
entity.setRotation(((Number) rotation.get(0)).floatValue(), ((Number) rotation.get(1)).floatValue());
}
}
}
private static Object serializeItem(ItemStack item) {
@@ -5,6 +5,9 @@ import java.util.Locale;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.projectiles.ProjectileSource;
import net.coreprotect.bukkit.BukkitAdapter;
import net.coreprotect.config.ConfigHandler;
@@ -19,6 +22,24 @@ public class EntityUtils extends Queue {
throw new IllegalStateException("Utility class");
}
public static String getEntityUser(Entity entity) {
if (entity instanceof Projectile) {
ProjectileSource shooter = ((Projectile) entity).getShooter();
if (shooter instanceof Entity) {
entity = (Entity) shooter;
}
}
if (entity instanceof Player) {
return entity.getName();
}
if (entity == null) {
return null;
}
EntityType type = entity.getType();
return type == null ? null : "#" + type.name().toLowerCase(Locale.ROOT);
}
public static int getEntityId(EntityType type) {
if (type == null) {
return -1;