Repository: PaperMC/Paper
File: paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java (Java)

Code before:
            throw new IndexOutOfBoundsException("Slot " + slot + " out of range for inventory of size " + this.inventory.getSize());
        }

        return dropItemRaw(this.inventory.getItem(slot), amount, true);
    }

    @Override
    public @Nullable Item dropItemRandomly(final @NotNull EquipmentSlot slot, final int amount) {
        return dropItemRaw(this.inventory.getItem(slot), amount, true);
    }

    private @Nullable Item dropItemRaw(final ItemStack originalItemStack, final int amount, final boolean throwRandomly) {
        if (originalItemStack == null || originalItemStack.isEmpty() || amount <= 0) {
            return null;
        }

        final ItemStack clonedItemStack = originalItemStack.clone();
        final int droppedAmount = Math.min(clonedItemStack.getAmount(), amount);
        clonedItemStack.setAmount(droppedAmount);

        final ItemEntity droppedEntity = this.getHandle().drop(CraftItemStack.asNMSCopy(clonedItemStack), throwRandomly, true);
        if (droppedEntity == null) {
            return null;
        }

        originalItemStack.setAmount(originalItemStack.getAmount() - droppedAmount);
        return new CraftItem(this.server, droppedEntity);
    }

    @Override
    public float getExhaustion() {
        return this.getHandle().getFoodData().exhaustionLevel;
    }

    @Override
    public void setExhaustion(float value) {
        this.getHandle().getFoodData().exhaustionLevel = value;
    }

    @Override
    public float getSaturation() {
        return this.getHandle().getFoodData().saturationLevel;
    }

    @Override
    public void setSaturation(float value) {
        this.getHandle().getFoodData().saturationLevel = value;
    }

    @Override
    public int getFoodLevel() {

Review comment (suggestion):
I think this logic could be made nicer by operating on the underlying nms.ItemStack. All the Inventory#getItem should be returning mirrors (lets double check to be sure), but if they are, you can just get the underlying stack with `CraftItemStack#unwrap`. Then you can use the nms `.split` method which nicely handles all of the splitting/copying for you.

Code after:
        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;
    }

    @Override
    public void setExhaustion(float value) {
        this.getHandle().getFoodData().exhaustionLevel = value;
    }

    @Override
    public float getSaturation() {
        return this.getHandle().getFoodData().saturationLevel;
    }

    @Override
    public void setSaturation(float value) {
        this.getHandle().getFoodData().saturationLevel = value;
    }

    @Override
    public int getFoodLevel() {
        return this.getHandle().getFoodData().foodLevel;
    }

    @Override
    public void setFoodLevel(int value) {
        this.getHandle().getFoodData().foodLevel = value;
    }