Created
October 12, 2023 19:04
-
-
Save WizardlyBump17/ad5e407a7a760a8671ebd5527b3da506 to your computer and use it in GitHub Desktop.
Getting the inventory after the InventoryClickEvent
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(this, ListenerPriority.HIGHEST, List.of(PacketType.fromClass(ServerboundContainerClickPacket.class)), ListenerOptions.SYNC) { | |
@Override | |
public void onPacketReceiving(PacketEvent event) { | |
ServerboundContainerClickPacket packet = (ServerboundContainerClickPacket) event.getPacket().getHandle(); | |
ServerPlayer serverPlayer = ((CraftPlayer) event.getPlayer()).getHandle(); | |
PacketUtil.handleContainerClick(packet, serverPlayer); | |
event.setCancelled(true); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.wizardlybump17.laptev.betterenderchest.api.event.PostInventoryClickEvent; | |
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; | |
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; | |
import lombok.experimental.UtilityClass; | |
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; | |
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket; | |
import net.minecraft.server.level.ServerPlayer; | |
import net.minecraft.world.inventory.AbstractContainerMenu; | |
import net.minecraft.world.inventory.Slot; | |
import net.minecraft.world.item.Item; | |
import net.minecraft.world.item.ItemStack; | |
import net.minecraft.world.level.block.Blocks; | |
import org.bukkit.Bukkit; | |
import org.bukkit.craftbukkit.v1_20_R1.util.CraftMagicNumbers; | |
import org.bukkit.event.inventory.*; | |
import org.bukkit.inventory.CraftingInventory; | |
import org.bukkit.inventory.InventoryView; | |
import org.bukkit.inventory.SmithingInventory; | |
@UtilityClass | |
public class PacketUtil { | |
/** | |
* <p> | |
* Manually handles the inventory click packet. The code of this method was copied from the {@link net.minecraft.server.network.ServerGamePacketListenerImpl#handleContainerClick(ServerboundContainerClickPacket)} | |
* but the code style was changed, which may cause some bugs because I inverted some {@code if}s to add {@code break}s and {@code return}s without testing properly.<br> | |
* I strongly believe that this method is working correctly, but if you find any bugs, please report them to me. | |
* </p> | |
* <p> | |
* The end of this method calls the {@link PostInventoryClickEvent}, which was the reason this method exists. | |
* </p> | |
* @param packet the packet to handle | |
* @param player the player who clicked | |
*/ | |
public static void handleContainerClick(ServerboundContainerClickPacket packet, ServerPlayer player) { | |
if (player.isImmobile()) | |
return; | |
player.resetLastActionTime(); | |
if (player.containerMenu.containerId != packet.getContainerId() || !player.containerMenu.stillValid(player)) | |
return; | |
boolean cancelled = player.isSpectator(); | |
int i = packet.getSlotNum(); | |
if (!player.containerMenu.isValidSlotIndex(i)) | |
return; | |
boolean flag = packet.getStateId() != player.containerMenu.getStateId(); | |
player.containerMenu.suppressRemoteUpdates(); | |
if (packet.getSlotNum() < -1 && packet.getSlotNum() != -999) | |
return; | |
InventoryView inventory = player.containerMenu.getBukkitView(); | |
InventoryType.SlotType type = inventory.getSlotType(packet.getSlotNum()); | |
InventoryClickEvent event; | |
ClickType click = ClickType.UNKNOWN; | |
InventoryAction action = InventoryAction.UNKNOWN; | |
switch (packet.getClickType()) { | |
case PICKUP -> { | |
if (packet.getButtonNum() == 0) | |
click = ClickType.LEFT; | |
else if (packet.getButtonNum() == 1) | |
click = ClickType.RIGHT; | |
if (packet.getButtonNum() != 0 && packet.getButtonNum() != 1) | |
break; | |
action = InventoryAction.NOTHING; | |
if (packet.getSlotNum() == -999) { | |
if (!player.containerMenu.getCarried().isEmpty()) | |
action = packet.getButtonNum() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR; | |
break; | |
} | |
if (packet.getSlotNum() < 0) | |
break; | |
Slot slot = player.containerMenu.getSlot(packet.getSlotNum()); | |
ItemStack clickedItem = slot.getItem(); | |
ItemStack cursor = player.containerMenu.getCarried(); | |
if (clickedItem.isEmpty()) { | |
if (!cursor.isEmpty()) | |
action = packet.getButtonNum() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE; | |
break; | |
} | |
if (!slot.mayPickup(player)) | |
break; | |
if (cursor.isEmpty()) { | |
action = packet.getButtonNum() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF; | |
break; | |
} | |
if (slot.mayPlace(cursor)) { | |
if (ItemStack.isSameItemSameTags(clickedItem, cursor)) { | |
int toPlace = packet.getButtonNum() == 0 ? cursor.getCount() : 1; | |
toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.getCount()); | |
toPlace = Math.min(toPlace, slot.container.getMaxStackSize() - clickedItem.getCount()); | |
if (toPlace == 1) | |
action = InventoryAction.PLACE_ONE; | |
else if (toPlace == cursor.getCount()) | |
action = InventoryAction.PLACE_ALL; | |
else if (toPlace < 0) | |
action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks | |
else if (toPlace != 0) | |
action = InventoryAction.PLACE_SOME; | |
} else if (cursor.getCount() <= slot.getMaxStackSize()) | |
action = InventoryAction.SWAP_WITH_CURSOR; | |
break; | |
} | |
if (!ItemStack.isSameItemSameTags(cursor, clickedItem) || clickedItem.getCount() < 0 || clickedItem.getCount() + cursor.getCount() > cursor.getMaxStackSize()) | |
break; | |
action = InventoryAction.PICKUP_ALL; | |
} | |
case QUICK_MOVE -> { | |
if (packet.getButtonNum() == 0) | |
click = ClickType.SHIFT_LEFT; | |
else if (packet.getButtonNum() == 1) | |
click = ClickType.SHIFT_RIGHT; | |
if (packet.getButtonNum() != 0 && packet.getButtonNum() != 1) | |
break; | |
if (packet.getSlotNum() < 0) { | |
action = InventoryAction.NOTHING; | |
break; | |
} | |
Slot slot = player.containerMenu.getSlot(packet.getSlotNum()); | |
if (slot.mayPickup(player) && slot.hasItem()) | |
action = InventoryAction.MOVE_TO_OTHER_INVENTORY; | |
else | |
action = InventoryAction.NOTHING; | |
} | |
case SWAP -> { | |
if (!((packet.getButtonNum() >= 0 && packet.getButtonNum() < 9) || packet.getButtonNum() == 40)) | |
break; | |
click = (packet.getButtonNum() == 40) ? ClickType.SWAP_OFFHAND : ClickType.NUMBER_KEY; | |
Slot clickedSlot = player.containerMenu.getSlot(packet.getSlotNum()); | |
if (!clickedSlot.mayPickup(player)) { | |
action = InventoryAction.NOTHING; | |
break; | |
} | |
ItemStack hotbar = player.getInventory().getItem(packet.getButtonNum()); | |
boolean canCleanSwap = hotbar.isEmpty() || (clickedSlot.container == player.getInventory() && clickedSlot.mayPlace(hotbar)); | |
if (clickedSlot.hasItem()) { | |
if (canCleanSwap) | |
action = InventoryAction.HOTBAR_SWAP; | |
else | |
action = InventoryAction.HOTBAR_MOVE_AND_READD; | |
} else if (!clickedSlot.hasItem() && !hotbar.isEmpty() && clickedSlot.mayPlace(hotbar)) | |
action = InventoryAction.HOTBAR_SWAP; | |
else | |
action = InventoryAction.NOTHING; | |
} | |
case CLONE -> { | |
if (packet.getButtonNum() != 2) | |
break; | |
click = ClickType.MIDDLE; | |
if (packet.getSlotNum() < 0) { | |
action = InventoryAction.NOTHING; | |
break; | |
} | |
Slot slot = player.containerMenu.getSlot(packet.getSlotNum()); | |
if (slot.hasItem() && player.getAbilities().instabuild && player.containerMenu.getCarried().isEmpty()) | |
action = InventoryAction.CLONE_STACK; | |
else | |
action = InventoryAction.NOTHING; | |
} | |
case THROW -> { | |
if (packet.getSlotNum() < 0) { | |
click = ClickType.LEFT; | |
if (packet.getButtonNum() == 1) | |
click = ClickType.RIGHT; | |
action = InventoryAction.NOTHING; | |
break; | |
} | |
if (packet.getButtonNum() == 0) { | |
click = ClickType.DROP; | |
Slot slot = player.containerMenu.getSlot(packet.getSlotNum()); | |
if (slot.hasItem() && slot.mayPickup(player) && !slot.getItem().isEmpty() && slot.getItem().getItem() != Item.byBlock(Blocks.AIR)) | |
action = InventoryAction.DROP_ONE_SLOT; | |
else | |
action = InventoryAction.NOTHING; | |
break; | |
} | |
if (packet.getButtonNum() != 1) | |
break; | |
click = ClickType.CONTROL_DROP; | |
Slot slot = player.containerMenu.getSlot(packet.getSlotNum()); | |
if (slot.hasItem() && slot.mayPickup(player) && !slot.getItem().isEmpty() && slot.getItem().getItem() != Item.byBlock(Blocks.AIR)) | |
action = InventoryAction.DROP_ALL_SLOT; | |
else | |
action = InventoryAction.NOTHING; | |
} | |
case QUICK_CRAFT -> player.containerMenu.clicked(packet.getSlotNum(), packet.getButtonNum(), packet.getClickType(), player); | |
case PICKUP_ALL -> { | |
click = ClickType.DOUBLE_CLICK; | |
action = InventoryAction.NOTHING; | |
if (packet.getSlotNum() < 0 || player.containerMenu.getCarried().isEmpty()) | |
break; | |
ItemStack cursor = player.containerMenu.getCarried(); | |
if (inventory.getTopInventory().contains(CraftMagicNumbers.getMaterial(cursor.getItem())) || inventory.getBottomInventory().contains(CraftMagicNumbers.getMaterial(cursor.getItem()))) | |
action = InventoryAction.COLLECT_TO_CURSOR; | |
} | |
default -> { | |
} | |
} | |
if (packet.getClickType() != net.minecraft.world.inventory.ClickType.QUICK_CRAFT) { | |
if (click == ClickType.NUMBER_KEY) | |
event = new InventoryClickEvent(inventory, type, packet.getSlotNum(), click, action, packet.getButtonNum()); | |
else | |
event = new InventoryClickEvent(inventory, type, packet.getSlotNum(), click, action); | |
org.bukkit.inventory.Inventory top = inventory.getTopInventory(); | |
if (packet.getSlotNum() == 0 && top instanceof CraftingInventory craftingInventory) { | |
org.bukkit.inventory.Recipe recipe = craftingInventory.getRecipe(); | |
if (recipe != null) { | |
if (click == ClickType.NUMBER_KEY) | |
event = new CraftItemEvent(recipe, inventory, type, packet.getSlotNum(), click, action, packet.getButtonNum()); | |
else | |
event = new CraftItemEvent(recipe, inventory, type, packet.getSlotNum(), click, action); | |
} | |
} | |
if (packet.getSlotNum() == 3 && top instanceof SmithingInventory smithingInventory) { | |
org.bukkit.inventory.ItemStack result = smithingInventory.getResult(); | |
if (result != null) { | |
if (click == ClickType.NUMBER_KEY) | |
event = new SmithItemEvent(inventory, type, packet.getSlotNum(), click, action, packet.getButtonNum()); | |
else | |
event = new SmithItemEvent(inventory, type, packet.getSlotNum(), click, action); | |
} | |
} | |
event.setCancelled(cancelled); | |
AbstractContainerMenu oldContainer = player.containerMenu; // SPIGOT-1224 | |
Bukkit.getPluginManager().callEvent(event); | |
if (player.containerMenu != oldContainer) | |
return; | |
switch (event.getResult()) { | |
case ALLOW, DEFAULT -> player.containerMenu.clicked(i, packet.getButtonNum(), packet.getClickType(), player); | |
case DENY -> { | |
switch (action) { | |
case PICKUP_ALL, MOVE_TO_OTHER_INVENTORY, HOTBAR_MOVE_AND_READD, HOTBAR_SWAP, COLLECT_TO_CURSOR, UNKNOWN -> | |
player.containerMenu.sendAllDataToRemote(); | |
case PICKUP_SOME, PICKUP_HALF, PICKUP_ONE, PLACE_ALL, PLACE_SOME, PLACE_ONE, SWAP_WITH_CURSOR -> { | |
player.connection.send(new ClientboundContainerSetSlotPacket(-1, -1, player.inventoryMenu.incrementStateId(), player.containerMenu.getCarried())); | |
player.connection.send(new ClientboundContainerSetSlotPacket(player.containerMenu.containerId, player.inventoryMenu.incrementStateId(), packet.getSlotNum(), player.containerMenu.getSlot(packet.getSlotNum()).getItem())); | |
} | |
case DROP_ONE_SLOT, DROP_ALL_SLOT -> player.connection.send(new ClientboundContainerSetSlotPacket(player.containerMenu.containerId, player.inventoryMenu.incrementStateId(), packet.getSlotNum(), player.containerMenu.getSlot(packet.getSlotNum()).getItem())); | |
case DROP_ALL_CURSOR, DROP_ONE_CURSOR, CLONE_STACK -> player.connection.send(new ClientboundContainerSetSlotPacket(-1, -1, player.inventoryMenu.incrementStateId(), player.containerMenu.getCarried())); | |
case NOTHING -> { | |
} | |
} | |
} | |
} | |
if (event instanceof CraftItemEvent || event instanceof SmithItemEvent) | |
player.containerMenu.sendAllDataToRemote(); | |
} | |
for (Int2ObjectMap.Entry<ItemStack> entry : Int2ObjectMaps.fastIterable(packet.getChangedSlots())) | |
player.containerMenu.setRemoteSlotNoCopy(entry.getIntKey(), entry.getValue()); | |
player.containerMenu.setRemoteCarried(packet.getCarriedItem()); | |
player.containerMenu.resumeRemoteUpdates(); | |
if (flag) | |
player.containerMenu.broadcastFullState(); | |
else | |
player.containerMenu.broadcastChanges(); | |
new PostInventoryClickEvent(inventory, packet.getSlotNum(), click, action, packet.getButtonNum()).callEvent(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import lombok.Getter; | |
import lombok.NonNull; | |
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket; | |
import net.minecraft.server.level.ServerPlayer; | |
import org.bukkit.entity.HumanEntity; | |
import org.bukkit.event.inventory.ClickType; | |
import org.bukkit.event.inventory.InventoryAction; | |
import org.bukkit.event.inventory.InventoryEvent; | |
import org.bukkit.event.inventory.InventoryType; | |
import org.bukkit.inventory.Inventory; | |
import org.bukkit.inventory.InventoryView; | |
import org.jetbrains.annotations.Nullable; | |
/** | |
* <p> | |
* This event is called after the {@link org.bukkit.event.inventory.InventoryClickEvent}. | |
* It is called in the end of the {@link com.wizardlybump17.laptev.betterenderchest.api.util.PacketUtil#handleContainerClick(ServerboundContainerClickPacket, ServerPlayer)} method. | |
* </p> | |
*/ | |
@Getter | |
public class PostInventoryClickEvent extends InventoryEvent { | |
private final int rawSlot; | |
private final @NonNull ClickType click; | |
private final @NonNull InventoryAction action; | |
private final int hotbarButton; | |
public PostInventoryClickEvent(@NonNull InventoryView transaction, int slot, @NonNull ClickType click, @NonNull InventoryAction action, int hotbarButton) { | |
super(transaction); | |
rawSlot = slot; | |
this.click = click; | |
this.action = action; | |
this.hotbarButton = hotbarButton; | |
} | |
public @Nullable Inventory getClickedInventory() { | |
return transaction.getInventory(rawSlot); | |
} | |
public int getSlot() { | |
return transaction.convertSlot(rawSlot); | |
} | |
public @NonNull InventoryType.SlotType getSlotType() { | |
return transaction.getSlotType(rawSlot); | |
} | |
public @NonNull HumanEntity getWhoClicked() { | |
return transaction.getPlayer(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
boa mano wizardly vou usar muito!!!