diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerUseBowWithoutProjectileEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerUseBowWithoutProjectileEvent.java new file mode 100644 index 0000000000..59e432878c --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerUseBowWithoutProjectileEvent.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 player tries to draw a bow or load a crossbow without having a suitable projectile in their inventory + */ +@NullMarked +public class PlayerUseBowWithoutProjectileEvent extends PlayerEvent { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final ItemStack item; + private ItemStack projectile; + + @ApiStatus.Internal + public PlayerUseBowWithoutProjectileEvent(final Player player, final ItemStack item) { + super(player); + this.item = item; + this.projectile = ItemStack.empty(); + } + + /** + * Gets the item which the player tries to use + * + * @return the item + */ + public ItemStack getItem() { + return item.clone(); + } + + /** + * Gets the projectile that should be used + * + * @return the projectile + */ + public ItemStack getProjectile() { + return projectile.clone(); + } + + /** + * Sets the projectile that should be used + *
+ * Note: setting this to {@link ItemStack#empty()} will prevent the player from using the bow/crossbow + * + * @param projectile the projectile + */ + public void setProjectile(ItemStack projectile) { + this.projectile = projectile.clone(); + } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } +} diff --git a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch index 4d20a5178a..85af582d44 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch @@ -669,3 +669,20 @@ for (int i = 0; i < this.inventory.getContainerSize(); i++) { ItemStack item = this.inventory.getItem(i); +@@ -2007,7 +_,15 @@ + } + } + +- return this.abilities.instabuild ? new ItemStack(Items.ARROW) : ItemStack.EMPTY; ++ // Paper start - Call PlayerUseBowWithoutProjectileEvent in Player#getProjectile ++ if (this.abilities.instabuild) { ++ return new ItemStack(Items.ARROW); ++ } else { ++ io.papermc.paper.event.player.PlayerUseBowWithoutProjectileEvent event = new io.papermc.paper.event.player.PlayerUseBowWithoutProjectileEvent((org.bukkit.entity.Player) getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack)); ++ event.callEvent(); ++ return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getProjectile()); ++ } ++ // Paper end - Call PlayerUseBowWithoutProjectileEvent in Player#getProjectile + } + } + }