diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftAbstractInventoryView.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftAbstractInventoryView.java new file mode 100644 index 0000000000..58d2dfe817 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftAbstractInventoryView.java @@ -0,0 +1,221 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Preconditions; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class CraftAbstractInventoryView implements InventoryView { + + @Override + public void setItem(final int slot, @Nullable final ItemStack item) { + Inventory inventory = getInventory(slot); + if (inventory != null) { + inventory.setItem(convertSlot(slot), item); + } else if (item != null) { + getPlayer().getWorld().dropItemNaturally(getPlayer().getLocation(), item); + } + } + + @Nullable + @Override + public ItemStack getItem(final int slot) { + Inventory inventory = getInventory(slot); + return (inventory == null) ? null : inventory.getItem(convertSlot(slot)); + } + + @Override + public void setCursor(@Nullable final ItemStack item) { + getPlayer().setItemOnCursor(item); + } + + @Nullable + @Override + public ItemStack getCursor() { + return getPlayer().getItemOnCursor(); + } + + @Nullable + @Override + public Inventory getInventory(final int rawSlot) { + // Slot may be -1 if not properly detected due to client bug + // e.g. dropping an item into part of the enchantment list section of an enchanting table + if (rawSlot == OUTSIDE || rawSlot == -1) { + return null; + } + Preconditions.checkArgument(rawSlot >= 0, "Negative, non outside slot %s", rawSlot); + Preconditions.checkArgument(rawSlot < countSlots(), "Slot %s greater than inventory slot count", rawSlot); + + if (rawSlot < getTopInventory().getSize()) { + return getTopInventory(); + } else { + return getBottomInventory(); + } + } + + @Override + public int convertSlot(final int rawSlot) { + int numInTop = getTopInventory().getSize(); + // Index from the top inventory as having slots from [0,size] + if (rawSlot < numInTop) { + return rawSlot; + } + + // Move down the slot index by the top size + int slot = rawSlot - numInTop; + + // Player crafting slots are indexed differently. The matrix is caught by the first return. + // Creative mode is the same, except that you can't see the crafting slots (but the IDs are still used) + if (getType() == InventoryType.CRAFTING || getType() == InventoryType.CREATIVE) { + /* + * Raw Slots: + * + * 5 1 2 0 + * 6 3 4 + * 7 + * 8 45 + * 9 10 11 12 13 14 15 16 17 + * 18 19 20 21 22 23 24 25 26 + * 27 28 29 30 31 32 33 34 35 + * 36 37 38 39 40 41 42 43 44 + */ + + /* + * Converted Slots: + * + * 39 1 2 0 + * 38 3 4 + * 37 + * 36 40 + * 9 10 11 12 13 14 15 16 17 + * 18 19 20 21 22 23 24 25 26 + * 27 28 29 30 31 32 33 34 35 + * 0 1 2 3 4 5 6 7 8 + */ + + if (slot < 4) { + // Send [5,8] to [39,36] + return 39 - slot; + } else if (slot > 39) { + // Slot lives in the extra slot section + return slot; + } else { + // Reset index so 9 -> 0 + slot -= 4; + } + } + + // 27 = 36 - 9 + if (slot >= 27) { + // Put into hotbar section + slot -= 27; + } else { + // Take out of hotbar section + // 9 = 36 - 27 + slot += 9; + } + + return slot; + } + + @NotNull + @Override + public InventoryType.SlotType getSlotType(final int slot) { + InventoryType.SlotType type = InventoryType.SlotType.CONTAINER; + if (slot >= 0 && slot < this.getTopInventory().getSize()) { + switch (this.getType()) { + case BLAST_FURNACE: + case FURNACE: + case SMOKER: + if (slot == 2) { + type = InventoryType.SlotType.RESULT; + } else if (slot == 1) { + type = InventoryType.SlotType.FUEL; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case BREWING: + if (slot == 3) { + type = InventoryType.SlotType.FUEL; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case ENCHANTING: + type = InventoryType.SlotType.CRAFTING; + break; + case WORKBENCH: + case CRAFTING: + if (slot == 0) { + type = InventoryType.SlotType.RESULT; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case BEACON: + type = InventoryType.SlotType.CRAFTING; + break; + case ANVIL: + case SMITHING: + case CARTOGRAPHY: + case GRINDSTONE: + case MERCHANT: + if (slot == 2) { + type = InventoryType.SlotType.RESULT; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case STONECUTTER: + if (slot == 1) { + type = InventoryType.SlotType.RESULT; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case LOOM: + case SMITHING_NEW: + if (slot == 3) { + type = InventoryType.SlotType.RESULT; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + default: + // Nothing to do, it's a CONTAINER slot + } + } else { + if (slot < 0) { + type = InventoryType.SlotType.OUTSIDE; + } else if (this.getType() == InventoryType.CRAFTING) { // Also includes creative inventory + if (slot < 9) { + type = InventoryType.SlotType.ARMOR; + } else if (slot > 35) { + type = InventoryType.SlotType.QUICKBAR; + } + } else if (slot >= (this.countSlots() - (9 + 4 + 1))) { // Quickbar, Armor, Offhand + type = InventoryType.SlotType.QUICKBAR; + } + } + return type; + } + + @Override + public void close() { + getPlayer().closeInventory(); + } + + @Override + public int countSlots() { + return getTopInventory().getSize() + getBottomInventory().getSize(); + } + + @Override + public boolean setProperty(@NotNull final Property prop, final int value) { + return getPlayer().setWindowProperty(prop, value); + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java index 1d324ea82e..6ad2e399e3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java @@ -48,7 +48,7 @@ public class CraftContainer extends Container { } public CraftContainer(final Inventory inventory, final EntityHuman player, int id) { - this(new InventoryView() { + this(new CraftAbstractInventoryView() { private final String originalTitle = (inventory instanceof CraftInventoryCustom) ? ((CraftInventoryCustom.MinecraftInventory) ((CraftInventory) inventory).getInventory()).getTitle() : inventory.getType().getDefaultTitle(); private String title = originalTitle; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java index 3532ebf598..6d36909643 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java @@ -15,7 +15,7 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; -public class CraftInventoryView extends InventoryView { +public class CraftInventoryView extends CraftAbstractInventoryView { private final Container container; private final CraftHumanEntity player; private final CraftInventory viewing; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java index 5d28381b3f..7701a32407 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java @@ -67,6 +67,10 @@ public class Commodore { "org/spigotmc/event/entity/EntityDismountEvent", "org/bukkit/event/entity/EntityDismountEvent" ); + private static final Set CLASS_TO_INTERFACE = Set.of( + "org/bukkit/inventory/InventoryView" + ); + private static Map createReroutes(Class clazz) { Map reroutes = RerouteBuilder.buildFromClass(clazz); REROUTES.add(reroutes); @@ -288,6 +292,17 @@ public class Commodore { return; } + if (CLASS_TO_INTERFACE.contains(owner)) { + if (opcode == Opcodes.INVOKEVIRTUAL) { + opcode = Opcodes.INVOKEINTERFACE; + } + + if (opcode == Opcodes.H_INVOKEVIRTUAL) { + opcode = Opcodes.H_INVOKEINTERFACE; + } + itf = true; + } + // SPIGOT-4496 if (owner.equals("org/bukkit/map/MapView") && name.equals("getId") && desc.equals("()S")) { // Should be same size on stack so just call other method