From f22a4959f2d1acc89d0934204e5df7b2f39e3e57 Mon Sep 17 00:00:00 2001 From: mrtz6 <78804817+mrtz6@users.noreply.github.com> Date: Wed, 20 May 2026 21:40:46 +0200 Subject: [PATCH 1/8] feat: add PlayerBundleItemSelectEvent --- build-data/paper.at | 1 + .../player/PlayerBundleItemSelectEvent.java | 65 +++++++++++++++++++ ...on-checking-in-player-move-packet-ha.patch | 12 ++-- ...0026-DataConverter-Moonrise-co-fixes.patch | 6 +- .../ServerGamePacketListenerImpl.java.patch | 36 +++++++++- 5 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java diff --git a/build-data/paper.at b/build-data/paper.at index fc06d593c438..0ef9b3431958 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -560,6 +560,7 @@ public net.minecraft.world.item.ItemStackLinkedSet TYPE_AND_TAG public net.minecraft.world.item.JukeboxSongPlayer song public net.minecraft.world.item.MapItem createNewSavedData(Lnet/minecraft/server/level/ServerLevel;IIIZZLnet/minecraft/resources/ResourceKey;)Lnet/minecraft/world/level/saveddata/maps/MapId; public net.minecraft.world.item.StandingAndWallBlockItem wallBlock +public net.minecraft.world.item.component.BundleContents$Mutable indexIsOutsideAllowedBounds(I)Z public net.minecraft.world.item.component.ItemContainerContents MAX_SIZE public net.minecraft.world.item.component.ItemContainerContents items public net.minecraft.world.item.component.ResolvableProfile unpack()Lcom/mojang/datafixers/util/Either; diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java new file mode 100644 index 000000000000..b5d3ced4945c --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java @@ -0,0 +1,65 @@ +package io.papermc.paper.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a {@link Player} selects an item inside a bundle. + *

+ * NOTE: This event does not fire for bundle item selections in creative mode player inventories. + */ +@NullMarked +public final class PlayerBundleItemSelectEvent extends PlayerEvent { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final ItemStack bundle; + private final ItemStack selectedItem; + private final int selectedIndex; + + @ApiStatus.Internal + public PlayerBundleItemSelectEvent(final Player player, final ItemStack bundle, final ItemStack selectedItem, final int selectedIndex) { + super(player); + + this.bundle = bundle; + this.selectedItem = selectedItem; + this.selectedIndex = selectedIndex; + } + + /** + * Gets the bundle item. + * @return the bundle item + */ + public ItemStack getBundle() { + return this.bundle; + } + + /** + * Gets the selected item inside the bundle. + * @return the selected item + */ + public ItemStack getSelectedItem() { + return this.selectedItem; + } + + /** + * Gets the selected index. + * @return the selected index + */ + public int getSelectedIndex() { + return this.selectedIndex; + } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } +} diff --git a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch index 068477b5ba1f..00ccf8091df6 100644 --- a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch +++ b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Optimise collision checking in player move packet handling Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index fad017de5d9fc31988aa4edf97fb3f9bcbee56c8..06132bde0aed9dd9088c884ee31056b9176b6f90 100644 +index d24c6e5369a25e847a342c0a97735c6270ae58a2..b62928b2203434fbc77b374428a69d19d89d99e9 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -633,6 +633,7 @@ public class ServerGamePacketListenerImpl @@ -72,7 +72,7 @@ index fad017de5d9fc31988aa4edf97fb3f9bcbee56c8..06132bde0aed9dd9088c884ee31056b9 } @Override -@@ -1541,7 +1575,7 @@ public class ServerGamePacketListenerImpl +@@ -1570,7 +1604,7 @@ public class ServerGamePacketListenerImpl } } @@ -81,7 +81,7 @@ index fad017de5d9fc31988aa4edf97fb3f9bcbee56c8..06132bde0aed9dd9088c884ee31056b9 xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above -@@ -1580,6 +1614,7 @@ public class ServerGamePacketListenerImpl +@@ -1609,6 +1643,7 @@ public class ServerGamePacketListenerImpl boolean playerStandsOnSomething = this.player.verticalCollisionBelow; this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist)); this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move @@ -89,7 +89,7 @@ index fad017de5d9fc31988aa4edf97fb3f9bcbee56c8..06132bde0aed9dd9088c884ee31056b9 // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { return; // ... thanks Mojang for letting move calls teleport across dimensions. -@@ -1613,7 +1648,17 @@ public class ServerGamePacketListenerImpl +@@ -1642,7 +1677,17 @@ public class ServerGamePacketListenerImpl } // Paper start - Add fail move event @@ -108,7 +108,7 @@ index fad017de5d9fc31988aa4edf97fb3f9bcbee56c8..06132bde0aed9dd9088c884ee31056b9 if (!allowMovement) { io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, targetX, targetY, targetZ, targetYRot, targetXRot, false); -@@ -1751,7 +1796,7 @@ public class ServerGamePacketListenerImpl +@@ -1780,7 +1825,7 @@ public class ServerGamePacketListenerImpl private boolean updateAwaitingTeleport() { if (this.awaitingPositionFromClient != null) { @@ -117,7 +117,7 @@ index fad017de5d9fc31988aa4edf97fb3f9bcbee56c8..06132bde0aed9dd9088c884ee31056b9 this.awaitingTeleportTime = this.tickCount; this.teleport( this.awaitingPositionFromClient.x, -@@ -1770,6 +1815,34 @@ public class ServerGamePacketListenerImpl +@@ -1799,6 +1844,34 @@ public class ServerGamePacketListenerImpl } } diff --git a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch index 900a854dba73..11d69922f815 100644 --- a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch +++ b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch @@ -42,10 +42,10 @@ index d2877c20f389d0131e1dd208b464f590671e5d82..27bdc70d861ca39487ad16cb3afb89d6 updateComponent(root, "minecraft:trim", hiddenComponents); updateComponent(root, "minecraft:unbreakable", hiddenComponents); diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 06132bde0aed9dd9088c884ee31056b9176b6f90..08374da2d9b84e8ff13c670d7f45867fb0bdd897 100644 +index b62928b2203434fbc77b374428a69d19d89d99e9..46d6c6e345c3eb9652c9ef9737d8446165ebe466 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1819,9 +1819,14 @@ public class ServerGamePacketListenerImpl +@@ -1848,9 +1848,14 @@ public class ServerGamePacketListenerImpl private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) { final List collisionsBB = new java.util.ArrayList<>(); final List collisionsVoxel = new java.util.ArrayList<>(); @@ -62,7 +62,7 @@ index 06132bde0aed9dd9088c884ee31056b9176b6f90..08374da2d9b84e8ff13c670d7f45867f ); diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index fa3809be5c0488c5a1d2639579d68619b0a55de9..c332d48f5cac2f3be93201c66f4f7cdba916cbac 100644 +index 7e4cf8a13e0ff2198cb7b6d80fb3fa343339943b..4522fde3a08637a01130ad39dc8a11a2c4f7f1cb 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java @@ -1529,7 +1529,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 535ad83014bf..a303f4278490 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -382,7 +382,41 @@ } @Override -@@ -572,6 +_,7 @@ +@@ -567,11 +_,41 @@ + public void handleBundleItemSelectedPacket(final ServerboundSelectBundleItemPacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + this.player.containerMenu.setSelectedBundleItemIndex(packet.slotId(), packet.selectedItemIndex()); ++ ++ // Paper start - PlayerBundleItemSelectEvent ++ if (this.player.isCreative() && this.player.containerMenu instanceof net.minecraft.world.inventory.InventoryMenu) { ++ return; ++ } ++ ++ final int slotId = packet.slotId(); ++ final int selectedIndex = packet.selectedItemIndex(); ++ if (slotId < 0 || slotId >= this.player.containerMenu.slots.size() || selectedIndex == net.minecraft.world.item.component.BundleContents.NO_SELECTED_ITEM_INDEX) { ++ return; ++ } ++ ++ final ItemStack item = this.player.containerMenu.slots.get(slotId).getItem(); ++ if (!item.is(net.minecraft.tags.ItemTags.BUNDLES)) { ++ return; ++ } ++ ++ final net.minecraft.world.item.component.BundleContents contents = item.get(net.minecraft.core.component.DataComponents.BUNDLE_CONTENTS); ++ if (contents != null) { ++ if (!new net.minecraft.world.item.component.BundleContents.Mutable(contents).indexIsOutsideAllowedBounds(selectedIndex)) { ++ new io.papermc.paper.event.player.PlayerBundleItemSelectEvent( ++ this.getCraftPlayer(), ++ item.asBukkitMirror(), ++ java.util.Objects.requireNonNull(contents.getSelectedItem()).create().asBukkitMirror(), ++ selectedIndex ++ ).callEvent(); ++ } ++ } ++ // Paper end - PlayerBundleItemSelectEvent + } + @Override public void handleRecipeBookChangeSettingsPacket(final ServerboundRecipeBookChangeSettingsPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); From 69f438169761d575d40c866a7d7ed13581162360 Mon Sep 17 00:00:00 2001 From: mrtz6 <78804817+mrtz6@users.noreply.github.com> Date: Wed, 20 May 2026 22:00:09 +0200 Subject: [PATCH 2/8] style: adjust javadoc formatting --- .../paper/event/player/PlayerBundleItemSelectEvent.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java index b5d3ced4945c..1e0f385219b0 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java @@ -32,6 +32,7 @@ public PlayerBundleItemSelectEvent(final Player player, final ItemStack bundle, /** * Gets the bundle item. + * * @return the bundle item */ public ItemStack getBundle() { @@ -40,6 +41,7 @@ public ItemStack getBundle() { /** * Gets the selected item inside the bundle. + * * @return the selected item */ public ItemStack getSelectedItem() { @@ -48,6 +50,7 @@ public ItemStack getSelectedItem() { /** * Gets the selected index. + * * @return the selected index */ public int getSelectedIndex() { From 1e189bda49955db1c5c785e7f6b18214da2fa544 Mon Sep 17 00:00:00 2001 From: mrtz6 <78804817+mrtz6@users.noreply.github.com> Date: Wed, 20 May 2026 22:21:00 +0200 Subject: [PATCH 3/8] format: add clarification parenthesis --- .../server/network/ServerGamePacketListenerImpl.java.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index a303f4278490..6bce759532d3 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -405,7 +405,7 @@ + + final net.minecraft.world.item.component.BundleContents contents = item.get(net.minecraft.core.component.DataComponents.BUNDLE_CONTENTS); + if (contents != null) { -+ if (!new net.minecraft.world.item.component.BundleContents.Mutable(contents).indexIsOutsideAllowedBounds(selectedIndex)) { ++ if (!(new net.minecraft.world.item.component.BundleContents.Mutable(contents)).indexIsOutsideAllowedBounds(selectedIndex)) { + new io.papermc.paper.event.player.PlayerBundleItemSelectEvent( + this.getCraftPlayer(), + item.asBukkitMirror(), From 0115042404b3d8af7aef509d02b588b138f61c0a Mon Sep 17 00:00:00 2001 From: mrtz6 <78804817+mrtz6@users.noreply.github.com> Date: Wed, 20 May 2026 22:49:25 +0200 Subject: [PATCH 4/8] docs: clarify return on creative mode --- ...collision-checking-in-player-move-packet-ha.patch | 12 ++++++------ .../0026-DataConverter-Moonrise-co-fixes.patch | 4 ++-- .../network/ServerGamePacketListenerImpl.java.patch | 4 +++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch index 00ccf8091df6..b19632d06779 100644 --- a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch +++ b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Optimise collision checking in player move packet handling Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index d24c6e5369a25e847a342c0a97735c6270ae58a2..b62928b2203434fbc77b374428a69d19d89d99e9 100644 +index ec9ad6ae3166771ccc43f10b9b7f58831376b924..718cfc527f93c1cb2e7808a1d8d511790efcff6c 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -633,6 +633,7 @@ public class ServerGamePacketListenerImpl @@ -72,7 +72,7 @@ index d24c6e5369a25e847a342c0a97735c6270ae58a2..b62928b2203434fbc77b374428a69d19 } @Override -@@ -1570,7 +1604,7 @@ public class ServerGamePacketListenerImpl +@@ -1572,7 +1606,7 @@ public class ServerGamePacketListenerImpl } } @@ -81,7 +81,7 @@ index d24c6e5369a25e847a342c0a97735c6270ae58a2..b62928b2203434fbc77b374428a69d19 xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above -@@ -1609,6 +1643,7 @@ public class ServerGamePacketListenerImpl +@@ -1611,6 +1645,7 @@ public class ServerGamePacketListenerImpl boolean playerStandsOnSomething = this.player.verticalCollisionBelow; this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist)); this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move @@ -89,7 +89,7 @@ index d24c6e5369a25e847a342c0a97735c6270ae58a2..b62928b2203434fbc77b374428a69d19 // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { return; // ... thanks Mojang for letting move calls teleport across dimensions. -@@ -1642,7 +1677,17 @@ public class ServerGamePacketListenerImpl +@@ -1644,7 +1679,17 @@ public class ServerGamePacketListenerImpl } // Paper start - Add fail move event @@ -108,7 +108,7 @@ index d24c6e5369a25e847a342c0a97735c6270ae58a2..b62928b2203434fbc77b374428a69d19 if (!allowMovement) { io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, targetX, targetY, targetZ, targetYRot, targetXRot, false); -@@ -1780,7 +1825,7 @@ public class ServerGamePacketListenerImpl +@@ -1782,7 +1827,7 @@ public class ServerGamePacketListenerImpl private boolean updateAwaitingTeleport() { if (this.awaitingPositionFromClient != null) { @@ -117,7 +117,7 @@ index d24c6e5369a25e847a342c0a97735c6270ae58a2..b62928b2203434fbc77b374428a69d19 this.awaitingTeleportTime = this.tickCount; this.teleport( this.awaitingPositionFromClient.x, -@@ -1799,6 +1844,34 @@ public class ServerGamePacketListenerImpl +@@ -1801,6 +1846,34 @@ public class ServerGamePacketListenerImpl } } diff --git a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch index 11d69922f815..c79988809121 100644 --- a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch +++ b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch @@ -42,10 +42,10 @@ index d2877c20f389d0131e1dd208b464f590671e5d82..27bdc70d861ca39487ad16cb3afb89d6 updateComponent(root, "minecraft:trim", hiddenComponents); updateComponent(root, "minecraft:unbreakable", hiddenComponents); diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index b62928b2203434fbc77b374428a69d19d89d99e9..46d6c6e345c3eb9652c9ef9737d8446165ebe466 100644 +index 718cfc527f93c1cb2e7808a1d8d511790efcff6c..80233a6a16197f26b4c2d59189a69429d7819d11 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1848,9 +1848,14 @@ public class ServerGamePacketListenerImpl +@@ -1850,9 +1850,14 @@ public class ServerGamePacketListenerImpl private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) { final List collisionsBB = new java.util.ArrayList<>(); final List collisionsVoxel = new java.util.ArrayList<>(); diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 6bce759532d3..e6917337ea2b 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -382,13 +382,15 @@ } @Override -@@ -567,11 +_,41 @@ +@@ -567,11 +_,43 @@ public void handleBundleItemSelectedPacket(final ServerboundSelectBundleItemPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); this.player.containerMenu.setSelectedBundleItemIndex(packet.slotId(), packet.selectedItemIndex()); + + // Paper start - PlayerBundleItemSelectEvent + if (this.player.isCreative() && this.player.containerMenu instanceof net.minecraft.world.inventory.InventoryMenu) { ++ // The packet's slotId will always be 0 if this packet is sent for a player selecting an item in a bundle in their creative inventory, ++ // making it unfit for firing an event. + return; + } + From 3bb7d561abd79cf2312a7c45b2549075cb34bec7 Mon Sep 17 00:00:00 2001 From: mrtz6 <78804817+mrtz6@users.noreply.github.com> Date: Thu, 21 May 2026 13:44:45 +0200 Subject: [PATCH 5/8] feat: expose previous selection and change supertype to InventoryEvent --- .../PlayerBundleItemSelectEvent.java | 130 ++++++++++++++++++ .../player/PlayerBundleItemSelectEvent.java | 68 --------- ...on-checking-in-player-move-packet-ha.patch | 12 +- ...0026-DataConverter-Moonrise-co-fixes.patch | 4 +- .../ServerGamePacketListenerImpl.java.patch | 55 ++++---- 5 files changed, 166 insertions(+), 103 deletions(-) create mode 100644 paper-api/src/main/java/io/papermc/paper/event/inventory/PlayerBundleItemSelectEvent.java delete mode 100644 paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java diff --git a/paper-api/src/main/java/io/papermc/paper/event/inventory/PlayerBundleItemSelectEvent.java b/paper-api/src/main/java/io/papermc/paper/event/inventory/PlayerBundleItemSelectEvent.java new file mode 100644 index 000000000000..f27f7cde6259 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/inventory/PlayerBundleItemSelectEvent.java @@ -0,0 +1,130 @@ +package io.papermc.paper.event.inventory; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a {@link Player} selects an item inside a bundle. + *

+ * NOTE: This event does not fire for bundle item selections in creative mode player inventories. + */ +@NullMarked +public final class PlayerBundleItemSelectEvent extends InventoryEvent { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final ItemStack bundle; + private final int rawSlot; + private final int slot; + + private final ItemStack previousItem; + private final ItemStack selectedItem; + + private final int previousIndex; + private final int selectedIndex; + + @ApiStatus.Internal + public PlayerBundleItemSelectEvent(final InventoryView view, final ItemStack bundle, final int rawSlot, final ItemStack previousItem, final ItemStack selectedItem, final int previousIndex, final int selectedIndex) { + super(view); + + this.bundle = bundle; + this.rawSlot = rawSlot; + this.slot = view.convertSlot(rawSlot); + + this.previousItem = previousItem; + this.selectedItem = selectedItem; + + this.previousIndex = previousIndex; + this.selectedIndex = selectedIndex; + } + + /** + * Gets the player who triggered the event. + * + * @return the player + */ + public Player getPlayer() { + return (Player) this.getView().getPlayer(); + } + + /** + * Gets the bundle item. + * + * @return the bundle item + */ + public ItemStack getBundle() { + return this.bundle; + } + + /** + * Gets the slot number of the bundle, depending on which {@link Inventory} it is located in. + * + * @return the slot number + * @see InventoryClickEvent#getSlot() + */ + public int getSlot() { + return this.slot; + } + + /** + * Gets the raw slot number of the bundle inside the {@link InventoryView}. + * + * @return the raw slot number + * @see InventoryClickEvent#getRawSlot() + */ + public int getRawSlot() { + return this.rawSlot; + } + + /** + * Gets the previously selected item inside the bundle. If no item was previously selected, this will return an empty item. + + * @return the previously selected item + */ + public ItemStack getPreviousItem() { + return this.previousItem; + } + + /** + * Gets the selected item inside the bundle. + * + * @return the selected item + */ + public ItemStack getSelectedItem() { + return this.selectedItem; + } + + /** + * Gets the previously selected index. If no item was previously selected, this will be -1. + * + * @return the previously selected index + */ + public int getPreviousIndex() { + return this.previousIndex; + } + + /** + * Gets the selected index. + * + * @return the selected index + */ + public int getSelectedIndex() { + return this.selectedIndex; + } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java deleted file mode 100644 index 1e0f385219b0..000000000000 --- a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerBundleItemSelectEvent.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.papermc.paper.event.player; - -import org.bukkit.entity.Player; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.PlayerEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; - -/** - * Called when a {@link Player} selects an item inside a bundle. - *

- * NOTE: This event does not fire for bundle item selections in creative mode player inventories. - */ -@NullMarked -public final class PlayerBundleItemSelectEvent extends PlayerEvent { - - private static final HandlerList HANDLER_LIST = new HandlerList(); - - private final ItemStack bundle; - private final ItemStack selectedItem; - private final int selectedIndex; - - @ApiStatus.Internal - public PlayerBundleItemSelectEvent(final Player player, final ItemStack bundle, final ItemStack selectedItem, final int selectedIndex) { - super(player); - - this.bundle = bundle; - this.selectedItem = selectedItem; - this.selectedIndex = selectedIndex; - } - - /** - * Gets the bundle item. - * - * @return the bundle item - */ - public ItemStack getBundle() { - return this.bundle; - } - - /** - * Gets the selected item inside the bundle. - * - * @return the selected item - */ - public ItemStack getSelectedItem() { - return this.selectedItem; - } - - /** - * Gets the selected index. - * - * @return the selected index - */ - public int getSelectedIndex() { - return this.selectedIndex; - } - - @Override - public HandlerList getHandlers() { - return HANDLER_LIST; - } - - public static HandlerList getHandlerList() { - return HANDLER_LIST; - } -} diff --git a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch index b19632d06779..2c5f7d617533 100644 --- a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch +++ b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Optimise collision checking in player move packet handling Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index ec9ad6ae3166771ccc43f10b9b7f58831376b924..718cfc527f93c1cb2e7808a1d8d511790efcff6c 100644 +index 679e32dcc04e4affca327e1f77920630ec3a1842..e96efe1c8192e9fb5930adc46195ce753856263d 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -633,6 +633,7 @@ public class ServerGamePacketListenerImpl @@ -72,7 +72,7 @@ index ec9ad6ae3166771ccc43f10b9b7f58831376b924..718cfc527f93c1cb2e7808a1d8d51179 } @Override -@@ -1572,7 +1606,7 @@ public class ServerGamePacketListenerImpl +@@ -1570,7 +1604,7 @@ public class ServerGamePacketListenerImpl } } @@ -81,7 +81,7 @@ index ec9ad6ae3166771ccc43f10b9b7f58831376b924..718cfc527f93c1cb2e7808a1d8d51179 xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above -@@ -1611,6 +1645,7 @@ public class ServerGamePacketListenerImpl +@@ -1609,6 +1643,7 @@ public class ServerGamePacketListenerImpl boolean playerStandsOnSomething = this.player.verticalCollisionBelow; this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist)); this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move @@ -89,7 +89,7 @@ index ec9ad6ae3166771ccc43f10b9b7f58831376b924..718cfc527f93c1cb2e7808a1d8d51179 // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { return; // ... thanks Mojang for letting move calls teleport across dimensions. -@@ -1644,7 +1679,17 @@ public class ServerGamePacketListenerImpl +@@ -1642,7 +1677,17 @@ public class ServerGamePacketListenerImpl } // Paper start - Add fail move event @@ -108,7 +108,7 @@ index ec9ad6ae3166771ccc43f10b9b7f58831376b924..718cfc527f93c1cb2e7808a1d8d51179 if (!allowMovement) { io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, targetX, targetY, targetZ, targetYRot, targetXRot, false); -@@ -1782,7 +1827,7 @@ public class ServerGamePacketListenerImpl +@@ -1780,7 +1825,7 @@ public class ServerGamePacketListenerImpl private boolean updateAwaitingTeleport() { if (this.awaitingPositionFromClient != null) { @@ -117,7 +117,7 @@ index ec9ad6ae3166771ccc43f10b9b7f58831376b924..718cfc527f93c1cb2e7808a1d8d51179 this.awaitingTeleportTime = this.tickCount; this.teleport( this.awaitingPositionFromClient.x, -@@ -1801,6 +1846,34 @@ public class ServerGamePacketListenerImpl +@@ -1799,6 +1844,34 @@ public class ServerGamePacketListenerImpl } } diff --git a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch index c79988809121..bc3d24ba71ab 100644 --- a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch +++ b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch @@ -42,10 +42,10 @@ index d2877c20f389d0131e1dd208b464f590671e5d82..27bdc70d861ca39487ad16cb3afb89d6 updateComponent(root, "minecraft:trim", hiddenComponents); updateComponent(root, "minecraft:unbreakable", hiddenComponents); diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 718cfc527f93c1cb2e7808a1d8d511790efcff6c..80233a6a16197f26b4c2d59189a69429d7819d11 100644 +index e96efe1c8192e9fb5930adc46195ce753856263d..f04d07d82f3d9c693fa7765570162677e09dd625 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1850,9 +1850,14 @@ public class ServerGamePacketListenerImpl +@@ -1848,9 +1848,14 @@ public class ServerGamePacketListenerImpl private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) { final List collisionsBB = new java.util.ArrayList<>(); final List collisionsVoxel = new java.util.ArrayList<>(); diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index e6917337ea2b..c5b9a51aef9e 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -382,41 +382,42 @@ } @Override -@@ -567,11 +_,43 @@ +@@ -565,13 +_,43 @@ + + @Override public void handleBundleItemSelectedPacket(final ServerboundSelectBundleItemPacket packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); - this.player.containerMenu.setSelectedBundleItemIndex(packet.slotId(), packet.selectedItemIndex()); -+ +- PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); ++ net.minecraft.network.protocol.PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); ++ // this.player.containerMenu.setSelectedBundleItemIndex(packet.slotId(), packet.selectedItemIndex()); // Paper - PlayerBundleItemSelectEvent; moved to expose previous index + // Paper start - PlayerBundleItemSelectEvent -+ if (this.player.isCreative() && this.player.containerMenu instanceof net.minecraft.world.inventory.InventoryMenu) { ++ if (!this.player.isCreative() || !(this.player.containerMenu instanceof net.minecraft.world.inventory.InventoryMenu)) { + // The packet's slotId will always be 0 if this packet is sent for a player selecting an item in a bundle in their creative inventory, + // making it unfit for firing an event. -+ return; -+ } -+ -+ final int slotId = packet.slotId(); -+ final int selectedIndex = packet.selectedItemIndex(); -+ if (slotId < 0 || slotId >= this.player.containerMenu.slots.size() || selectedIndex == net.minecraft.world.item.component.BundleContents.NO_SELECTED_ITEM_INDEX) { -+ return; -+ } -+ -+ final ItemStack item = this.player.containerMenu.slots.get(slotId).getItem(); -+ if (!item.is(net.minecraft.tags.ItemTags.BUNDLES)) { -+ return; -+ } + -+ final net.minecraft.world.item.component.BundleContents contents = item.get(net.minecraft.core.component.DataComponents.BUNDLE_CONTENTS); -+ if (contents != null) { -+ if (!(new net.minecraft.world.item.component.BundleContents.Mutable(contents)).indexIsOutsideAllowedBounds(selectedIndex)) { -+ new io.papermc.paper.event.player.PlayerBundleItemSelectEvent( -+ this.getCraftPlayer(), -+ item.asBukkitMirror(), -+ java.util.Objects.requireNonNull(contents.getSelectedItem()).create().asBukkitMirror(), -+ selectedIndex -+ ).callEvent(); ++ final int slotId = packet.slotId(); ++ final int selectedIndex = packet.selectedItemIndex(); ++ if (slotId >= 0 && slotId < this.player.containerMenu.slots.size() && selectedIndex != net.minecraft.world.item.component.BundleContents.NO_SELECTED_ITEM_INDEX) { ++ final net.minecraft.world.item.ItemStack item = this.player.containerMenu.slots.get(slotId).getItem(); ++ if (item.is(net.minecraft.tags.ItemTags.BUNDLES)) { ++ final net.minecraft.world.item.component.BundleContents contents = item.get(net.minecraft.core.component.DataComponents.BUNDLE_CONTENTS); ++ if (contents != null) { ++ if (!(new net.minecraft.world.item.component.BundleContents.Mutable(contents)).indexIsOutsideAllowedBounds(selectedIndex)) { ++ new io.papermc.paper.event.inventory.PlayerBundleItemSelectEvent( ++ this.player.containerMenu.getBukkitView(), ++ item.asBukkitMirror(), ++ slotId, ++ contents.getSelectedItem() != null ? contents.getSelectedItem().create().asBukkitMirror() : org.bukkit.inventory.ItemStack.empty(), ++ contents.items().get(selectedIndex).create().asBukkitMirror(), ++ contents.getSelectedItemIndex(), ++ selectedIndex ++ ).callEvent(); ++ } ++ } ++ } + } + } + // Paper end - PlayerBundleItemSelectEvent + this.player.containerMenu.setSelectedBundleItemIndex(packet.slotId(), packet.selectedItemIndex()); } @Override From 7804af60f10a450e4e2d73a0c163748abef57393 Mon Sep 17 00:00:00 2001 From: mrtz6 <78804817+mrtz6@users.noreply.github.com> Date: Thu, 21 May 2026 15:10:24 +0200 Subject: [PATCH 6/8] fix: revert FQDN addition --- .../server/network/ServerGamePacketListenerImpl.java.patch | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index c5b9a51aef9e..7cc8a823e8d3 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -382,12 +382,10 @@ } @Override -@@ -565,13 +_,43 @@ - +@@ -566,12 +_,42 @@ @Override public void handleBundleItemSelectedPacket(final ServerboundSelectBundleItemPacket packet) { -- PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); -+ net.minecraft.network.protocol.PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + // this.player.containerMenu.setSelectedBundleItemIndex(packet.slotId(), packet.selectedItemIndex()); // Paper - PlayerBundleItemSelectEvent; moved to expose previous index + // Paper start - PlayerBundleItemSelectEvent + if (!this.player.isCreative() || !(this.player.containerMenu instanceof net.minecraft.world.inventory.InventoryMenu)) { From a0ded529266c6fccdbd8fe96e9b8169042432fac Mon Sep 17 00:00:00 2001 From: mrtz6 <78804817+mrtz6@users.noreply.github.com> Date: Thu, 21 May 2026 16:12:13 +0200 Subject: [PATCH 7/8] docs: remove unnecessary comment --- ...collision-checking-in-player-move-packet-ha.patch | 12 ++++++------ .../0026-DataConverter-Moonrise-co-fixes.patch | 4 ++-- .../network/ServerGamePacketListenerImpl.java.patch | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch index 2c5f7d617533..11f19a0cc658 100644 --- a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch +++ b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Optimise collision checking in player move packet handling Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 679e32dcc04e4affca327e1f77920630ec3a1842..e96efe1c8192e9fb5930adc46195ce753856263d 100644 +index 897d08ff6ee4a590c345d35b8b5c0a3195f20184..81e8d5b62481229f1af04f376ed8bfbb876e8158 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -633,6 +633,7 @@ public class ServerGamePacketListenerImpl @@ -72,7 +72,7 @@ index 679e32dcc04e4affca327e1f77920630ec3a1842..e96efe1c8192e9fb5930adc46195ce75 } @Override -@@ -1570,7 +1604,7 @@ public class ServerGamePacketListenerImpl +@@ -1569,7 +1603,7 @@ public class ServerGamePacketListenerImpl } } @@ -81,7 +81,7 @@ index 679e32dcc04e4affca327e1f77920630ec3a1842..e96efe1c8192e9fb5930adc46195ce75 xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above -@@ -1609,6 +1643,7 @@ public class ServerGamePacketListenerImpl +@@ -1608,6 +1642,7 @@ public class ServerGamePacketListenerImpl boolean playerStandsOnSomething = this.player.verticalCollisionBelow; this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist)); this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move @@ -89,7 +89,7 @@ index 679e32dcc04e4affca327e1f77920630ec3a1842..e96efe1c8192e9fb5930adc46195ce75 // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { return; // ... thanks Mojang for letting move calls teleport across dimensions. -@@ -1642,7 +1677,17 @@ public class ServerGamePacketListenerImpl +@@ -1641,7 +1676,17 @@ public class ServerGamePacketListenerImpl } // Paper start - Add fail move event @@ -108,7 +108,7 @@ index 679e32dcc04e4affca327e1f77920630ec3a1842..e96efe1c8192e9fb5930adc46195ce75 if (!allowMovement) { io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, targetX, targetY, targetZ, targetYRot, targetXRot, false); -@@ -1780,7 +1825,7 @@ public class ServerGamePacketListenerImpl +@@ -1779,7 +1824,7 @@ public class ServerGamePacketListenerImpl private boolean updateAwaitingTeleport() { if (this.awaitingPositionFromClient != null) { @@ -117,7 +117,7 @@ index 679e32dcc04e4affca327e1f77920630ec3a1842..e96efe1c8192e9fb5930adc46195ce75 this.awaitingTeleportTime = this.tickCount; this.teleport( this.awaitingPositionFromClient.x, -@@ -1799,6 +1844,34 @@ public class ServerGamePacketListenerImpl +@@ -1798,6 +1843,34 @@ public class ServerGamePacketListenerImpl } } diff --git a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch index bc3d24ba71ab..0a9f0aa1022d 100644 --- a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch +++ b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch @@ -42,10 +42,10 @@ index d2877c20f389d0131e1dd208b464f590671e5d82..27bdc70d861ca39487ad16cb3afb89d6 updateComponent(root, "minecraft:trim", hiddenComponents); updateComponent(root, "minecraft:unbreakable", hiddenComponents); diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index e96efe1c8192e9fb5930adc46195ce753856263d..f04d07d82f3d9c693fa7765570162677e09dd625 100644 +index 81e8d5b62481229f1af04f376ed8bfbb876e8158..128d50a97d04aa658e31caf21eb6a9c88c46efbe 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1848,9 +1848,14 @@ public class ServerGamePacketListenerImpl +@@ -1847,9 +1847,14 @@ public class ServerGamePacketListenerImpl private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) { final List collisionsBB = new java.util.ArrayList<>(); final List collisionsVoxel = new java.util.ArrayList<>(); diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 7cc8a823e8d3..32833c0cc7df 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -382,11 +382,10 @@ } @Override -@@ -566,12 +_,42 @@ +@@ -566,12 +_,41 @@ @Override public void handleBundleItemSelectedPacket(final ServerboundSelectBundleItemPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); -+ // this.player.containerMenu.setSelectedBundleItemIndex(packet.slotId(), packet.selectedItemIndex()); // Paper - PlayerBundleItemSelectEvent; moved to expose previous index + // Paper start - PlayerBundleItemSelectEvent + if (!this.player.isCreative() || !(this.player.containerMenu instanceof net.minecraft.world.inventory.InventoryMenu)) { + // The packet's slotId will always be 0 if this packet is sent for a player selecting an item in a bundle in their creative inventory, From e5e563f67f3f2c236b3f41134c43773029248db7 Mon Sep 17 00:00:00 2001 From: mrtz6 <78804817+mrtz6@users.noreply.github.com> Date: Thu, 21 May 2026 16:44:54 +0200 Subject: [PATCH 8/8] refactor: extract event firing to CraftEventFactory --- ...on-checking-in-player-move-packet-ha.patch | 12 +++---- ...0026-DataConverter-Moonrise-co-fixes.patch | 4 +-- .../ServerGamePacketListenerImpl.java.patch | 31 ++---------------- .../craftbukkit/event/CraftEventFactory.java | 32 +++++++++++++++++++ 4 files changed, 42 insertions(+), 37 deletions(-) diff --git a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch index 11f19a0cc658..abeae4a7c8de 100644 --- a/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch +++ b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Optimise collision checking in player move packet handling Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 897d08ff6ee4a590c345d35b8b5c0a3195f20184..81e8d5b62481229f1af04f376ed8bfbb876e8158 100644 +index e8ec4da3241c2cb212efdb75974e942b5d434695..5933a3a25954c0c7a2eac2361a805c751f286684 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -633,6 +633,7 @@ public class ServerGamePacketListenerImpl @@ -72,7 +72,7 @@ index 897d08ff6ee4a590c345d35b8b5c0a3195f20184..81e8d5b62481229f1af04f376ed8bfbb } @Override -@@ -1569,7 +1603,7 @@ public class ServerGamePacketListenerImpl +@@ -1542,7 +1576,7 @@ public class ServerGamePacketListenerImpl } } @@ -81,7 +81,7 @@ index 897d08ff6ee4a590c345d35b8b5c0a3195f20184..81e8d5b62481229f1af04f376ed8bfbb xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above -@@ -1608,6 +1642,7 @@ public class ServerGamePacketListenerImpl +@@ -1581,6 +1615,7 @@ public class ServerGamePacketListenerImpl boolean playerStandsOnSomething = this.player.verticalCollisionBelow; this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist)); this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move @@ -89,7 +89,7 @@ index 897d08ff6ee4a590c345d35b8b5c0a3195f20184..81e8d5b62481229f1af04f376ed8bfbb // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { return; // ... thanks Mojang for letting move calls teleport across dimensions. -@@ -1641,7 +1676,17 @@ public class ServerGamePacketListenerImpl +@@ -1614,7 +1649,17 @@ public class ServerGamePacketListenerImpl } // Paper start - Add fail move event @@ -108,7 +108,7 @@ index 897d08ff6ee4a590c345d35b8b5c0a3195f20184..81e8d5b62481229f1af04f376ed8bfbb if (!allowMovement) { io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, targetX, targetY, targetZ, targetYRot, targetXRot, false); -@@ -1779,7 +1824,7 @@ public class ServerGamePacketListenerImpl +@@ -1752,7 +1797,7 @@ public class ServerGamePacketListenerImpl private boolean updateAwaitingTeleport() { if (this.awaitingPositionFromClient != null) { @@ -117,7 +117,7 @@ index 897d08ff6ee4a590c345d35b8b5c0a3195f20184..81e8d5b62481229f1af04f376ed8bfbb this.awaitingTeleportTime = this.tickCount; this.teleport( this.awaitingPositionFromClient.x, -@@ -1798,6 +1843,34 @@ public class ServerGamePacketListenerImpl +@@ -1771,6 +1816,34 @@ public class ServerGamePacketListenerImpl } } diff --git a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch index 0a9f0aa1022d..e10ebdbe87f6 100644 --- a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch +++ b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch @@ -42,10 +42,10 @@ index d2877c20f389d0131e1dd208b464f590671e5d82..27bdc70d861ca39487ad16cb3afb89d6 updateComponent(root, "minecraft:trim", hiddenComponents); updateComponent(root, "minecraft:unbreakable", hiddenComponents); diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 81e8d5b62481229f1af04f376ed8bfbb876e8158..128d50a97d04aa658e31caf21eb6a9c88c46efbe 100644 +index 5933a3a25954c0c7a2eac2361a805c751f286684..be4c9d9d70f0bac382f008ab9decc9023dbc5e57 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1847,9 +1847,14 @@ public class ServerGamePacketListenerImpl +@@ -1820,9 +1820,14 @@ public class ServerGamePacketListenerImpl private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) { final List collisionsBB = new java.util.ArrayList<>(); final List collisionsVoxel = new java.util.ArrayList<>(); diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 32833c0cc7df..76249fa5cc28 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -382,38 +382,11 @@ } @Override -@@ -566,12 +_,41 @@ +@@ -566,12 +_,14 @@ @Override public void handleBundleItemSelectedPacket(final ServerboundSelectBundleItemPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); -+ // Paper start - PlayerBundleItemSelectEvent -+ if (!this.player.isCreative() || !(this.player.containerMenu instanceof net.minecraft.world.inventory.InventoryMenu)) { -+ // The packet's slotId will always be 0 if this packet is sent for a player selecting an item in a bundle in their creative inventory, -+ // making it unfit for firing an event. -+ -+ final int slotId = packet.slotId(); -+ final int selectedIndex = packet.selectedItemIndex(); -+ if (slotId >= 0 && slotId < this.player.containerMenu.slots.size() && selectedIndex != net.minecraft.world.item.component.BundleContents.NO_SELECTED_ITEM_INDEX) { -+ final net.minecraft.world.item.ItemStack item = this.player.containerMenu.slots.get(slotId).getItem(); -+ if (item.is(net.minecraft.tags.ItemTags.BUNDLES)) { -+ final net.minecraft.world.item.component.BundleContents contents = item.get(net.minecraft.core.component.DataComponents.BUNDLE_CONTENTS); -+ if (contents != null) { -+ if (!(new net.minecraft.world.item.component.BundleContents.Mutable(contents)).indexIsOutsideAllowedBounds(selectedIndex)) { -+ new io.papermc.paper.event.inventory.PlayerBundleItemSelectEvent( -+ this.player.containerMenu.getBukkitView(), -+ item.asBukkitMirror(), -+ slotId, -+ contents.getSelectedItem() != null ? contents.getSelectedItem().create().asBukkitMirror() : org.bukkit.inventory.ItemStack.empty(), -+ contents.items().get(selectedIndex).create().asBukkitMirror(), -+ contents.getSelectedItemIndex(), -+ selectedIndex -+ ).callEvent(); -+ } -+ } -+ } -+ } -+ } -+ // Paper end - PlayerBundleItemSelectEvent ++ CraftEventFactory.callPlayerBundleItemSelectEvent(this.player, packet.slotId(), packet.selectedItemIndex()); // Paper - PlayerBundleItemSelectEvent this.player.containerMenu.setSelectedBundleItemIndex(packet.slotId(), packet.selectedItemIndex()); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 7a42d1810f74..fb9a1a98dddc 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -1606,6 +1606,38 @@ public static BlockIgniteEvent callBlockIgniteEvent(Level level, BlockPos pos, I return event; } + public static void callPlayerBundleItemSelectEvent(final net.minecraft.world.entity.player.Player player, final int slotId, final int selectedIndex) { + if (player.isCreative() && player.containerMenu instanceof net.minecraft.world.inventory.InventoryMenu) { + // The packet's slotId will always be 0 if this packet is sent for a player selecting an item in a bundle in their creative inventory, + // making it unfit for firing an event. + return; + } + + if (slotId < 0 || slotId >= player.containerMenu.slots.size() || selectedIndex == net.minecraft.world.item.component.BundleContents.NO_SELECTED_ITEM_INDEX) { + return; + } + + final net.minecraft.world.item.ItemStack item = player.containerMenu.slots.get(slotId).getItem(); + if (!item.is(net.minecraft.tags.ItemTags.BUNDLES)) { + return; + } + + final net.minecraft.world.item.component.BundleContents contents = item.get(net.minecraft.core.component.DataComponents.BUNDLE_CONTENTS); + if (contents == null || (new net.minecraft.world.item.component.BundleContents.Mutable(contents)).indexIsOutsideAllowedBounds(selectedIndex)) { + return; + } + + new io.papermc.paper.event.inventory.PlayerBundleItemSelectEvent( + player.containerMenu.getBukkitView(), + item.asBukkitMirror(), + slotId, + contents.getSelectedItem() != null ? contents.getSelectedItem().create().asBukkitMirror() : org.bukkit.inventory.ItemStack.empty(), + contents.items().get(selectedIndex).create().asBukkitMirror(), + contents.getSelectedItemIndex(), + selectedIndex + ).callEvent(); + } + public static void handleInventoryCloseEvent(net.minecraft.world.entity.player.Player human, org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { InventoryCloseEvent event = new InventoryCloseEvent(human.containerMenu.getBukkitView(), reason); // Paper human.level().getCraftServer().getPluginManager().callEvent(event);