Add PlayerPickBlockEvent and PlayerPickEntityEvent (#12425)

Extensions of the existing PlayerPickItemEvent that allow more fine grained access to relevant context, like the picked block or the entity.
This commit is contained in:
David 2025-05-02 22:14:27 +02:00 committed by GitHub
parent 1074237311
commit 825685f82f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 112 additions and 13 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -10,27 +10,44 @@ import org.jetbrains.annotations.Range;
import org.jspecify.annotations.NullMarked; 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 * Event that is fired when a player uses the pick item functionality
* appropriate item). After the handling of this event, the contents of the source and the target slot will be swapped * (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. * and the currently selected hotbar slot of the player will be set to the target slot.
*
* @see PlayerPickEntityEvent
* @see PlayerPickBlockEvent
*/ */
@NullMarked @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 static final HandlerList HANDLER_LIST = new HandlerList();
private final boolean includeData;
private int targetSlot; private int targetSlot;
private int sourceSlot; private int sourceSlot;
private boolean cancelled; private boolean cancelled;
@ApiStatus.Internal @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); super(player);
this.includeData = includeData;
this.targetSlot = targetSlot; this.targetSlot = targetSlot;
this.sourceSlot = sourceSlot; 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. * Returns the slot the item that is being picked goes into.
* *

View File

@ -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 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 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 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -555,7 +555,7 @@ public class ServerGamePacketListenerImpl @@ -555,7 +555,7 @@ public class ServerGamePacketListenerImpl
@ -88,7 +88,7 @@ index 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062
} }
@Override @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 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 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 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; boolean flag1 = this.player.verticalCollisionBelow;
this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5)); 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 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 // Paper start - prevent position desync
if (this.awaitingPositionFromClient != null) { if (this.awaitingPositionFromClient != null) {
return; // ... thanks Mojang for letting move calls teleport across dimensions. 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 // Paper start - Add fail move event
@ -124,7 +124,7 @@ index 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062
if (teleportBack) { if (teleportBack) {
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
toX, toY, toZ, toYaw, toPitch, false); toX, toY, toZ, toYaw, toPitch, false);
@@ -1638,7 +1681,7 @@ public class ServerGamePacketListenerImpl @@ -1640,7 +1683,7 @@ public class ServerGamePacketListenerImpl
private boolean updateAwaitingTeleport() { private boolean updateAwaitingTeleport() {
if (this.awaitingPositionFromClient != null) { if (this.awaitingPositionFromClient != null) {
@ -133,7 +133,7 @@ index 5e921c490814be31fc2843327c0e2cc76bda6620..f49a2c18ec20a7181951389066b7d062
this.awaitingTeleportTime = this.tickCount; this.awaitingTeleportTime = this.tickCount;
this.teleport( this.teleport(
this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.x,
@@ -1657,6 +1700,33 @@ public class ServerGamePacketListenerImpl @@ -1659,6 +1702,33 @@ public class ServerGamePacketListenerImpl
} }
} }

View File

@ -523,7 +523,7 @@
this.player.sendSystemMessage(Component.translatable("advMode.notAllowed")); this.player.sendSystemMessage(Component.translatable("advMode.notAllowed"));
} else { } else {
BaseCommandBlock commandBlock = packet.getCommandBlock(this.player.level()); BaseCommandBlock commandBlock = packet.getCommandBlock(this.player.level());
@@ -661,7 +_,7 @@ @@ -661,11 +_,11 @@
boolean flag = this.player.hasInfiniteMaterials() && packet.includeData(); boolean flag = this.player.hasInfiniteMaterials() && packet.includeData();
ItemStack cloneItemStack = blockState.getCloneItemStack(serverLevel, blockPos, flag); ItemStack cloneItemStack = blockState.getCloneItemStack(serverLevel, blockPos, flag);
if (!cloneItemStack.isEmpty()) { if (!cloneItemStack.isEmpty()) {
@ -532,7 +532,23 @@
addBlockDataToItem(blockState, serverLevel, blockPos, cloneItemStack); 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())) { if (stack.isItemEnabled(this.player.level().enabledFeatures())) {
Inventory inventory = this.player.getInventory(); Inventory inventory = this.player.getInventory();
int i = inventory.findSlotMatchingItem(stack); int i = inventory.findSlotMatchingItem(stack);
@ -540,7 +556,9 @@
+ final int sourceSlot = i; + final int sourceSlot = i;
+ final int targetSlot = Inventory.isHotbarSlot(sourceSlot) ? sourceSlot : inventory.getSuitableHotbarSlot(); + final int targetSlot = Inventory.isHotbarSlot(sourceSlot) ? sourceSlot : inventory.getSuitableHotbarSlot();
+ final org.bukkit.entity.Player bukkitPlayer = this.player.getBukkitEntity(); + 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()) { + if (!event.callEvent()) {
+ return; + return;
+ } + }