forked from PaperMC/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add InventoryHolder developer docs (PaperMC#132)
- Loading branch information
1 parent
805c1d1
commit 06267a1
Showing
2 changed files
with
163 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -99,6 +99,7 @@ const paper: SidebarsConfig = { | |
], | ||
}, | ||
"dev/api/pdc", | ||
"dev/api/custom-inventory-holder", | ||
], | ||
}, | ||
], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
--- | ||
slug: /dev/custom-inventory-holder | ||
--- | ||
|
||
# Custom InventoryHolder | ||
|
||
Custom InventoryHolders can be used to identify your plugin's inventories in events. | ||
|
||
## Why use an InventoryHolder? | ||
|
||
Custom InventoryHolders simplify the steps you need to do to make sure an inventory was created by your plugin. | ||
|
||
:::info | ||
|
||
Using inventory names for identification is unreliable as other plugins can create inventories with names exactly as yours | ||
and with components you need to make sure the name is exactly the same or serialize it to other formats. | ||
|
||
Custom InventoryHolders have no such downsides and by using them you're guaranteed to have methods available to handle your inventory. | ||
|
||
::: | ||
|
||
## Creating a custom InventoryHolder | ||
|
||
InventoryHolder is an interface that we must implement. | ||
We can do this the following way: create a new class that will create our `Inventory` in the constructor. | ||
|
||
:::info | ||
|
||
The constructor takes your main plugin class as an argument in order to create the `Inventory`. | ||
If you wish, you can use the static method `Bukkit.createInventory(InventoryHolder, int)` instead and remove the argument. | ||
|
||
::: | ||
|
||
```java | ||
public class MyInventory implements InventoryHolder { | ||
|
||
private final Inventory inventory; | ||
|
||
public MyInventory(MyPlugin plugin) { | ||
// Create an Inventory with 9 slots, `this` here is our InventoryHolder. | ||
this.inventory = plugin.getServer().createInventory(this, 9); | ||
} | ||
|
||
@Override | ||
public Inventory getInventory() { | ||
return this.inventory; | ||
} | ||
|
||
} | ||
``` | ||
|
||
## Opening the inventory | ||
|
||
To open the inventory, first we have to instantiate our `MyInventory` class and then open the inventory for the player. | ||
You can do that wherever you need. | ||
|
||
:::note | ||
|
||
We pass an instance of our plugin's main class as it's required by the constructor. If you've used the static method and removed the constructor | ||
argument you don't have to pass it here. | ||
|
||
::: | ||
|
||
```java | ||
Player player; // Assume we have a Player instance. | ||
// This can be a command, another event or anywhere else you have a Player. | ||
|
||
MyInventory myInventory = new MyInventory(myPlugin); | ||
player.openInventory(myInventory.getInventory()); | ||
``` | ||
|
||
## Listening to an event | ||
|
||
Once we have the inventory open, we can listen to any inventory events we like and check if the `Inventory#getHolder()` | ||
returns an instance of our `MyInventory`. | ||
|
||
```java | ||
@EventHandler | ||
public void onInventoryClick(InventoryClickEvent event) { | ||
Inventory inventory = event.getInventory(); | ||
// Check if the holder is our MyInventory, | ||
// if yes, use instanceof pattern matching to store it in a variable immediately. | ||
if (!(inventory.getHolder() instanceof MyInventory myInventory)) { | ||
// It's not our inventory, ignore it. | ||
return; | ||
} | ||
|
||
// Do what we need in the event. | ||
} | ||
``` | ||
|
||
## Storing data on the custom InventoryHolder | ||
|
||
You can store extra data for your inventories on the custom InventoryHolder by adding fields and methods to your class. | ||
|
||
Let's make an inventory that counts the amount of times we clicked a stone inside it. | ||
First let's modify our `MyInventory` class a little: | ||
|
||
```java | ||
public class MyInventory implements InventoryHolder { | ||
|
||
private final Inventory inventory; | ||
|
||
private int clicks = 0; // Store the amount of clicks. | ||
|
||
public MyInventory(MyPlugin plugin) { | ||
this.inventory = plugin.getServer().createInventory(this, 9); | ||
|
||
// Set the stone that we're going to be clicking. | ||
this.inventory.setItem(0, new ItemStack(Material.STONE)); | ||
} | ||
|
||
// A method we will call in the listener whenever the player clicks the stone. | ||
public void addClick() { | ||
this.clicks++; | ||
this.updateCounter(); | ||
} | ||
|
||
// A method that will update the counter item. | ||
private void updateCounter() { | ||
this.inventory.setItem(8, new ItemStack(Material.BEDROCK, this.clicks)); | ||
} | ||
|
||
@Override | ||
public Inventory getInventory() { | ||
return this.inventory; | ||
} | ||
|
||
} | ||
``` | ||
|
||
Now, we can modify our listener to check if the player clicked the stone, and if so, add a click. | ||
|
||
```java | ||
@EventHandler | ||
public void onInventoryClick(InventoryClickEvent event) { | ||
// We're getting the clicked inventory to avoid situations where the player | ||
// already has a stone in their inventory and clicks that one. | ||
Inventory inventory = event.getClickedInventory(); | ||
// Add a null check in case the player clicked outside the window. | ||
if (inventory == null || !(inventory.getHolder() instanceof MyInventory myInventory)) { | ||
return; | ||
} | ||
|
||
event.setCancelled(true); | ||
|
||
ItemStack clicked = event.getCurrentItem(); | ||
// Check if the player clicked the stone. | ||
if (clicked != null && clicked.getType() == Material.STONE) { | ||
// Use the method we have on MyInventory to increment the field | ||
// and update the counter. | ||
myInventory.addClick(); | ||
} | ||
} | ||
``` | ||
|
||
:::info | ||
|
||
You can store the created `MyInventory` instance, e.g. on a `Map<UUID, MyInventory>` for per-player use, or as a field to share the inventory between | ||
all players, and use it to persist the counter even when opening the inventory for the next time. | ||
|
||
::: |