diff --git a/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java b/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java
index 188c8e27d7..b1d1b759af 100644
--- a/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java
+++ b/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java
@@ -706,6 +706,107 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
*/
public boolean dropItem(boolean dropAll);
+ // Paper start - Extend HumanEntity#dropItem API
+ /**
+ * Makes the entity drop an item from their inventory.
+ *
+ * This method calls {@link HumanEntity#dropItem(int slot)}
+ * with the first {@link ItemStack} occurrence in the inventory
+ *
+ * @param itemStack The ItemStack to drop
+ * @return The dropped item, or null if the action was unsuccessful
+ */
+ public @Nullable Item dropItem(final @NotNull ItemStack itemStack);
+
+ /**
+ * Makes the entity drop an item from their inventory.
+ *
+ * This method calls {@link HumanEntity#dropItem(int slot, java.util.UUID thrower)}
+ * with the first {@link ItemStack} occurrence in the inventory
+ *
+ * @param itemStack The ItemStack to drop from their inventory
+ * @param thrower The {@link java.util.UUID} to set the resulting {@link Item}'s thrower to
+ * @return The dropped item, or null if the action was unsuccessful
+ */
+ public @Nullable Item dropItem(final @NotNull ItemStack itemStack, final @Nullable java.util.UUID thrower);
+
+ /**
+ * Makes the entity drop an item from their inventory based on the specified ItemStack.
+ *
+ * This method calls {@link HumanEntity#dropItem(int slot, java.util.UUID thrower, boolean throwRandomly)}
+ * with the first {@link ItemStack} occurrence in the inventory
+ *
+ * @param itemStack The ItemStack to drop
+ * @param thrower The {@link java.util.UUID} to set the resulting {@link Item}'s thrower to
+ * @param throwRandomly Whether the item should disperse randomly.
+ * This means that instead of the item being dropped where the player is currently looking,
+ * it instead throws it in any direction, similar to how items drop after a player's death.
+ * @return The dropped item, or null if the action was unsuccessful
+ */
+ public @Nullable Item dropItem(final @NotNull ItemStack itemStack, final @Nullable java.util.UUID thrower, final boolean throwRandomly);
+
+ /**
+ * Makes the entity drop an item from their inventory based on the slot.
+ *
+ * @param slot The slot to drop
+ * @return The dropped item, or null if the action was unsuccessful
+ * @throws IndexOutOfBoundsException If the slot is negative or bigger than the player's inventory
+ */
+ public @Nullable Item dropItem(final int slot);
+
+ /**
+ * Makes the entity drop an item from their inventory based on the slot.
+ *
+ * @param slot The slot to drop
+ * @param thrower The {@link java.util.UUID} to set the resulting {@link Item}'s thrower to
+ * @return The dropped item, or null if the action was unsuccessful
+ * @throws IndexOutOfBoundsException If the slot is negative or bigger than the player's inventory
+ */
+ public @Nullable Item dropItem(final int slot, final @Nullable java.util.UUID thrower);
+
+ /**
+ * Makes the entity drop an item from their inventory based on the slot.
+ *
+ * @param slot The slot to drop
+ * @param thrower The {@link java.util.UUID} to set the resulting {@link Item}'s thrower to
+ * @param throwRandomly Whether the item should disperse randomly.
+ * This means that instead of the item being dropped where the player is currently looking,
+ * it instead throws it in any direction, similar to how items drop after a player's death.
+ * @return The dropped item entity, or null if the action was unsuccessful
+ * @throws IndexOutOfBoundsException If the slot is negative or bigger than the player's inventory
+ */
+ public @Nullable Item dropItem(final int slot, final @Nullable java.util.UUID thrower, final boolean throwRandomly);
+
+ /**
+ * Makes the entity drop an item from their inventory based on the {@link org.bukkit.inventory.EquipmentSlot}
+ *
+ * @param slot The equipment slot to drop
+ * @return The dropped item entity, or null if the action was unsuccessful
+ */
+ public @Nullable Item dropItem(final @NotNull org.bukkit.inventory.EquipmentSlot slot);
+
+ /**
+ * Makes the entity drop an item from their inventory based on the {@link org.bukkit.inventory.EquipmentSlot}
+ *
+ * @param slot The equipment slot to drop
+ * @param thrower The {@link java.util.UUID} to set the resulting {@link Item}'s thrower to
+ * @return The dropped item entity, or null if the action was unsuccessful
+ */
+ public @Nullable Item dropItem(final @NotNull org.bukkit.inventory.EquipmentSlot slot, final @Nullable java.util.UUID thrower);
+
+ /**
+ * Makes the player drop an item from their inventory based on the equipment slot.
+ *
+ * @param slot The equipment slot to drop
+ * @param thrower The {@link java.util.UUID} to set the resulting {@link Item}'s thrower to
+ * @param throwRandomly Whether the item should disperse randomly.
+ * This means that instead of the item being dropped where the player is currently looking,
+ * it instead throws it in any direction, similar to how items drop after a player's death.
+ * @return The dropped item entity, or null if the action was unsuccessful
+ */
+ public @Nullable Item dropItem(final @NotNull org.bukkit.inventory.EquipmentSlot slot, final @Nullable java.util.UUID thrower, final boolean throwRandomly);
+ // Paper end
+
/**
* Gets the players current exhaustion level.
*
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java index e345cdbfab..3b225dc19e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java @@ -66,6 +66,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; // Paper - Extend HumanEntity#dropItem API +import org.jetbrains.annotations.Nullable; // Paper - Extend HumanEntity#dropItem API public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity { private CraftInventoryPlayer inventory; @@ -801,6 +803,86 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity { // Paper end - Fix HumanEntity#drop not updating the client inv } + // Paper start - Extend HumanEntity#dropItem API + @Override + public @Nullable org.bukkit.entity.Item dropItem(final @NotNull ItemStack itemStack) { + return this.dropItem(itemStack, null, false); + } + + @Override + public @Nullable org.bukkit.entity.Item dropItem(final @NotNull ItemStack itemStack, final @Nullable java.util.UUID thrower) { + return this.dropItem(itemStack, thrower, false); + } + + @Override + public @Nullable org.bukkit.entity.Item dropItem(final @NotNull ItemStack itemStack, final @Nullable java.util.UUID thrower, final boolean throwRandomly) { + final int slot = this.inventory.first(itemStack); + if (slot == -1) { + return null; + } + + return this.dropItem(slot, thrower, throwRandomly); + } + + @Override + public @Nullable org.bukkit.entity.Item dropItem(final int slot) { + return this.dropItem(slot, null, false); + } + + @Override + public @Nullable org.bukkit.entity.Item dropItem(final int slot, final @Nullable java.util.UUID thrower) { + return this.dropItem(slot, thrower, false); + } + + @Override + public @Nullable org.bukkit.entity.Item dropItem(final int slot, final @Nullable java.util.UUID thrower, final boolean throwRandomly) { + // Make sure the slot is in bounds + if (slot < 0 || this.inventory.getSize() <= slot) { + throw new IndexOutOfBoundsException("Slot " + slot + " out of range for inventory of size " + this.inventory.getSize()); + } + + final ItemStack stack = this.inventory.getItem(slot); + final org.bukkit.entity.Item itemEntity = dropItemRaw(stack, thrower, throwRandomly); + + this.inventory.setItem(slot, null); + return itemEntity; + } + + @Override + public @Nullable org.bukkit.entity.Item dropItem(final @NotNull org.bukkit.inventory.EquipmentSlot slot) { + return dropItem(slot, null, false); + } + + @Override + public @Nullable org.bukkit.entity.Item dropItem(final @NotNull org.bukkit.inventory.EquipmentSlot slot, final @Nullable java.util.UUID thrower) { + return dropItem(slot, thrower, false); + } + + @Override + public @Nullable org.bukkit.entity.Item dropItem(final @NotNull org.bukkit.inventory.EquipmentSlot slot, final @Nullable java.util.UUID thrower, final boolean throwRandomly) { + final ItemStack stack = this.inventory.getItem(slot); + final org.bukkit.entity.Item itemEntity = dropItemRaw(stack, thrower, throwRandomly); + + this.inventory.setItem(slot, null); + return itemEntity; + } + + private org.bukkit.entity.Item dropItemRaw(final ItemStack is, final @Nullable java.util.UUID thrower, final boolean throwRandomly) { + if (is == null || is.getType() == Material.AIR) { + return null; + } + + final net.minecraft.world.entity.item.ItemEntity droppedEntity = this.getHandle().drop(CraftItemStack.asNMSCopy(is), throwRandomly); + if (droppedEntity == null) { + return null; + } + + droppedEntity.thrower = thrower; + return (org.bukkit.entity.Item) droppedEntity.getBukkitEntity(); + } + // Paper end + + @Override public float getExhaustion() { return this.getHandle().getFoodData().exhaustionLevel;