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;