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

@sn0wkzy
Copy link

sn0wkzy commented Oct 13, 2023

boa mano wizardly vou usar muito!!!

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