diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickBlockEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickBlockEvent.java new file mode 100644 index 0000000000..40c3348f2a --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickBlockEvent.java @@ -0,0 +1,32 @@ +package io.papermc.paper.event.player; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Event that is fired when a player uses the pick item functionality on a block + * (middle-clicking a block to get the appropriate item). + * After the handling of this event, the contents of the source and the target slot will be swapped, + * and the currently selected hotbar slot of the player will be set to the target slot. + */ +@NullMarked +public class PlayerPickBlockEvent extends PlayerPickItemEvent { + private final Block block; + + @ApiStatus.Internal + public PlayerPickBlockEvent(final Player player, final Block block, final boolean includeData, final int targetSlot, final int sourceSlot) { + super(player, includeData, targetSlot, sourceSlot); + this.block = block; + } + + /** + * Retrieves the block associated with this event. + * + * @return the block involved in the event + */ + public Block getBlock() { + return this.block; + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickEntityEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickEntityEvent.java new file mode 100644 index 0000000000..a12b244cfb --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickEntityEvent.java @@ -0,0 +1,32 @@ +package io.papermc.paper.event.player; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Event that is fired when a player uses the pick item functionality on an entity + * (middle-clicking an entity to get the appropriate item). + * After the handling of this event, the contents of the source and the target slot will be swapped, + * and the currently selected hotbar slot of the player will be set to the target slot. + */ +@NullMarked +public class PlayerPickEntityEvent extends PlayerPickItemEvent { + private final Entity entity; + + @ApiStatus.Internal + public PlayerPickEntityEvent(final Player player, final Entity entity, final boolean includeData, final int targetSlot, final int sourceSlot) { + super(player, includeData, targetSlot, sourceSlot); + this.entity = entity; + } + + /** + * Retrieves the entity associated with this event. + * + * @return the entity involved in the event + */ + public Entity getEntity() { + return this.entity; + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickItemEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickItemEvent.java index 7de240399f..93c498d772 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickItemEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPickItemEvent.java @@ -10,27 +10,44 @@ import org.jetbrains.annotations.Range; import org.jspecify.annotations.NullMarked; /** - * Event that is fired when a player uses the pick item functionality (middle-clicking a block or entity to get the - * appropriate item). After the handling of this event, the contents of the source and the target slot will be swapped + * Event that is fired when a player uses the pick item functionality + * (middle-clicking a {@link PlayerPickBlockEvent block} + * or {@link PlayerPickEntityEvent entity} to get the appropriate item). + * After the handling of this event, the contents of the source and the target slot will be swapped, * and the currently selected hotbar slot of the player will be set to the target slot. + * + * @see PlayerPickEntityEvent + * @see PlayerPickBlockEvent */ @NullMarked -public class PlayerPickItemEvent extends PlayerEvent implements Cancellable { +public abstract class PlayerPickItemEvent extends PlayerEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); + private final boolean includeData; + private int targetSlot; private int sourceSlot; private boolean cancelled; @ApiStatus.Internal - public PlayerPickItemEvent(final Player player, final int targetSlot, final int sourceSlot) { + protected PlayerPickItemEvent(final Player player, final boolean includeData, final int targetSlot, final int sourceSlot) { super(player); + this.includeData = includeData; this.targetSlot = targetSlot; this.sourceSlot = sourceSlot; } + /** + * Checks whether the player wants block/entity data included. + * + * @return {@code true} if data is included, otherwise {@code false}. + */ + public boolean isIncludeData() { + return includeData; + } + /** * Returns the slot the item that is being picked goes into. * diff --git a/paper-server/patches/features/0029-Optimise-collision-checking-in-player-move-packet-ha.patch b/paper-server/patches/features/0029-Optimise-collision-checking-in-player-move-packet-ha.patch index 8dce7a440b..b00faac56b 100644 --- a/paper-server/patches/features/0029-Optimise-collision-checking-in-player-move-packet-ha.patch +++ b/paper-server/patches/features/0029-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 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062b48d43fa 100644 +index 0be741820fc7da2aac4f4aad85c4238ef49a0f57..337976c5c1ead87c36daa4e741b06e5a195b8302 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -555,7 +555,7 @@ public class ServerGamePacketListenerImpl @@ -88,7 +88,7 @@ index 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062 } @Override -@@ -1430,7 +1462,7 @@ public class ServerGamePacketListenerImpl +@@ -1432,7 +1464,7 @@ public class ServerGamePacketListenerImpl } } @@ -97,7 +97,7 @@ index 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062 d3 = d - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above d4 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above d5 = d2 - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above -@@ -1469,6 +1501,7 @@ public class ServerGamePacketListenerImpl +@@ -1471,6 +1503,7 @@ public class ServerGamePacketListenerImpl boolean flag1 = this.player.verticalCollisionBelow; this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5)); this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move @@ -105,7 +105,7 @@ index 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062 // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { return; // ... thanks Mojang for letting move calls teleport across dimensions. -@@ -1501,7 +1534,17 @@ public class ServerGamePacketListenerImpl +@@ -1503,7 +1536,17 @@ public class ServerGamePacketListenerImpl } // Paper start - Add fail move event @@ -124,7 +124,7 @@ index 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062 if (teleportBack) { io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, toX, toY, toZ, toYaw, toPitch, false); -@@ -1638,7 +1681,7 @@ public class ServerGamePacketListenerImpl +@@ -1640,7 +1683,7 @@ public class ServerGamePacketListenerImpl private boolean updateAwaitingTeleport() { if (this.awaitingPositionFromClient != null) { @@ -133,7 +133,7 @@ index 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062 this.awaitingTeleportTime = this.tickCount; this.teleport( this.awaitingPositionFromClient.x, -@@ -1657,6 +1700,33 @@ public class ServerGamePacketListenerImpl +@@ -1659,6 +1702,33 @@ public class ServerGamePacketListenerImpl } } 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 892544dc01..0b712a82ba 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 @@ -523,7 +523,7 @@ this.player.sendSystemMessage(Component.translatable("advMode.notAllowed")); } else { BaseCommandBlock commandBlock = packet.getCommandBlock(this.player.level()); -@@ -661,7 +_,7 @@ +@@ -661,11 +_,11 @@ boolean flag = this.player.hasInfiniteMaterials() && packet.includeData(); ItemStack cloneItemStack = blockState.getCloneItemStack(serverLevel, blockPos, flag); if (!cloneItemStack.isEmpty()) { @@ -532,7 +532,23 @@ addBlockDataToItem(blockState, serverLevel, blockPos, cloneItemStack); } -@@ -698,18 +_,29 @@ +- this.tryPickItem(cloneItemStack); ++ this.tryPickItem(cloneItemStack, blockPos, null, packet.includeData()); // Paper - Extend PlayerPickItemEvent API + } + } + } +@@ -689,27 +_,40 @@ + if (entityOrPart != null && this.player.canInteractWithEntity(entityOrPart, 3.0)) { + ItemStack pickResult = entityOrPart.getPickResult(); + if (pickResult != null && !pickResult.isEmpty()) { +- this.tryPickItem(pickResult); ++ this.tryPickItem(pickResult, null, entityOrPart, packet.includeData()); // Paper - Extend PlayerPickItemEvent API + } + } + } + +- private void tryPickItem(ItemStack stack) { ++ private void tryPickItem(ItemStack stack, @Nullable BlockPos blockPos, @Nullable Entity entity, boolean includeData) { // Paper - Extend PlayerPickItemEvent API if (stack.isItemEnabled(this.player.level().enabledFeatures())) { Inventory inventory = this.player.getInventory(); int i = inventory.findSlotMatchingItem(stack); @@ -540,7 +556,9 @@ + final int sourceSlot = i; + final int targetSlot = Inventory.isHotbarSlot(sourceSlot) ? sourceSlot : inventory.getSuitableHotbarSlot(); + final org.bukkit.entity.Player bukkitPlayer = this.player.getBukkitEntity(); -+ final io.papermc.paper.event.player.PlayerPickItemEvent event = new io.papermc.paper.event.player.PlayerPickItemEvent(bukkitPlayer, targetSlot, sourceSlot); ++ final io.papermc.paper.event.player.PlayerPickItemEvent event = entity != null ++ ? new io.papermc.paper.event.player.PlayerPickEntityEvent(bukkitPlayer, entity.getBukkitEntity(), includeData, targetSlot, sourceSlot) ++ : new io.papermc.paper.event.player.PlayerPickBlockEvent(bukkitPlayer, org.bukkit.craftbukkit.block.CraftBlock.at(this.player.level(), blockPos), includeData, targetSlot, sourceSlot); + if (!event.callEvent()) { + return; + }