跳到主要内容

自定义InventoryHolder

InventoryHolder是一种在事件中标识你的插件物品栏的方式。

为什么要使用InventoryHolder?

InventoryHolder简化了确保物品栏是由你的插件创建的步骤。

使用物品栏名称进行标识是不可靠的,因为其他插件甚至玩家可以创建与你的名称完全相同的物品栏。 对于组件,你还需要确保名称完全相同或将其序列化为其他格式。

自定义InventoryHolder没有这些缺点,使用它们可以确保有可用的方法来处理你的物品栏。

创建自定义持有者

第一步是实现InventoryHolder接口。 我们可以通过以下方式实现:创建一个新类,该类将在构造函数中创建我们的Inventory

信息

构造函数需要你的主插件类作为参数来创建Inventory。 如果你愿意,你可以使用静态方法Bukkit#createInventory(InventoryHolder, int)代替并删除参数。

MyInventory.java
public class MyInventory implements InventoryHolder {

private final Inventory inventory;

public MyInventory(MyPlugin plugin) {
// 创建一个有9个槽位的物品栏,这里的`this`是我们的InventoryHolder
this.inventory = plugin.getServer().createInventory(this, 9);
}

@Override
public Inventory getInventory() {
return this.inventory;
}

}

打开物品栏

要打开物品栏,首先我们必须实例化我们的MyInventory类,然后为玩家打开物品栏。 你可以在任何需要的地方这样做。

备注

我们传递一个插件主类的实例,因为构造函数需要它。如果你使用了静态方法并删除了构造函数参数, 你就不需要在这里传递它。

Player player; // 假设我们有一个Player实例。
// 这可以是一个命令、另一个事件或任何其他有Player的地方。

MyInventory myInventory = new MyInventory(myPlugin);
player.openInventory(myInventory.getInventory());

监听事件

一旦我们打开了物品栏,我们可以监听任何我们想要的物品栏事件,并检查 Inventory#getHolder()是否返回我们的MyInventory的实例。

@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
Inventory inventory = event.getInventory();
// 检查持有者是否是我们的MyInventory,
// 如果是,使用instanceof模式匹配立即将其存储在变量中。
if (!(inventory.getHolder(false) instanceof MyInventory myInventory)) {
// 不是我们的物品栏,忽略它。
return;
}

// 在事件中做我们需要做的事。
}

在持有者上存储数据

你可以通过向类添加字段和方法来为你的物品栏存储额外的数据。

让我们制作一个计算我们在其中点击石头次数的物品栏。 首先,让我们稍微修改一下我们的MyInventory类:

MyInventory.java
public class MyInventory implements InventoryHolder {

private final Inventory inventory;

private int clicks = 0; // 存储点击次数。

public MyInventory(MyPlugin plugin) {
this.inventory = plugin.getServer().createInventory(this, 9);

// 设置我们要点击的石头。
this.inventory.setItem(0, ItemStack.of(Material.STONE));
}

// 一个我们将在监听器中调用的方法,当玩家点击石头时。
public void addClick() {
this.clicks++;
this.updateCounter();
}

// 一个更新计数器物品的方法。
private void updateCounter() {
this.inventory.setItem(8, ItemStack.of(Material.BEDROCK, this.clicks));
}

@Override
public Inventory getInventory() {
return this.inventory;
}

}

现在,我们可以修改我们的监听器来检查玩家是否点击了石头,如果是,添加一次点击。

@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
// 我们获取被点击的物品栏以避免玩家
// 已经在他们的物品栏中有一个石头并点击那个的情况。
Inventory inventory = event.getClickedInventory();
// 添加空值检查,以防玩家点击窗口外。
if (inventory == null || !(inventory.getHolder(false) instanceof MyInventory myInventory)) {
return;
}

event.setCancelled(true);

ItemStack clicked = event.getCurrentItem();
// 检查玩家是否点击了石头。
if (clicked != null && clicked.getType() == Material.STONE) {
// 使用我们在MyInventory上的方法来增加字段
// 并更新计数器。
myInventory.addClick();
}
}
信息

你可以存储创建的MyInventory实例,例如在Map<UUID, MyInventory>上用于每个玩家使用,或作为字段在所有玩家之间共享物品栏, 并使用它在下次打开物品栏时保持计数器。