--- a/net/minecraft/world/inventory/Container.java +++ b/net/minecraft/world/inventory/Container.java @@ -36,6 +36,20 @@ import net.minecraft.world.level.block.entity.TileEntity; import org.slf4j.Logger; +// CraftBukkit start +import com.google.common.base.Preconditions; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.protocol.game.PacketPlayOutSetSlot; +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 { private static final Logger LOGGER = LogUtils.getLogger(); @@ -68,6 +82,31 @@ private ContainerSynchronizer synchronizer; private boolean suppressRemoteUpdates; + // 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; + } + protected boolean opened; + public void startOpen() { + this.opened = true; + } + // CraftBukkit end + protected Container(@Nullable Containers containers, int i) { this.carried = ItemStack.EMPTY; this.remoteSlots = NonNullList.create(); @@ -201,6 +240,16 @@ } + // CraftBukkit start + public void broadcastCarriedItem() { + ItemStack itemstack = this.getCarried().copy(); + this.remoteCarried.force(itemstack); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, itemstack); + } + } + // CraftBukkit end + public void removeSlotListener(ICrafting icrafting) { this.containerListeners.remove(icrafting); } @@ -408,7 +457,7 @@ } } else if (this.quickcraftStatus == 2) { if (!this.quickcraftSlots.isEmpty()) { - if (this.quickcraftSlots.size() == 1) { + if (false && this.quickcraftSlots.size() == 1) { // CraftBukkit - treat everything as a drag since we are unable to easily call InventoryClickEvent instead int l = ((Slot) this.quickcraftSlots.iterator().next()).index; this.resetQuickCraft(); @@ -424,6 +473,7 @@ } int i1 = this.getCarried().getCount(); + Map draggedSlots = new HashMap(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) for (Slot slot1 : this.quickcraftSlots) { ItemStack itemstack2 = this.getCarried(); @@ -434,12 +484,48 @@ int l1 = Math.min(getQuickCraftPlaceCount(this.quickcraftSlots, this.quickcraftType, itemstack1) + j1, k1); i1 -= l1 - j1; - slot1.setByPlayer(itemstack1.copyWithCount(l1)); + // slot1.setByPlayer(itemstack1.copyWithCount(l1)); + draggedSlots.put(slot1.index, itemstack1.copyWithCount(l1)); // CraftBukkit - Put in map instead of setting } } - itemstack1.setCount(i1); - this.setCarried(itemstack1); + // CraftBukkit start - InventoryDragEvent + InventoryView view = getBukkitView(); + org.bukkit.inventory.ItemStack newcursor = CraftItemStack.asCraftMirror(itemstack1); + newcursor.setAmount(i1); + Map eventmap = new HashMap(); + for (Map.Entry 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 = this.getCarried(); + this.setCarried(CraftItemStack.asNMSCopy(newcursor)); + + InventoryDragEvent event = new InventoryDragEvent(view, (newcursor.getType() != org.bukkit.Material.AIR ? newcursor : null), CraftItemStack.asBukkitCopy(oldCursor), this.quickcraftType == 1, eventmap); + entityhuman.level().getCraftServer().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 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 (this.getCarried() != null) { + this.setCarried(CraftItemStack.asNMSCopy(event.getCursor())); + needsUpdate = true; + } + } else { + this.setCarried(oldCursor); + } + + if (needsUpdate && entityhuman instanceof EntityPlayer) { + this.sendAllDataToRemote(); + } + // CraftBukkit end } this.resetQuickCraft(); @@ -454,8 +540,11 @@ if (i == -999) { if (!this.getCarried().isEmpty()) { if (clickaction == ClickAction.PRIMARY) { - entityhuman.drop(this.getCarried(), true); + // CraftBukkit start + ItemStack carried = this.getCarried(); this.setCarried(ItemStack.EMPTY); + entityhuman.drop(carried, true); + // CraftBukkit start } else { entityhuman.drop(this.getCarried().split(1), true); } @@ -521,6 +610,15 @@ } slot3.setChanged(); + // CraftBukkit start - Make sure the client has the right slot contents + if (entityhuman instanceof EntityPlayer && slot3.getMaxStackSize() != IInventory.MAX_STACK) { + ((EntityPlayer) entityhuman).connection.send(new PacketPlayOutSetSlot(this.containerId, this.incrementStateId(), slot3.index, slot3.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).connection.send(new PacketPlayOutSetSlot(this.containerId, this.incrementStateId(), 0, this.getSlot(0).getItem())); + } + } + // CraftBukkit end } } else if (inventoryclicktype == InventoryClickType.SWAP && (j >= 0 && j < 9 || j == 40)) { ItemStack itemstack6 = playerinventory.getItem(j); @@ -589,7 +687,11 @@ } itemstack9 = slot6.safeTake(j3, Integer.MAX_VALUE, entityhuman); - entityhuman.drop(itemstack9, true); + // CraftBukkit start - SPIGOT-8010: break loop + if (entityhuman.drop(itemstack9, true) == null) { + break; + } + // CraftBukkit end entityhuman.handleCreativeModeItemDrop(itemstack9); } } @@ -651,8 +753,9 @@ ItemStack itemstack = this.getCarried(); if (!itemstack.isEmpty()) { + this.setCarried(ItemStack.EMPTY); // CraftBukkit - SPIGOT-4556 - from below dropOrPlaceInInventory(entityhuman, itemstack); - this.setCarried(ItemStack.EMPTY); + // this.setCarried(ItemStack.EMPTY); // CraftBukkit - moved up } } @@ -881,6 +984,11 @@ } public ItemStack getCarried() { + // CraftBukkit start + if (this.carried.isEmpty()) { + this.setCarried(ItemStack.EMPTY); + } + // CraftBukkit end return this.carried; }