Skip to content

Instantly share code, notes, and snippets.

@WizardlyBump17
Created October 12, 2023 19:04
Show Gist options
  • Save WizardlyBump17/ad5e407a7a760a8671ebd5527b3da506 to your computer and use it in GitHub Desktop.
Save WizardlyBump17/ad5e407a7a760a8671ebd5527b3da506 to your computer and use it in GitHub Desktop.
Getting the inventory after the InventoryClickEvent
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);
}
});
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();
}
}
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();
}
}
@akafasty
Copy link

parabéns por criar algo inútil irmão! sucesso na carreira

@merelysnow
Copy link

boa mano wizardly vou usar muito!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment