--- a/net/minecraft/server/Container.java +++ b/net/minecraft/server/Container.java @@ -7,6 +7,18 @@ import java.util.Set; import javax.annotation.Nullable; +// CraftBukkit start +import com.google.common.base.Preconditions; +import java.util.HashMap; +import java.util.Map; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.Event.Result; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.InventoryView; +// CraftBukkit end + public abstract class Container { public NonNullList<ItemStack> items = NonNullList.a(); @@ -21,6 +33,27 @@ private final List<ICrafting> listeners = Lists.newArrayList(); private final Set<EntityHuman> k = Sets.newHashSet(); + // CraftBukkit start + public boolean checkReachable = true; + public abstract InventoryView getBukkitView(); + public void transferTo(Container other, org.bukkit.craftbukkit.entity.CraftHumanEntity player) { + InventoryView source = this.getBukkitView(), destination = other.getBukkitView(); + ((CraftInventory) source.getTopInventory()).getInventory().onClose(player); + ((CraftInventory) source.getBottomInventory()).getInventory().onClose(player); + ((CraftInventory) destination.getTopInventory()).getInventory().onOpen(player); + ((CraftInventory) destination.getBottomInventory()).getInventory().onOpen(player); + } + private IChatBaseComponent title; + public final IChatBaseComponent getTitle() { + Preconditions.checkState(this.title != null, "Title not set"); + return this.title; + } + public final void setTitle(IChatBaseComponent title) { + Preconditions.checkState(this.title == null, "Title already set"); + this.title = title; + } + // CraftBukkit end + protected Container(@Nullable Containers<?> containers, int i) { this.e = containers; this.windowId = i; @@ -202,6 +235,7 @@ k = playerinventory.getCarried().getCount(); Iterator iterator = this.i.iterator(); + Map<Integer, ItemStack> draggedSlots = new HashMap<Integer, ItemStack>(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) while (iterator.hasNext()) { Slot slot1 = (Slot) iterator.next(); ItemStack itemstack3 = playerinventory.getCarried(); @@ -217,12 +251,48 @@ } k -= itemstack4.getCount() - j1; - slot1.set(itemstack4); + // slot1.set(itemstack4); + draggedSlots.put(slot1.rawSlotIndex, itemstack4); // CraftBukkit - Put in map instead of setting } } - itemstack2.setCount(k); - playerinventory.setCarried(itemstack2); + // CraftBukkit start - InventoryDragEvent + InventoryView view = getBukkitView(); + org.bukkit.inventory.ItemStack newcursor = CraftItemStack.asCraftMirror(itemstack2); + newcursor.setAmount(k); + Map<Integer, org.bukkit.inventory.ItemStack> eventmap = new HashMap<Integer, org.bukkit.inventory.ItemStack>(); + for (Map.Entry<Integer, ItemStack> ditem : draggedSlots.entrySet()) { + eventmap.put(ditem.getKey(), CraftItemStack.asBukkitCopy(ditem.getValue())); + } + + // It's essential that we set the cursor to the new value here to prevent item duplication if a plugin closes the inventory. + ItemStack oldCursor = playerinventory.getCarried(); + playerinventory.setCarried(CraftItemStack.asNMSCopy(newcursor)); + + InventoryDragEvent event = new InventoryDragEvent(view, (newcursor.getType() != org.bukkit.Material.AIR ? newcursor : null), CraftItemStack.asBukkitCopy(oldCursor), this.dragType == 1, eventmap); + entityhuman.world.getServer().getPluginManager().callEvent(event); + + // Whether or not a change was made to the inventory that requires an update. + boolean needsUpdate = event.getResult() != Result.DEFAULT; + + if (event.getResult() != Result.DENY) { + for (Map.Entry<Integer, ItemStack> dslot : draggedSlots.entrySet()) { + view.setItem(dslot.getKey(), CraftItemStack.asBukkitCopy(dslot.getValue())); + } + // The only time the carried item will be set to null is if the inventory is closed by the server. + // If the inventory is closed by the server, then the cursor items are dropped. This is why we change the cursor early. + if (playerinventory.getCarried() != null) { + playerinventory.setCarried(CraftItemStack.asNMSCopy(event.getCursor())); + needsUpdate = true; + } + } else { + playerinventory.setCarried(oldCursor); + } + + if (needsUpdate && entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).updateInventory(this); + } + // CraftBukkit end } this.d(); @@ -239,8 +309,11 @@ if (i == -999) { if (!playerinventory.getCarried().isEmpty()) { if (j == 0) { - entityhuman.drop(playerinventory.getCarried(), true); + // CraftBukkit start + ItemStack carried = playerinventory.getCarried(); playerinventory.setCarried(ItemStack.b); + entityhuman.drop(carried, true); + // CraftBukkit start } if (j == 1) { @@ -328,6 +401,15 @@ } slot2.d(); + // CraftBukkit start - Make sure the client has the right slot contents + if (entityhuman instanceof EntityPlayer && slot2.getMaxStackSize() != 64) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, slot2.rawSlotIndex, slot2.getItem())); + // Updating a crafting inventory makes the client reset the result slot, have to send it again + if (this.getBukkitView().getType() == InventoryType.WORKBENCH || this.getBukkitView().getType() == InventoryType.CRAFTING) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, 0, this.getSlot(0).getItem())); + } + } + // CraftBukkit end } } } else if (inventoryclicktype == InventoryClickType.SWAP) { @@ -430,8 +512,11 @@ PlayerInventory playerinventory = entityhuman.inventory; if (!playerinventory.getCarried().isEmpty()) { - entityhuman.drop(playerinventory.getCarried(), false); + // CraftBukkit start - SPIGOT-4556 + ItemStack carried = playerinventory.getCarried(); playerinventory.setCarried(ItemStack.b); + entityhuman.drop(carried, false); + // CraftBukkit end } }