From dad1e16c188f51f4ca193094e9d76cd91da319bb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 16 Mar 2021 11:59:07 -0400 Subject: [PATCH] Fix item dupe in own inventory Reduce desync for other actions in own inventory Closes #182 --- .../openinv/listeners/InventoryListener.java | 91 +++++++++++++++---- 1 file changed, 75 insertions(+), 16 deletions(-) diff --git a/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java b/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java index 33221f2..676635a 100644 --- a/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java +++ b/plugin/src/main/java/com/lishid/openinv/listeners/InventoryListener.java @@ -16,7 +16,8 @@ package com.lishid.openinv.listeners; -import com.lishid.openinv.IOpenInv; +import com.lishid.openinv.OpenInv; +import com.lishid.openinv.internal.ISpecialPlayerInventory; import com.lishid.openinv.util.InventoryAccess; import com.lishid.openinv.util.Permissions; import org.bukkit.GameMode; @@ -25,11 +26,14 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.inventory.InventoryInteractEvent; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; /** * Listener for inventory-related events to prevent modification of inventories where not allowed. @@ -38,14 +42,14 @@ import org.bukkit.inventory.Inventory; */ public class InventoryListener implements Listener { - private final IOpenInv plugin; + private final OpenInv plugin; - public InventoryListener(final IOpenInv plugin) { + public InventoryListener(final OpenInv plugin) { this.plugin = plugin; } @EventHandler - public void onInventoryClose(final InventoryCloseEvent event) { + public void onInventoryClose(@NotNull final InventoryCloseEvent event) { if (!(event.getPlayer() instanceof Player)) { return; } @@ -58,37 +62,92 @@ public class InventoryListener implements Listener { } @EventHandler(priority = EventPriority.LOWEST) - public void onInventoryClick(InventoryClickEvent event) { - onInventoryInteract(event); + public void onInventoryClick(@NotNull final InventoryClickEvent event) { + if (handleInventoryInteract(event)) { + return; + } + + // Only specially handle actions in the player's own inventory. + if (!event.getWhoClicked().equals(event.getView().getTopInventory().getHolder())) { + return; + } + + // Safe cast - has to be a player to be the holder of a special player inventory. + Player player = (Player) event.getWhoClicked(); + + if (event.getAction() != InventoryAction.MOVE_TO_OTHER_INVENTORY) { + // All own-inventory interactions require updates to display properly. + // Update in same tick after event completion. + this.plugin.getServer().getScheduler().runTask(this.plugin, player::updateInventory); + return; + } + + // Extra handling for MOVE_TO_OTHER_INVENTORY - apparently Mojang no longer removes the item from the target + // inventory prior to adding it to existing stacks. + ItemStack currentItem = event.getCurrentItem(); + if (currentItem == null) { + // Other plugin doing some sort of handling (would be NOTHING for null item otherwise), ignore. + return; + } + + ItemStack clone = currentItem.clone(); + event.setCurrentItem(null); + + // Complete add action in same tick after event completion. + this.plugin.getServer().getScheduler().runTask(this.plugin, () -> { + player.getInventory().addItem(clone); + player.updateInventory(); + }); } @EventHandler(priority = EventPriority.LOWEST) - public void onInventoryDrag(InventoryDragEvent event) { - onInventoryInteract(event); + public void onInventoryDrag(@NotNull final InventoryDragEvent event) { + handleInventoryInteract(event); } - private void onInventoryInteract(InventoryInteractEvent event) { + /** + * Handle common InventoryInteractEvent functions. + * + * @param event the InventoryInteractEvent + * @return true unless the top inventory is an opened player inventory + */ + private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent event) { HumanEntity entity = event.getWhoClicked(); + // Un-cancel spectator interactions. if (Permissions.SPECTATE.hasPermission(entity) && entity.getGameMode() == GameMode.SPECTATOR) { event.setCancelled(false); } if (event.isCancelled()) { - return; + return true; } - Inventory inventory = event.getInventory(); + Inventory inventory = event.getView().getTopInventory(); - if (InventoryAccess.isPlayerInventory(inventory)) { - if (!Permissions.EDITINV.hasPermission(entity)) { - event.setCancelled(true); - } - } else if (InventoryAccess.isEnderChest(inventory)) { + // Is the inventory a special ender chest? + if (InventoryAccess.isEnderChest(inventory)) { + // Disallow ender chest interaction for users without edit permission. if (!Permissions.EDITENDER.hasPermission(entity)) { event.setCancelled(true); } + return true; } + + ISpecialPlayerInventory playerInventory = InventoryAccess.getPlayerInventory(inventory); + + // Ignore inventories other than special player inventories. + if (playerInventory == null) { + return true; + } + + // Disallow player inventory interaction for users without edit permission. + if (!Permissions.EDITINV.hasPermission(entity)) { + event.setCancelled(true); + return true; + } + + return false; } }