Set item on empty slot

Hi,
i try to set an item on an empty slot in a player inventory. I thought i’d just check if there is an item in this slot, if not get the SlotPos and set this item to this slot position.
But it doesn’t work.
My Code:

for (Inventory i : player.getInventory().slots()) {

            Optional<ItemStack> item = i.peek();

            if (!item.isPresent()) {
                SlotPos slotPos = i.getProperty(SlotPos.class, null).get();
                i.query(SlotPos.of(slotPos)).set(ItemStack.of(ItemTypes.COOKED_FISH, 1));
                break;
            }
        }

I always get this error:

java.util.NoSuchElementException: No value present
        at java.util.Optional.get(Unknown Source) ~[?:1.8.0_121]

I use the Version 5.1.0

Please post the full error stack trace.

So, the NSE Exception is being thrown by #getProperty. My understand is that the SlotPos property represents the position of the a Slot in a higher Inventory, and thus the Slot is not aware of it’s SlotPos because it can have multiple parents. If someone with a better understanding of the InventoryAPI can clarify what’s up, I’d be interested in knowing too.

However, you’re jumping through hoops to do something that’s relatively simply. When you’re iterating through Inventory#slots, your Inventory i is the Inventory representation of a given Slot (to get a better idea for why, it’d be good to review this PR even though it’s a bit outdated).

Effectively, you’re trying to get an InventoryProperty from your Inventory, then query it again for the same property - which if it works, isn’t getting you anywhere.

Now, let me ask you this - what’s wrong with using player.getInventory().offer(ItemStack itemStack)? The #offer method checks to see if there are empty ItemStacks or existing ones of the same type, and is generally sufficient for most use cases. If you don’t need to know exactly what slot it’s going to, why go through the hassle of iterating through all of them?

Anyways, I thought at first you were trying to set all empty slots, so I wrote this before the previous and I really don’t want to delete it. So, here’s a cleaner way you can do this if #offer doesn’t suit your needs.

// Iterates through all Slots within this inventory. While the resulting
// Inventory should be an instanceof Slot, we don't need to identify it
// as such and can use the provided Inventory methods.
for (Inventory i : player.getInventory().slots()) {
    // As per the API, #size returns the number of ItemStacks present
    // within this Inventory. As it is also a Slot, it must return 0
    // or 1, where 0 means there is no ItemStack in that slot (empty).
    if (i.size() == 0) {
        // It is possible to use either #offer or #set (see the API
        // javadocs for the difference). I would personally use #offer
        // in this case because it will not replace existing ItemStacks.
        i.offer(ItemStack.of(ItemTypes.COOKED_FISH, 1));
        // If you only wanted the first empty slot (poor me :/)
        break;
    }
}

I think the important thing to note is that your code should follow the principle of least astonishment. Avoid trying to do things like obtain a SlotPos property and re-query it. You should also address cases where no empty slot is found, as well as handle the InventoryTransactionResult if need be. Make sure your code is clear and easy to understand, and it’ll make your life much easier.

1 Like

Thank you again Simon,
#offer does exactly what I wanted. I don’t know why I always think so complicated.
Thank you very much for your explanation, it helps me a lot.

FYI:
Inventories only have a single parent (or none/itself)
The SlotPos is present on the slots if the parent is a Grid.
In his case it is not a Grid and the key was invalid:
i.getProperty(SlotPos.class, "slotpos") (lowercased class-name in most/all cases)
or
i.getInventoryProperty(SlotPos.class) (defaults to lowercased class-name)
would be the correct way to query for a property.

SlotPos is the SlotPosition in a GridInventory (so its only present in one)
SlotIndex.class is the SlotIndex in an Indexed Inventory (which all are at this time) and should always be present on slots.

@Simon_Flash

1 Like