Inventories
Player inventories, armor, chests and moving items around safely.
Players carry items, wear armor, store things in chests and click around in windows, and plugins need to see and change all of it. On this page you will learn which slot is which, how to read, count, add and remove items safely, and how to react when players click inside an inventory. At the end you build a /sell command and a /trash bin.
What is an inventory?
An inventory is a numbered row of slots, and each slot holds either nothing or one ItemStack. In Java it is an object of the type Inventory. Many things in Minecraft have one:
- A player (the thing you open with E), through
player.getInventory(). - A player's ender chest, through
player.getEnderChest(). - A container block such as a chest, barrel, hopper or furnace.
- A menu a plugin builds just for show. The page Building GUI menus is all about those.
Every Inventory offers the same methods, whatever it belongs to: getItem(slot), setItem(slot, item), addItem(item), contains(...) and so on. Learn them once and they work on all of these.
The player inventory: which slot is which
A player inventory has 41 slots you can see, and they are numbered in a fixed order:
- 0 to 8: the hotbar, left to right.
- 9 to 35: the main inventory, starting top left and reading like a book, row by row.
- 36 to 39: the armor slots: 36 boots, 37 leggings, 38 chestplate, 39 helmet.
- 40: the off hand.
Here is a player inventory with a few items placed, labeled with the slot each one occupies (hover an item to read its label):
Reading and writing one slot
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsHandAndSlots.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
29String describeSlot(Player player, int slot) {30 ItemStack item = player.getInventory().getItem(slot);31 if (item == null || item.isEmpty()) {32 return "slot " + slot + " is empty";33 }34 return "slot " + slot + " holds " + item.getAmount() + " x " + item.getType();35}- Looks into one slot. The result is the
ItemStackstored there. - An empty slot can come back as
null (Java's word for "no object at all") or as an empty stack, so always check for both. Checking only one is a classic source of crashes. - Only reached when there is a real item, so it is safe to ask for its amount and type.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsHandAndSlots.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
15void putDiamondInFirstHotbarSlot(Player player) {16 player.getInventory().setItem(0, ItemStack.of(Material.DIAMOND));17}- Puts the item in slot 0 and replaces whatever was there. Nothing is merged and nothing comes back, the old item is simply gone.
The item in the hand
The most common read in plugin code is "what is the player holding?":
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsHandAndSlots.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
10boolean isHoldingSomething(Player player) {11 ItemStack inHand = player.getInventory().getItemInMainHand();12 return !inHand.isEmpty();13}- The stack in the selected hotbar slot. With an empty hand you get an empty stack (air), never
null. - An empty hand is an empty stack, so
isEmpty()is the right question, not "is it null?".
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsHandAndSlots.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
19void clearTheSelectedSlot(Player player) {20 PlayerInventory inventory = player.getInventory();21 int selectedSlot = inventory.getHeldItemSlot();22 inventory.clear(selectedSlot);23}- Which hotbar slot, 0 to 8, the player has selected with the scroll wheel or number keys.
- Empties that single slot.
clear()with no number empties the entire inventory.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsHandAndSlots.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
25void giveTorchesInOffHand(Player player) {26 player.getInventory().setItemInOffHand(ItemStack.of(Material.TORCH, 16));27}- The off hand (slot 40) has its own methods:
getItemInOffHandandsetItemInOffHand.
Armor and equipment
You can reach armor through the slot numbers 36 to 39, but the named methods are much easier to read: getHelmet, getChestplate, getLeggings, getBoots and their set twins.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsArmorAndEquipment.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javayou are hereHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
12void giveDiamondHelmet(Player player) {13 player.getInventory().setHelmet(ItemStack.of(Material.DIAMOND_HELMET));14}- Puts a helmet on the player's head, replacing any helmet already there. The old one vanishes, so check first if that matters.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsArmorAndEquipment.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javayou are hereHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
16boolean isWearingHelmet(Player player) {17 ItemStack helmet = player.getInventory().getHelmet();18 return !helmet.isEmpty();19}- Never
null: with no helmet you get an empty stack. - True only when something is actually worn.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsArmorAndEquipment.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javayou are hereHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
21void takeOffAllArmor(Player player) {22 PlayerInventory inventory = player.getInventory();23 inventory.setHelmet(null);24 inventory.setChestplate(null);25 inventory.setLeggings(null);26 inventory.setBoots(null);27}- Passing
nullempties the slot. Whatever was worn is gone, not given back, so move it into the inventory first if you want to keep it.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsArmorAndEquipment.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javayou are hereHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
37int countArmorPieces(Player player) {38 int pieces = 0;39 for (ItemStack piece : player.getInventory().getArmorContents()) {40 if (piece != null && !piece.isEmpty()) {41 pieces++;42 }43 }44 return pieces;45}- An
array (a fixed-size numbered list) of the four armor slots, boots first. For players, empty slots can benull, so the loop checks for that. - A
for-each loop : it runs the body once for every item in the array. Loops are explained in the Java chapters.
There is a second way in: EntityEquipment. It works for players, and also for zombies, skeletons and every other mob that can wear things. It identifies slots by an EquipmentSlot name (HEAD, CHEST, LEGS, FEET, HAND, OFF_HAND), which is handy when a plugin wants one piece of code for any creature:
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsArmorAndEquipment.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javayou are hereHelper class
- Containers.javaHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
29void fullIronSet(Player player) {30 EntityEquipment equipment = player.getEquipment();31 equipment.setItem(EquipmentSlot.HEAD, ItemStack.of(Material.IRON_HELMET));32 equipment.setItem(EquipmentSlot.CHEST, ItemStack.of(Material.IRON_CHESTPLATE));33 equipment.setItem(EquipmentSlot.LEGS, ItemStack.of(Material.IRON_LEGGINGS));34 equipment.setItem(EquipmentSlot.FEET, ItemStack.of(Material.IRON_BOOTS));35}- The equipment view of this player. For a zombie you would write
zombie.getEquipment()and use the rest unchanged. - An
enum constant naming a body slot. ReadingHEADis clearer than remembering "slot 39".
Counting, checking and removing items
Plugins constantly ask questions like "does the player have 5 diamonds?" and "take 5 diamonds from the player". Inventories have methods for both. Learn what each one really compares, because the answers are surprising.
Counting by hand
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsCountingAndRemoving.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javayou are hereHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
11int countOf(Inventory inventory, Material material) {12 int total = 0;13 for (ItemStack stack : inventory.getStorageContents()) {14 if (stack != null && stack.getType() == material) {15 total += stack.getAmount();16 }17 }18 return total;19}- A running total, starting at zero.
- All slots where items are normally stored. For a player that is the hotbar and the main inventory, but not the armor or the off hand.
- Skips empty slots, then checks the material. The null check must come first, because asking a
nullfor its type crashes. - Adds this stack's amount to the total, so 3 stacks of 64 give 192.
This is the most flexible way, and it counts every diamond no matter its name or lore. Use it when you need to know "how many?".
The ready-made checks
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsCountingAndRemoving.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javayou are hereHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
21boolean hasAtLeastFiveDiamonds(Player player) {22 return player.getInventory().contains(Material.DIAMOND, 5);23}- True when the diamonds in all stacks add up to at least 5. It looks at the material only.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsCountingAndRemoving.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javayou are hereHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
25boolean hasAtLeastFivePlainDiamonds(Player player) {26 return player.getInventory().containsAtLeast(ItemStack.of(Material.DIAMOND), 5);27}- Adds up stacks that are similar to the item you give, meaning the same material and the same name, lore and enchantments. A renamed diamond does not count here.
Taking items away
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsCountingAndRemoving.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javayou are hereHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
29boolean takeFiveDiamonds(Player player) {30 Map<Integer, ItemStack> missing = player.getInventory().removeItem(ItemStack.of(Material.DIAMOND, 5));31 return missing.isEmpty();32}- Takes the given items out, across as many stacks as needed. It matches like
containsAtLeast: plain diamonds only. - Like
addItem, it returns what it could not do: the part that was not found. An empty map means it removed everything. - True when all five were taken.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsCountingAndRemoving.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javayou are hereHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
34void takeEveryDiamond(Player player) {35 player.getInventory().remove(Material.DIAMOND);36}- Removes every stack of that material in one go. There is no map of leftovers, it simply wipes them out.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsCountingAndRemoving.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javayou are hereHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
38void wipeInventory(Player player) {39 player.getInventory().clear();40}- Empties the whole inventory. For a player that includes armor and the off hand, so be careful with this one.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsCountingAndRemoving.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javaHelper class
- CountingAndRemoving.javayou are hereHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
42int firstFreeSlot(Player player) {43 return player.getInventory().firstEmpty();44}- The number of the first empty slot, or -1 when the inventory is full. Handy to check for room.
removeItem only looks in the storage slots. To also take from armor and the off hand, use removeItemAnySlot, which works exactly like removeItem but searches every slot.
Ender chests and container blocks
The ender chest belongs to the player and is the same inventory in every world, so you reach it from the player. It is an ordinary Inventory, so everything above works on it:
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsContainers.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javayou are hereHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
13void openEnderChest(Player player) {14 player.openInventory(player.getEnderChest());15}- The player's ender chest inventory.
- Opens an inventory as a window on the player's screen. Any inventory works, even one you made yourself.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsContainers.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javayou are hereHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
17void putStarterBreadInEnderChest(Player player) {18 player.getEnderChest().addItem(ItemStack.of(Material.BREAD, 8));19}- The same
addItemas before, only on a different inventory. Even if the player is not looking at it.
Chests, barrels, hoppers, furnaces and shulker boxes are blocks, so you reach their inventory through the block's state. A block state is a snapshot of the block that knows its special data. You ask whether it is a container with instanceof:
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsContainers.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javayou are hereHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
21Inventory inventoryOf(Block block) {22 if (block.getState() instanceof Container container) {23 return container.getInventory();24 }25 return null;26}- Captures the block's current state. For a chest, that state knows about the inventory.
- Asks "is this state some kind of container?" and, if it is, gives it to you under the name
container. This is calledpattern matching . Inheritance and interfaces explainsinstanceof. - The live inventory. Changes you make to it appear in the real chest.
- When the block is not a container, there is no inventory. The caller must handle
null.
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsContainers.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javayou are hereHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
28int emptySlotsInChest(Block block) {29 if (!(block.getState() instanceof Chest chest)) {30 return 0;31 }32 int empty = 0;33 for (ItemStack stack : chest.getInventory().getContents()) {34 if (stack == null || stack.isEmpty()) {35 empty++;36 }37 }38 return empty;39}- Stops early for anything that is not a chest. The "!" turns the check around, so the rest of the method only runs for chests.
- All slots of the chest, including empty ones (which may be
null).
A double chest is one big inventory made of two chest blocks. chest.getInventory() gives you the whole thing, and chest.getBlockInventory() gives you only the half that belongs to this block:
Where this file livesinventories-snippetssrcmainjavacomexampleinventoriessnippetsContainers.java
The package com.example.inventoriessnippets is the folder path com/example/inventoriessnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-snippets/
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
- ArmorAndEquipment.javaHelper class
- Containers.javayou are hereHelper class
- CountingAndRemoving.javaHelper class
- HandAndSlots.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriessnippets/Package com.example.inventoriessnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
41boolean isDoubleChest(Block block) {42 if (!(block.getState() instanceof Chest chest)) {43 return false;44 }45 return chest.getInventory().getSize() > chest.getBlockInventory().getSize();46}- 27 for one chest half. If the whole inventory is bigger, there is a second half next to it.
Blocks themselves, such as how to find the block a player is looking at, are covered in Blocks and block data.
Inventory events
Plugins hear about everything players do with inventories through events. These four matter most:
| Event | Fires when | Can you cancel it? |
|---|---|---|
InventoryClickEvent | A player clicks a slot in any open inventory, including their own. | Yes |
InventoryDragEvent | A player holds a click and drags items across several slots. | Yes |
InventoryOpenEvent | A player opens a chest, furnace, menu or other window. | Yes |
InventoryCloseEvent | A player closes a window, or something closes it for them. | No |
Canceling a click event stops the click from doing anything, which is how plugins stop players from taking items out of a menu. That idea is the base of GUI menus.
Two inventories in one window
When a player opens a chest, the window shows two inventories at once: the chest on top and the player's own inventory below. Each has its own slot numbers, which is why a click event reports two slot numbers:
event.getSlot()is the number inside the clicked inventory, ready forgetItem(slot). The same number can appear in both inventories.event.getRawSlot()is unique across the whole window.event.getClickedInventory()says which of the two was clicked, or isnullwhen the click was outside the window.event.getView().getTopInventory()andgetBottomInventory()give the two inventories to compare against.
What was clicked, and how
The event also says what the player was doing. Use getClick() for the kind of click:
ClickType | What the player did |
|---|---|
LEFT, RIGHT | Normal clicks. Left picks up or places the stack, right picks up half or places one. |
SHIFT_LEFT, SHIFT_RIGHT | Clicked while holding Shift: the item jumps to the other inventory. |
NUMBER_KEY, SWAP_OFFHAND | Pressed a number key 1 to 9 while hovering a slot, swapping with that hotbar slot, or pressed F to swap with the off hand. |
DROP, CONTROL_DROP | Pressed Q (or Ctrl+Q) over a slot to throw the item out. |
DOUBLE_CLICK | Double-clicked to gather matching items onto the cursor. |
MIDDLE, CREATIVE | Middle clicks and creative-mode clicks. |
event.getCurrentItem() is the item in the clicked slot, which can be null. event.getCursor() is the item the player is carrying on their mouse pointer.
Watch the events happen
The best way to understand them is to see them. This small plugin tells you everything that happens in the action bar and in chat while you play with inventories:
Where this file livesinventories-inspectorsrcmainjavacomexampleinventoriesinspectorInventoryInspector.java
The package com.example.inventoriesinspector is the folder path com/example/inventoriesinspector inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-inspector/
- src/main/
- java/com/example/inventoriesinspector/Package com.example.inventoriesinspector
- InventoriesInspectorPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- InventoryInspector.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesinspector/Package com.example.inventoriesinspector
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
15@EventHandler16public void onClick(InventoryClickEvent event) {17 if (!(event.getWhoClicked() instanceof Player player)) {18 return;19 }20 String where = "outside";21 if (event.getClickedInventory() != null) {22 where = event.getClickedInventory() == event.getView().getTopInventory() ? "top" : "bottom";23 }24 ItemStack clicked = event.getCurrentItem();25 String item = clicked == null || clicked.isEmpty() ? "nothing" : clicked.getType().name();26 player.sendRichMessage("<gray>Click <white><click></white> in the <white><where></white> inventory, slot <white><slot></white> (raw <white><raw></white>) on <white><item>",27 Placeholder.unparsed("click", event.getClick().name()),28 Placeholder.unparsed("where", where),29 Placeholder.unparsed("slot", String.valueOf(event.getSlot())),30 Placeholder.unparsed("raw", String.valueOf(event.getRawSlot())),31 Placeholder.unparsed("item", item));32}- Clickers are
HumanEntityobjects; this check keeps only players, which is what the messages are sent to. - Starts with "outside". It only changes if the click was inside a window.
- A click outside the window gives
null, so we must check before using it. - Compares the clicked inventory with the top one. If it is not the top one, it is the bottom one.
- An empty slot can be
nullor empty, so check both. - The click type as text, such as
SHIFT_LEFT. - The window-wide slot number.
Where this file livesinventories-inspectorsrcmainjavacomexampleinventoriesinspectorInventoryInspector.java
The package com.example.inventoriesinspector is the folder path com/example/inventoriesinspector inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-inspector/
- src/main/
- java/com/example/inventoriesinspector/Package com.example.inventoriesinspector
- InventoriesInspectorPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- InventoryInspector.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesinspector/Package com.example.inventoriesinspector
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
46@EventHandler47public void onClose(InventoryCloseEvent event) {48 event.getPlayer().sendRichMessage("<red>Closed it because: <reason>",49 Placeholder.unparsed("reason", event.getReason().name()));50}- Why the window closed: the player pressed Esc (
PLAYER), another window replaced it (OPEN_NEW), they teleported, died, disconnected, or a plugin closed it.
Open a chest, shift-click a stack into it, press a number key over a slot, and read what the plugin tells you. The file also has small onDrag and onOpen handlers that work the same way, so you will see lines for those too:
Click LEFT in the top inventory, slot 4 (raw 4) on nothing
Click SHIFT_LEFT in the bottom inventory, slot 2 (raw 56) on IRON_INGOT
Click NUMBER_KEY in the top inventory, slot 11 (raw 11) on BREAD
Closed it because: PLAYER
Notice that the shift-click shows the bottom inventory's slot 2 with raw slot 56. Slot 2 of the hotbar is 54 plus 2 in the window numbering, because the raw order is chest (0 to 26 for a one-chest window), then main inventory (27 to 53), then hotbar (54 to 62).
Example plugin: /sell and /trash
Time to build something. /sell turns the item in your hand into experience. /sell all sells every plain item of that kind in your inventory. /trash opens a bin that destroys whatever is in it when you close it.
- inventories-demo/
- src/
- main/
- java/
- com/example/inventoriesdemo/
- InventoriesDemoPlugin.javaRegisters both commands and the trash listener
- SellPrices.javaWhich items can be sold and for how much experience
- SellCommand.javaThe /sell command
- TrashHolder.javaMarks an inventory as "the trash bin"
- TrashCommand.javaThe /trash command: opens the bin
- TrashListener.javaEmpties the bin when it is closed
- com/example/inventoriesdemo/
- resources/
- plugin.ymlPlugin name, main class and permissions
- java/
- main/
- src/
First the price list. SellPrices holds a map from material to experience points, and two helpers that use it:
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoSellPrices.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javaCommand (BasicCommand)
- SellPrices.javayou are hereHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
20static boolean isSellable(ItemStack item) {21 return XP_PER_ITEM.containsKey(item.getType()) && item.isSimilar(ItemStack.of(item.getType()));22}- The material must be on the price list.
- The stack must also be a plain item with no custom name or lore. Otherwise players could sell your special quest items.
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoSellPrices.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javaCommand (BasicCommand)
- SellPrices.javayou are hereHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
24static int xpFor(ItemStack item) {25 return XP_PER_ITEM.getOrDefault(item.getType(), 0) * item.getAmount();26}- Looks up the price, using 0 when the material is not on the list.
- The whole stack is paid: price per item times amount.
The command sells either the held stack or, with the word all, everything of that kind:
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoSellCommand.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javayou are hereCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
12@Override13public void execute(CommandSourceStack source, String[] args) {14 if (!(source.getExecutor() instanceof Player player)) {15 source.getSender().sendRichMessage("<red>Only players can sell items.");16 return;17 }18 if (args.length > 0 && args[0].equalsIgnoreCase("all")) {19 sellAllOfTheHeldKind(player);20 } else {21 sellHeldStack(player);22 }23}- Only players have a hand and an inventory.
- The first word after
/sell.args.length > 0comes first, to avoid reading a word that is not there.
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoSellCommand.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javayou are hereCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
25private void sellHeldStack(Player player) {26 ItemStack held = player.getInventory().getItemInMainHand();27 if (held.isEmpty() || !SellPrices.isSellable(held)) {28 tellNotSellable(player);29 return;30 }31 int xp = SellPrices.xpFor(held);32 int amount = held.getAmount();33 player.getInventory().setItemInMainHand(ItemStack.empty());34 pay(player, amount, xp);35}- Refuse when the hand is empty or the item is not on the list. The empty check comes first, because it is the cheapest.
- Work out the payment before removing the item, because the live stack changes when you clear the hand.
- Takes the whole stack out of the hand.
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoSellCommand.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javayou are hereCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
37private void sellAllOfTheHeldKind(Player player) {38 ItemStack held = player.getInventory().getItemInMainHand();39 if (held.isEmpty() || !SellPrices.isSellable(held)) {40 tellNotSellable(player);41 return;42 }43 ItemStack kind = ItemStack.of(held.getType());44 Inventory inventory = player.getInventory();45 int amount = 0;46 int xp = 0;47 ItemStack[] contents = inventory.getStorageContents();48 for (int slot = 0; slot < contents.length; slot++) {49 ItemStack stack = contents[slot];50 if (stack != null && stack.isSimilar(kind)) {51 amount += stack.getAmount();52 xp += SellPrices.xpFor(stack);53 inventory.clear(slot);54 }55 }56 pay(player, amount, xp);57}- Remembers a plain item of the held kind. Without this copy, clearing the held slot would change
heldin the middle of the loop, because for players it is a live mirror. - A snapshot of all the storage slots, with the slot number being the position in the array.
- Skips empty slots, and sells only plain items of the same kind.
- Empties that single slot. Using the slot number from the loop makes sure only matching stacks disappear.
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoSellCommand.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javayou are hereCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
59private void pay(Player player, int amount, int xp) {60 player.giveExp(xp);61 player.sendRichMessage("<green>Sold <amount> item(s) for <xp> xp.",62 Placeholder.unparsed("amount", String.valueOf(amount)),63 Placeholder.unparsed("xp", String.valueOf(xp)));64}- Adds experience points to the player, like picking up orbs.
The trash bin needs a way to recognize its inventory when it closes. The player's own inventory and every chest also send close events, and you must not wipe those! Bukkit lets an inventory have a holder, an object that owns it. We make a tiny holder class just to act as a name tag:
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoTrashHolder.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javaCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javayou are hereMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.inventoriesdemo;2 3import net.kyori.adventure.text.Component;4import net.kyori.adventure.text.format.NamedTextColor;5import org.bukkit.Bukkit;6import org.bukkit.inventory.Inventory;7import org.bukkit.inventory.InventoryHolder;8 9final class TrashHolder implements InventoryHolder {10 11 private final Inventory inventory;12 13 TrashHolder() {14 this.inventory = Bukkit.createInventory(this, 27, Component.text("Trash: closing destroys everything", NamedTextColor.DARK_RED));15 }16 17 @Override18 public Inventory getInventory() {19 return inventory;20 }21}- Promises that this class can own an inventory. It has one required method,
getInventory. - Creates a brand-new inventory with 27 slots (3 rows). The first argument,
this, makes this object the holder. The text is the window title. - The title is a component, like every text in modern Paper.
- Returns the inventory this holder owns.
createInventoryalready linked the two, and this method lets other code go from holder to inventory.
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoTrashCommand.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javaCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javayou are hereCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
9@Override10public void execute(CommandSourceStack source, String[] args) {11 if (source.getExecutor() instanceof Player player) {12 player.openInventory(new TrashHolder().getInventory());13 } else {14 source.getSender().sendRichMessage("<red>Only players have a trash can.");15 }16}- Every time someone types
/trash, we create a fresh bin so two players never share one.
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoTrashListener.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javaCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
13@EventHandler14public void onClose(InventoryCloseEvent event) {15 Inventory closed = event.getInventory();16 if (!(closed.getHolder(false) instanceof TrashHolder)) {17 return;18 }19 int destroyed = 0;20 for (ItemStack stack : closed.getContents()) {21 if (stack != null) {22 destroyed += stack.getAmount();23 }24 }25 closed.clear();26 if (destroyed > 0 && event.getPlayer() instanceof Player player) {27 player.sendRichMessage("<gray>Destroyed <count> item(s).",28 Placeholder.unparsed("count", String.valueOf(destroyed)));29 }30}- Is the inventory that just closed a trash bin? Only then do we continue. The
falsemeans "do not copy block data just to look". - Empty slots in the contents array can be
null. - This is the "destroy": everything in the bin is erased.
- The event only promises a
HumanEntity, so we check that it is a player before messaging.
Where this file livesinventories-demosrcmainjavacomexampleinventoriesdemoInventoriesDemoPlugin.java
The package com.example.inventoriesdemo is the folder path com/example/inventoriesdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javayou are hereMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javaCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.inventoriesdemo;2 3import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;4import org.bukkit.plugin.java.JavaPlugin;5 6public final class InventoriesDemoPlugin extends JavaPlugin {7 8 @Override9 public void onEnable() {10 getServer().getPluginManager().registerEvents(new TrashListener(), this);11 getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {12 event.registrar().register("sell", "Sell the item in your hand for experience", new SellCommand());13 event.registrar().register("trash", "Open a bin that destroys what you put in it", new TrashCommand());14 });15 }16}- The listener must be registered, or the bin never empties.
- Registers
/sell. - Registers
/trash.
Where this file livesinventories-demosrcmainresourcesplugin.yml
Files in src/main/resources are copied into the jar exactly as they are. Paper looks for plugin.yml at the top of the jar.
- inventories-demo/
- src/main/
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- InventoriesDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- SellCommand.javaCommand (BasicCommand)
- SellPrices.javaHelper class
- TrashCommand.javaCommand (BasicCommand)
- TrashHolder.javaMenu (inventory holder)
- TrashListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlyou are hereTells Paper the plugin's name, version and main class
- java/com/example/inventoriesdemo/Package com.example.inventoriesdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1name: InventoriesDemo2version: '1.0.0'3main: com.example.inventoriesdemo.InventoriesDemoPlugin4api-version: '26.3'5description: A /sell command that trades held items for experience and a /trash bin that destroys items.6permissions:7 inventoriesdemo.sell:8 description: Lets a player use /sell.9 default: true10 inventoriesdemo.trash:11 description: Lets a player use /trash.12 default: true- One permission per command, both allowed for everybody by default.
Hold five diamonds and type /sell:
Type /sell while holding a sword, and you learn what is for sale:
And /trash opens a window like this one. Shift-click anything from your inventory into it, then close it with Esc:
You can download the plugin as a Gradle project, run it, or try the inspector plugin next to it.
Mistakes to avoid
- Treating an empty slot as always
null. Slots can benullor an empty stack. Checkitem == null || item.isEmpty(). - Forgetting that
addItemandremoveItemreturn what they could not do. Ignoring the map loses items or lets players buy things they cannot afford. - Removing before checking.
removeItemremoves as much as it can and stops, so a player with 3 of the 5 items you wanted already lost 3. Check first withcontainsAtLeast. - Comparing slots with the wrong numbers. In a click event,
getSlot()belongs to the clicked inventory andgetRawSlot()to the whole window. CheckgetClickedInventory()first. - Counting only
getType()when you meant plain items. Named or enchanted items of that material also match. UseisSimilaragainst a plain item when only plain ones count. - Mixing up
contains(ItemStack)andcontainsAtLeast. The first one needs one stack with exactly that amount.
Pay with iron
Write boolean payWithIron(Player player, int price). It should take exactly price iron ingots from the player and return true. If the player does not have enough, it should tell them and return false, and it must not take anything.
Hint 1
Ask first with containsAtLeast(ItemStack.of(Material.IRON_INGOT), price). Only remove the items when that is true.
Hint 2
The removal is player.getInventory().removeItem(ItemStack.of(Material.IRON_INGOT, price)).
Show the solution
The check goes first, so a player with too few ingots loses nothing. Only when the check passes does the method remove the ingots.
Where this file livesinventories-exercisesrcmainjavacomexampleinventoriesexercisePurchase.java
The package com.example.inventoriesexercise is the folder path com/example/inventoriesexercise inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-exercise/
- src/main/java/com/example/inventoriesexercise/Package com.example.inventoriesexercise
- HelmetIfEmpty.javaHelper class
- Purchase.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriesexercise/Package com.example.inventoriesexercise
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
9boolean payWithIron(Player player, int price) {10 ItemStack payment = ItemStack.of(Material.IRON_INGOT);11 if (!player.getInventory().containsAtLeast(payment, price)) {12 player.sendRichMessage("<red>You need " + price + " iron ingots.");13 return false;14 }15 player.getInventory().removeItem(ItemStack.of(Material.IRON_INGOT, price));16 return true;17}- Adds up all plain iron ingots in the inventory.
- Leaves early without touching the inventory.
- Takes the ingots only after the check passed.
Pumpkin helmet, politely
Write boolean equipPumpkin(Player player). It puts a carved pumpkin on the player's head, but only when the helmet slot is empty. It returns true when it equipped the pumpkin and false when the player was already wearing something.
Hint
Remember that getHelmet() never returns null: an empty slot is an empty stack.
Show the solution
Read the current helmet, stop early if it is not empty, and set the pumpkin otherwise.
Where this file livesinventories-exercisesrcmainjavacomexampleinventoriesexerciseHelmetIfEmpty.java
The package com.example.inventoriesexercise is the folder path com/example/inventoriesexercise inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- inventories-exercise/
- src/main/java/com/example/inventoriesexercise/Package com.example.inventoriesexercise
- HelmetIfEmpty.javayou are hereHelper class
- Purchase.javaHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/inventoriesexercise/Package com.example.inventoriesexercise
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
9boolean equipPumpkin(Player player) {10 ItemStack current = player.getInventory().getHelmet();11 if (!current.isEmpty()) {12 return false;13 }14 player.getInventory().setHelmet(ItemStack.of(Material.CARVED_PUMPKIN));15 return true;16}- An empty stack if nothing is worn.
- Something is worn, so leave it alone and report that nothing happened.
- The pumpkin you can wear.
Recap
- An
Inventoryis numbered slots starting at 0. A player's hotbar is 0 to 8, the main inventory 9 to 35, armor 36 to 39 and the off hand is 40. - An empty slot may be
nullor an empty stack. Check both.getItemInMainHand()never returnsnull. getHelmetand friends, orEntityEquipmentwithEquipmentSlot, reach the armor and the hands of players and mobs.contains(Material, amount)andcontainsAtLeastcount;removeItemremoves and returns what it could not remove;remove(Material),clear(slot)andclear()wipe things out.- Reach a chest's inventory through its block state, and a player's ender chest with
getEnderChest(). - Inventory events: click, drag, open and close. A window holds two inventories, so
getSlot()andgetRawSlot()differ. - An
InventoryHolderlets you recognize an inventory you made yourself.
Quick quiz
Which slot number is the helmet in a
PlayerInventory?Armor is 36 (boots), 37 (leggings), 38 (chestplate) and 39 (helmet). Slot 40 is the off hand and slot 0 is the first hotbar slot. You can also skip the numbers and usegetHelmet().You call
player.getInventory().getItem(5)and get backnull. What does that most likely mean?Empty slots can be returned asnull, which is why you checkitem == null || item.isEmpty()before using an item.A player has 3 diamonds. You call
removeItem(ItemStack.of(Material.DIAMOND, 5)). What happens?removeItemremoves as much as it can. To make a purchase all or nothing, checkcontainsAtLeastfirst.In an
InventoryClickEvent, what doesgetClickedInventory()tell you?The slot number comes fromgetSlot()orgetRawSlot(), and the player fromgetWhoClicked().Why does the /trash example use a
TrashHolderclass?Every inventory sends close events. Checking the holder makes sure only the bin is cleared. The title is passed tocreateInventoryseparately.
Next steps
- Building GUI menus: use inventories as clickable menus that players cannot steal from.
- Saving data on things: mark items so you can recognize them even after renames.
- Blocks and block data: find the chests your plugin should work with.