Extend HumanEntity#dropItem API (#11810)

This commit is contained in:
Strokkur24 2024-12-28 23:47:21 +01:00 committed by GitHub
parent 18da1ae4c0
commit 93a3df085c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 168 additions and 6 deletions

View file

@ -2,10 +2,12 @@ package org.bukkit.entity;
import java.util.Collection;
import java.util.Set;
import java.util.function.Consumer;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.InventoryView;
@ -14,6 +16,7 @@ import org.bukkit.inventory.MainHand;
import org.bukkit.inventory.Merchant;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.FireworkMeta;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -703,8 +706,115 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
*
* @param dropAll True to drop entire stack, false to drop 1 of the stack
* @return True if item was dropped successfully
* @apiNote You should instead use {@link #dropItem(EquipmentSlot, int)} or {@link #dropItem(EquipmentSlot)} with a {@link EquipmentSlot#HAND} parameter.
*/
public boolean dropItem(boolean dropAll);
@ApiStatus.Obsolete(since = "1.21.4")
boolean dropItem(boolean dropAll);
/**
* Makes the player drop all items from their inventory based on the inventory slot.
*
* @param slot the equipment slot to drop
* @return the dropped item entity, or null if the action was unsuccessful
*/
@Nullable
default Item dropItem(final int slot) {
return this.dropItem(slot, Integer.MAX_VALUE);
}
/**
* Makes the player drop an item from their inventory based on the inventory slot.
*
* @param slot the slot to drop
* @param amount the number of items to drop from this slot. Values below one always return null
* @return the dropped item entity, or null if the action was unsuccessful
* @throws IllegalArgumentException if the slot is negative or bigger than the player's inventory
*/
@Nullable
default Item dropItem(final int slot, final int amount) {
return this.dropItem(slot, amount, false, null);
}
/**
* Makes the player drop an item from their inventory based on the inventory slot.
*
* @param slot the slot to drop
* @param amount the number of items to drop from this slot. Values below one always return null
* @param throwRandomly controls the randomness of the dropped items velocity, where {@code true} mimics dropped
* items during a player's death, while {@code false} acts like a normal item drop.
* @param entityOperation the function to be run before adding the entity into the world
* @return the dropped item entity, or null if the action was unsuccessful
* @throws IllegalArgumentException if the slot is negative or bigger than the player's inventory
*/
@Nullable
Item dropItem(int slot, int amount, boolean throwRandomly, @Nullable Consumer<Item> entityOperation);
/**
* Makes the player drop all items from their inventory based on the equipment slot.
*
* @param slot the equipment slot to drop
* @return the dropped item entity, or null if the action was unsuccessful
*/
@Nullable
default Item dropItem(final @NotNull EquipmentSlot slot) {
return this.dropItem(slot, Integer.MAX_VALUE);
}
/**
* Makes the player drop an item from their inventory based on the equipment slot.
*
* @param slot the equipment slot to drop
* @param amount the amount of items to drop from this equipment slot. Values below one always return null
* @return the dropped item entity, or null if the action was unsuccessful
*/
@Nullable
default Item dropItem(final @NotNull EquipmentSlot slot, final int amount) {
return this.dropItem(slot, amount, false, null);
}
/**
* Makes the player drop an item from their inventory based on the equipment slot.
*
* @param slot the equipment slot to drop
* @param amount The amount of items to drop from this equipment slot. Values below one always return null
* @param throwRandomly controls the randomness of the dropped items velocity, where {@code true} mimics dropped
* items during a player's death, while {@code false} acts like a normal item drop.
* @param entityOperation the function to be run before adding the entity into the world
* @return the dropped item entity, or null if the action was unsuccessful
*/
@Nullable
Item dropItem(@NotNull EquipmentSlot slot, int amount, boolean throwRandomly, @Nullable Consumer<Item> entityOperation);
/**
* Makes the player drop any arbitrary {@link ItemStack}, independently of whether the player actually
* has that item in their inventory.
* <p>
* This method modifies neither the item nor the player's inventory.
* Item removal has to be handled by the method caller.
*
* @param itemStack the itemstack to drop
* @return the dropped item entity, or null if the action was unsuccessful
*/
@Nullable
default Item dropItem(final @NotNull ItemStack itemStack) {
return this.dropItem(itemStack, false, null);
}
/**
* Makes the player drop any arbitrary {@link ItemStack}, independently of whether the player actually
* has that item in their inventory.
* <p>
* This method modifies neither the item nor the player's inventory.
* Item removal has to be handled by the method caller.
*
* @param itemStack the itemstack to drop
* @param throwRandomly controls the randomness of the dropped items velocity, where {@code true} mimics dropped
* items during a player's death, while {@code false} acts like a normal item drop.
* @param entityOperation the function to be run before adding the entity into the world
* @return the dropped item entity, or null if the action was unsuccessful
*/
@Nullable
Item dropItem(final @NotNull ItemStack itemStack, boolean throwRandomly, @Nullable Consumer<Item> entityOperation);
/**
* Gets the players current exhaustion level.

View file

@ -1342,17 +1342,18 @@
}
public SectionPos getLastSectionPos() {
@@ -1930,21 +_,54 @@
@@ -1930,21 +_,55 @@
}
@Override
- public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem) {
+ public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem, boolean callEvent) { // CraftBukkit - SPIGOT-2942: Add boolean to call event
+ public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem, boolean callEvent, @Nullable java.util.function.Consumer<org.bukkit.entity.Item> entityOperation) { // Paper start - Extend HumanEntity#dropItem API
ItemEntity itemEntity = this.createItemStackToDrop(droppedItem, dropAround, traceItem);
if (itemEntity == null) {
return null;
} else {
+ // CraftBukkit start - fire PlayerDropItemEvent
+ if (entityOperation != null) entityOperation.accept((org.bukkit.entity.Item) itemEntity.getBukkitEntity());
+ if (callEvent) {
+ org.bukkit.entity.Player player = this.getBukkitEntity();
+ org.bukkit.entity.Item drop = (org.bukkit.entity.Item) itemEntity.getBukkitEntity();

View file

@ -112,16 +112,21 @@
this.removeEntitiesOnShoulder();
}
}
@@ -717,6 +_,13 @@
@@ -717,6 +_,18 @@
@Nullable
public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean includeThrowerName) {
+ // CraftBukkit start - SPIGOT-2942: Add boolean to call event
+ return this.drop(droppedItem, dropAround, includeThrowerName, true);
+ return this.drop(droppedItem, dropAround, includeThrowerName, true, null);
+ }
+
+ @Nullable
+ public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean includeThrowerName, boolean callEvent) {
+ return this.drop(droppedItem, dropAround, includeThrowerName, callEvent, null);
+ }
+
+ @Nullable
+ public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean includeThrowerName, boolean callEvent, @Nullable java.util.function.Consumer<org.bukkit.entity.Item> entityOperation) {
+ // CraftBukkit end
if (!droppedItem.isEmpty() && this.level().isClientSide) {
this.swing(InteractionHand.MAIN_HAND);

View file

@ -7,6 +7,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
@ -19,6 +20,7 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -48,13 +50,14 @@ import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.inventory.CraftMerchantCustom;
import org.bukkit.craftbukkit.inventory.CraftRecipe;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.entity.Firework;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Villager;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
@ -66,6 +69,8 @@ import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
private CraftInventoryPlayer inventory;
@ -801,6 +806,47 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
// Paper end - Fix HumanEntity#drop not updating the client inv
}
@Override
@Nullable
public Item dropItem(final int slot, final int amount, final boolean throwRandomly, final @Nullable Consumer<Item> entityOperation) {
Preconditions.checkArgument(slot >= 0 && slot < this.inventory.getSize(), "Slot %s is not a valid inventory slot.", slot);
return internalDropItemFromInventory(this.inventory.getItem(slot), amount, throwRandomly, entityOperation);
}
@Override
@Nullable
public Item dropItem(final @NotNull EquipmentSlot slot, final int amount, final boolean throwRandomly, final @Nullable Consumer<Item> entityOperation) {
return internalDropItemFromInventory(this.inventory.getItem(slot), amount, throwRandomly, entityOperation);
}
@Nullable
private Item internalDropItemFromInventory(final ItemStack originalItemStack, final int amount, final boolean throwRandomly, final @Nullable Consumer<Item> entityOperation) {
if (originalItemStack == null || originalItemStack.isEmpty() || amount <= 0) return null;
final net.minecraft.world.item.ItemStack nmsItemStack = CraftItemStack.unwrap(originalItemStack);
final net.minecraft.world.item.ItemStack dropContent = nmsItemStack.split(amount);
// This will return the itemstack back to its original amount in case events fail
final ItemEntity droppedEntity = this.getHandle().drop(dropContent, throwRandomly, true, true, entityOperation);
return droppedEntity == null ? null : (Item) droppedEntity.getBukkitEntity();
}
@Override
@Nullable
public Item dropItem(final @Nullable ItemStack itemStack, final boolean throwRandomly, final @Nullable Consumer<Item> entityOperation) {
// This method implementation differs from the previous dropItem implementations, as it does not source
// its itemstack from the players inventory. As such, we cannot reuse #internalDropItemFromInventory.
Preconditions.checkArgument(itemStack != null, "Cannot drop a null itemstack");
if (itemStack.isEmpty()) return null;
final net.minecraft.world.item.ItemStack nmsItemStack = CraftItemStack.asNMSCopy(itemStack);
// Do *not* call the event here, the item is not in the player inventory, they are not dropping it / do not need recovering logic (which would be a dupe).
final ItemEntity droppedEntity = this.getHandle().drop(nmsItemStack, throwRandomly, true, false, entityOperation);
return droppedEntity == null ? null : (Item) droppedEntity.getBukkitEntity();
}
@Override
public float getExhaustion() {
return this.getHandle().getFoodData().exhaustionLevel;