Added logging for dropping items into a hopper

This commit is contained in:
Intelli committed 2026-04-24 09:50:00 -06:00
1 parent 829bcc530d
commit cdc8082c3e
3 files changed
+256 -90

No files matched your search

@@ -53,6 +53,9 @@ public class ContainerLogger extends Queue {
else if (type == Material.JUKEBOX || type == Material.ARMOR_STAND) { else if (type == Material.JUKEBOX || type == Material.ARMOR_STAND) {
contents = (ItemStack[]) ((Object[]) container)[1]; contents = (ItemStack[]) ((Object[]) container)[1];
} }
else if (container instanceof ItemStack[]) {
contents = (ItemStack[]) container;
}
else { else {
Inventory inventory = (Inventory) container; Inventory inventory = (Inventory) container;
if (inventory != null) { if (inventory != null) {
@@ -13,6 +13,8 @@ import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.DoubleChest; import org.bukkit.block.DoubleChest;
import org.bukkit.block.Hopper;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
@@ -22,6 +24,7 @@ import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryPickupItemEvent;
import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.BlockInventoryHolder; import org.bukkit.inventory.BlockInventoryHolder;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
@@ -139,96 +142,7 @@ public final class InventoryChangeListener extends Queue implements Listener {
inventoryData = inventory.getContents(); inventoryData = inventory.getContents();
} }
int x = playerLocation.getBlockX(); return queueContainerTransaction(user, playerLocation, type, inventory, inventoryData, null);
int y = playerLocation.getBlockY();
int z = playerLocation.getBlockZ();
String transactingChestId = playerLocation.getWorld().getUID().toString() + "." + x + "." + y + "." + z;
String loggingChestId = user.toLowerCase(Locale.ROOT) + "." + x + "." + y + "." + z;
for (String loggingChestIdViewer : ConfigHandler.oldContainer.keySet()) {
if (loggingChestIdViewer.equals(loggingChestId) || !loggingChestIdViewer.endsWith("." + x + "." + y + "." + z)) {
continue;
}
if (ConfigHandler.oldContainer.get(loggingChestIdViewer) != null) { // player has pending consumer item
int sizeOld = ConfigHandler.oldContainer.get(loggingChestIdViewer).size();
ConfigHandler.forceContainer.computeIfAbsent(loggingChestIdViewer, k -> new ArrayList<>());
List<ItemStack[]> list = ConfigHandler.forceContainer.get(loggingChestIdViewer);
if (list != null && list.size() < sizeOld) {
ItemStack[] containerState = ItemUtils.getContainerState(inventoryData);
// If items have been removed by a hopper, merge into containerState
List<Object> transactingChest = ConfigHandler.transactingChest.get(transactingChestId);
if (transactingChest != null) {
List<Object> transactingChestList = Collections.synchronizedList(new ArrayList<>(transactingChest));
if (!transactingChestList.isEmpty()) {
ItemStack[] newState = new ItemStack[containerState.length + transactingChestList.size()];
int count = 0;
for (int j = 0; j < containerState.length; j++) {
newState[j] = containerState[j];
count++;
}
for (Object item : transactingChestList) {
ItemStack addItem = null;
ItemStack removeItem = null;
if (item instanceof ItemStack) {
addItem = (ItemStack) item;
}
else {
addItem = ((ItemStack[]) item)[0];
removeItem = ((ItemStack[]) item)[1];
}
// item was removed by hopper, add back to state
if (addItem != null) {
newState[count] = addItem;
count++;
}
// item was added by hopper, remove from state
if (removeItem != null) {
for (ItemStack check : newState) {
if (check != null && check.isSimilar(removeItem)) {
check.setAmount(check.getAmount() - 1);
break;
}
}
}
}
containerState = newState;
}
}
modifyForceContainer(loggingChestIdViewer, containerState);
}
}
}
int chestId = getChestId(loggingChestId);
if (chestId > 0) {
List<ItemStack[]> forceList = ConfigHandler.forceContainer.get(loggingChestId);
if (forceList != null) {
int forceSize = forceList.size();
List<ItemStack[]> list = ConfigHandler.oldContainer.get(loggingChestId);
if (list != null && list.size() <= forceSize) {
list.add(ItemUtils.getContainerState(inventoryData));
ConfigHandler.oldContainer.put(loggingChestId, list);
}
}
}
else {
List<ItemStack[]> list = new ArrayList<>();
list.add(ItemUtils.getContainerState(inventoryData));
ConfigHandler.oldContainer.put(loggingChestId, list);
}
ConfigHandler.transactingChest.computeIfAbsent(transactingChestId, k -> Collections.synchronizedList(new ArrayList<>()));
Queue.queueContainerTransaction(user, playerLocation, type, inventory, chestId);
return true;
} }
} }
} }
@@ -236,6 +150,103 @@ public final class InventoryChangeListener extends Queue implements Listener {
return false; return false;
} }
private static boolean queueContainerTransaction(String user, Location playerLocation, Material type, Object inventory, ItemStack[] inventoryData, ItemStack[] forceInventoryData) {
int x = playerLocation.getBlockX();
int y = playerLocation.getBlockY();
int z = playerLocation.getBlockZ();
String transactingChestId = playerLocation.getWorld().getUID().toString() + "." + x + "." + y + "." + z;
String loggingChestId = user.toLowerCase(Locale.ROOT) + "." + x + "." + y + "." + z;
for (String loggingChestIdViewer : ConfigHandler.oldContainer.keySet()) {
if (loggingChestIdViewer.equals(loggingChestId) || !loggingChestIdViewer.endsWith("." + x + "." + y + "." + z)) {
continue;
}
if (ConfigHandler.oldContainer.get(loggingChestIdViewer) != null) { // player has pending consumer item
int sizeOld = ConfigHandler.oldContainer.get(loggingChestIdViewer).size();
ConfigHandler.forceContainer.computeIfAbsent(loggingChestIdViewer, k -> new ArrayList<>());
List<ItemStack[]> list = ConfigHandler.forceContainer.get(loggingChestIdViewer);
if (list != null && list.size() < sizeOld) {
ItemStack[] containerState = ItemUtils.getContainerState(inventoryData);
// If items have been removed by a hopper, merge into containerState
List<Object> transactingChest = ConfigHandler.transactingChest.get(transactingChestId);
if (transactingChest != null) {
List<Object> transactingChestList = Collections.synchronizedList(new ArrayList<>(transactingChest));
if (!transactingChestList.isEmpty()) {
ItemStack[] newState = new ItemStack[containerState.length + transactingChestList.size()];
int count = 0;
for (int j = 0; j < containerState.length; j++) {
newState[j] = containerState[j];
count++;
}
for (Object item : transactingChestList) {
ItemStack addItem = null;
ItemStack removeItem = null;
if (item instanceof ItemStack) {
addItem = (ItemStack) item;
}
else {
addItem = ((ItemStack[]) item)[0];
removeItem = ((ItemStack[]) item)[1];
}
// item was removed by hopper, add back to state
if (addItem != null) {
newState[count] = addItem;
count++;
}
// item was added by hopper, remove from state
if (removeItem != null) {
for (ItemStack check : newState) {
if (check != null && check.isSimilar(removeItem)) {
check.setAmount(check.getAmount() - 1);
break;
}
}
}
}
containerState = newState;
}
}
modifyForceContainer(loggingChestIdViewer, containerState);
}
}
}
int chestId = getChestId(loggingChestId);
if (chestId > 0) {
List<ItemStack[]> forceList = ConfigHandler.forceContainer.get(loggingChestId);
if (forceList != null) {
int forceSize = forceList.size();
List<ItemStack[]> list = ConfigHandler.oldContainer.get(loggingChestId);
if (list != null && list.size() <= forceSize) {
list.add(ItemUtils.getContainerState(inventoryData));
ConfigHandler.oldContainer.put(loggingChestId, list);
}
}
}
else {
List<ItemStack[]> list = new ArrayList<>();
list.add(ItemUtils.getContainerState(inventoryData));
ConfigHandler.oldContainer.put(loggingChestId, list);
}
if (forceInventoryData != null) {
ConfigHandler.forceContainer.computeIfAbsent(loggingChestId, k -> new ArrayList<>()).add(ItemUtils.getContainerState(forceInventoryData));
}
ConfigHandler.transactingChest.computeIfAbsent(transactingChestId, k -> Collections.synchronizedList(new ArrayList<>()));
Queue.queueContainerTransaction(user, playerLocation, type, inventory, chestId);
return true;
}
static void onInventoryInteractAsync(Player player, Inventory inventory, boolean enderChest) { static void onInventoryInteractAsync(Player player, Inventory inventory, boolean enderChest) {
if (inventory == null) { if (inventory == null) {
return; return;
@@ -542,6 +553,105 @@ public final class InventoryChangeListener extends Queue implements Listener {
HopperPullListener.processHopperPull(location, "#hopper", sourceHolder, destinationHolder, event.getItem()); HopperPullListener.processHopperPull(location, "#hopper", sourceHolder, destinationHolder, event.getItem());
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
protected void onInventoryPickupItem(InventoryPickupItemEvent event) {
Inventory inventory = event.getInventory();
if (inventory == null) {
return;
}
InventoryHolder holder = PaperAdapter.ADAPTER.getHolder(inventory, false);
if (!(holder instanceof Hopper)) {
return;
}
Location location = inventory.getLocation();
if (location == null) {
location = ((Hopper) holder).getLocation();
}
if (location == null || !Config.getConfig(location.getWorld()).ITEM_TRANSACTIONS) {
return;
}
Item item = event.getItem();
if (item == null) {
return;
}
ItemStack[] oldContents = ItemUtils.getContainerState(inventory.getContents());
ItemStack[] newContents = addPickedItem(oldContents, item.getItemStack(), inventory.getMaxStackSize());
if (newContents == null) {
return;
}
String user = PlayerDropItemListener.getDroppedItemUser(item.getUniqueId());
if (user == null) {
user = "#hopper";
}
queueContainerTransaction(user, location, Material.HOPPER, newContents, oldContents, newContents);
}
private static ItemStack[] addPickedItem(ItemStack[] contents, ItemStack itemStack, int inventoryMaxStackSize) {
if (contents == null || itemStack == null || itemStack.getAmount() <= 0 || itemStack.getType() == Material.AIR) {
return null;
}
ItemStack[] result = ItemUtils.getContainerState(contents);
ItemStack pickedItem = itemStack.clone();
int remaining = pickedItem.getAmount();
for (ItemStack item : result) {
if (remaining <= 0) {
break;
}
if (item == null || item.getType() == Material.AIR || !item.isSimilar(pickedItem)) {
continue;
}
int maxStackSize = getMaxStackSize(item, inventoryMaxStackSize);
int accepted = Math.min(remaining, maxStackSize - item.getAmount());
if (accepted > 0) {
item.setAmount(item.getAmount() + accepted);
remaining -= accepted;
}
}
for (int i = 0; i < result.length && remaining > 0; i++) {
ItemStack item = result[i];
if (item != null && item.getType() != Material.AIR) {
continue;
}
int accepted = Math.min(remaining, getMaxStackSize(pickedItem, inventoryMaxStackSize));
ItemStack addedItem = pickedItem.clone();
addedItem.setAmount(accepted);
result[i] = addedItem;
remaining -= accepted;
}
if (remaining == pickedItem.getAmount()) {
return null;
}
return result;
}
private static int getMaxStackSize(ItemStack itemStack, int inventoryMaxStackSize) {
int maxStackSize = itemStack.getMaxStackSize();
if (inventoryMaxStackSize > 0 && (inventoryMaxStackSize < maxStackSize || maxStackSize == -1)) {
maxStackSize = inventoryMaxStackSize;
}
if (maxStackSize == -1) {
return 1;
}
return maxStackSize;
}
private boolean isAdvancedChest(Inventory inventory) { private boolean isAdvancedChest(Inventory inventory) {
return CoreProtect.getInstance().isAdvancedChestsEnabled() && AdvancedChestsAPI.getInventoryManager().getAdvancedChest(inventory) != null; return CoreProtect.getInstance().isAdvancedChestsEnabled() && AdvancedChestsAPI.getInventoryManager().getAdvancedChest(inventory) != null;
} }
@@ -3,6 +3,10 @@ package net.coreprotect.listener.player;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Item; import org.bukkit.entity.Item;
@@ -18,6 +22,19 @@ import net.coreprotect.consumer.Queue;
public final class PlayerDropItemListener extends Queue implements Listener { public final class PlayerDropItemListener extends Queue implements Listener {
private static final long ITEM_DROP_ATTRIBUTION_MS = TimeUnit.MINUTES.toMillis(5);
private static final Map<UUID, DropAttribution> itemDropAttributions = new ConcurrentHashMap<>();
private static class DropAttribution {
private final String user;
private final long expiresAt;
private DropAttribution(String user, long expiresAt) {
this.user = user;
this.expiresAt = expiresAt;
}
}
public static void playerDropItem(Location location, String user, ItemStack itemStack) { public static void playerDropItem(Location location, String user, ItemStack itemStack) {
if (!Config.getConfig(location.getWorld()).ITEM_DROPS || itemStack == null) { if (!Config.getConfig(location.getWorld()).ITEM_DROPS || itemStack == null) {
return; return;
@@ -34,10 +51,46 @@ public final class PlayerDropItemListener extends Queue implements Listener {
Queue.queueItemTransaction(user, location.clone(), time, 0, itemId); Queue.queueItemTransaction(user, location.clone(), time, 0, itemId);
} }
public static String getDroppedItemUser(UUID itemId) {
if (itemId == null) {
return null;
}
long timestamp = System.currentTimeMillis();
clearExpiredAttributions(timestamp);
DropAttribution attribution = itemDropAttributions.get(itemId);
if (attribution == null) {
return null;
}
if (attribution.expiresAt < timestamp) {
itemDropAttributions.remove(itemId);
return null;
}
return attribution.user;
}
private static void trackDroppedItem(Item item, String user) {
if (item == null || user == null || user.isEmpty()) {
return;
}
long timestamp = System.currentTimeMillis();
clearExpiredAttributions(timestamp);
itemDropAttributions.put(item.getUniqueId(), new DropAttribution(user, timestamp + ITEM_DROP_ATTRIBUTION_MS));
}
private static void clearExpiredAttributions(long timestamp) {
itemDropAttributions.entrySet().removeIf(entry -> entry.getValue().expiresAt < timestamp);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
protected void onPlayerDropItem(PlayerDropItemEvent event) { protected void onPlayerDropItem(PlayerDropItemEvent event) {
Item item = event.getItemDrop(); Item item = event.getItemDrop();
ItemStack itemStack = item.getItemStack(); ItemStack itemStack = item.getItemStack();
trackDroppedItem(item, event.getPlayer().getName());
playerDropItem(item.getLocation(), event.getPlayer().getName(), itemStack); playerDropItem(item.getLocation(), event.getPlayer().getName(), itemStack);
} }