gpt4 book ai didi

java - 用于打开PlayerInventory的Minecraft Bukkit API事件

转载 作者:行者123 更新时间:2023-12-01 12:42:40 25 4
gpt4 key购买 nike

打开PlayerInventory或更改内容时,我需要一个被调用的事件。
InventoryOpenEvent仅适用于箱子,炉子等,而不适用于PlayerInventory。
我正在将Java 1.6 SDK与Craftbukkit 1.7 API配合使用。

我的Minecraft TheWalls服务器上安装了插件ProWalls。现在,我正在编写一个自己的插件,应该扩展此插件。我的插件仅允许用户在游戏开始之前选择一个工具包,当游戏开始并且所有玩家被传送到其开始位置时,我的插件应添加之前选择的工具包的工具包项目。

现在的问题是,我需要片刻将玩家传送到生成点。
我首先用PlayerTeleportEvent尝试过,但这还为时过早。在ProWalls清除库存并添加自己的物料之前,将添加物料。

现在,我想用一个称为Event的事件进行尝试,将事件添加到播放器的清单中。然后,我可以检查添加的项目是否是ProWalls在游戏开始时添加的项目,如果属实,我将添加自己的项目。

ProWalls在开始时调用方法initGameSettings()来保存清单,清除清单并添加自己的物品。因此,另一种可能的解决方案是,在插件ProWalls调用自己的方法initGameSettings()时使事件被调用。但是我不知道这是否可行。
使用我当前的代码,我会在库存中稍后看到从套件中添加的物品,但几毫秒后,这些物品将被移除。如果没有其他解决方案,则可能需要使用计时器,计时器要等待几毫秒,然后添加项目。但是,正如我所发现的,这是没有好的解决方案...

为了更好地理解这里的顺序:
玩家加入TheWalls游戏,ProWalls插件调用initGameSettings(),插件清除玩家库存并添加自己的物品,现在我的插件应添加套件的物品。

最佳答案

主要编辑:在原始帖子下方包含NMS / OBC,以显示如何使用反射进行操作。

Player库存是通过选择Mojang缓存在客户端的。它在打开时不需要询问任何数据,因此除了服务器连接的初始请求外,它不会向服务器发送任何请求。没有方法只能在进行修改时测试库存何时打开。

我不太确定为什么除了某些按键触发之外还需要该事件。除非调用InvetoryClickEvent,否则清单永远不会改变,因此,当玩家加入/进入需要跟踪其清单的状态时,只需扫描其清单一次,然后听InventoryClickEvent即可处理更改。

编辑:存储不正确,因为服务器处理更改,缓存是一个更好的术语。

使用NMS / OBC:

使用反射和CraftBukkit / NMS类是可能的,但是这样做经常会发生变化,除非您将来需要对反射进行验证,否则插件将中断每次更新(有关此问题的教程非常丰富)。

当前问题:

Bukkit不允许您侦听来自Give命令或直接代码清单操纵的清单变化。因此,我们需要添加一种方法来侦听这些更改。最好的方法是覆盖更改库存的方法,并添加事件以简化将来的更改,或者在那里进行计算。我将解释后者,因为它更易于处理,并且还存在各种自定义事件教程。

设置环境:

使用NMS / OBC时,您需要导入CraftBukkit以获取所需的类。下载CraftBukkit jar,然后将其导入到您的项目中,但是通常这样做。如果您收到一些方法签名方法,请确保Bukkit的导入优先级更高。同样,教程也向您展示了如何在大多数IDE上修复该错误。

覆盖玩家库存:

我们需要做的第一件事是创建玩家清单类的子类。查看CraftPlayer内部对象,我们看到它存储了一个CraftInventoryPlayer类,而不是PlayerInventory,因此,这是我们必须扩展的类。创建扩展CraftInventoryPlayer的新类。确保它具有接受net.minecraft.server.VERSION.PlayerInventory的构造函数。应该用当前的NMS / OBC版本字符串替换VERSION。当前版本为1.7.10,版本为v1_7_R4。几乎每一次Minecraft版本更改都会改变这种情况,这是大多数版本错误的根源。通过扩展类和构造函数调用超级构造函数,您有了基本的自定义清单。现在,我们必须确定要覆盖的方法。

覆盖addAll方法:

假设Essentials和其他插件使用addAll方法,这是我们需要监视的方法。我们要做的是重写addAll方法,并将其功能包装在我们要进行的检查周围。在您的自定义清单类中,我们执行以下操作。

public HashMap<Integer, ItemStack> addItem(ItemStack... items) {
HashMap<Integer, ItemStack> leftovers = super.addItem(items);
//Examination code
return leftovers;
}


此方法将调用原始的 addItem方法,但允许您检查返回值并检查实际添加到播放器清单中的内容。通过检查 itemsleftovers的差异,您可以准确地分辨出哪些项目以及向清单中添加了哪些项目,并在发生重要事件时调用自己的事件和方法。由于我不确定您要完成的工作,我将让您编写考试代码。

用我们的自定义类替换玩家的广告资源:

我不确定在哪里进行替换,但是我提供了我能想到的最佳选择。启用插件后,我们想替换玩家的清单,但是一旦禁用,我们就需要撤消所做的更改,以便可以正确卸载自定义类。我们将听 PlayerJoinEventPlayerQuitEvent进行这些更改。

@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
CraftPlayer craftPlayer = (CraftPlayer) event.getPlayer();
//I am not actually typing this code in an IDE, so feel free to change
//the try-catch block to only catch what is needed
try {
Field field = CraftHumanEntity.class.getDeclaredField("inventory");
field.setAccessible(true);
CraftInventoryPlayer originalInventory = (CraftInventoryPlayer) field.get(craftPlayer);
//Store inventory here, I will use a Map that would be declared above.
originalInventories.put(craftPlayer, originalInventory);
field.set(craftPlayer, new CustomInventory(craftPlayer.getHandle().inventory));
} catch (Exception e) {
Bukkit.getLogger.log(Level.SEVERE, "Error creating custom player inventory", e);
}
}

@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
CraftPlayer craftPlayer = (CraftPlayer) event.getPlayer();
//I am not actually typing this code in an IDE, so feel free to change
//the try-catch block to only catch what is needed
try {
Field field = CraftHumanEntity.class.getDeclaredField("inventory");
field.setAccessible(true);
CraftInventoryPlayer originalInventory = originalInventories.get(craftPlayer);
//Store inventory here, I will use a Map that would be declared above.
field.set(craftPlayer, originalInventory);
} catch (Exception e) {
Bukkit.getLogger.log(Level.SEVERE, "Error replacing player inventory with original", e);
}
}


这两个反射有点复杂,但是我会尽力解释。几乎每个 Bukkit接口在 CraftBukkit中都有相应的实现,该实现在接口名称前加上“ Craft”。有时单词顺序也会更改。在 PlayerJoinEvent侦听器中,我们得到 CraftHumanEntity类,并得到它的 inventory字段。这是存储玩家库存的字段。它是私有的,因此我们使用 getDeclaredField方法而不是 getField方法,并且必须提供声明它的确切类。然后,我们使该字段可访问并处理其数据。对于玩家加入,我们 get存储在该字段中的当前 CraftInventoryPlayer,然后将其存储在其他位置以供以后检索。然后,将该字段 set放入我们的自定义清单对象。请注意,构造函数接受 net.minecraft.server.PlayerInventory,因此我们将清单提供给构造函数。我们终于捕获了这里可能发生的所有各种异常,并且我们已经成功覆盖了玩家的库存。在 PlayerQuitEvent中,我们进行相反的操作,将自定义库存替换为原始库存,因为我们不再需要对其进行管理。

如果我在上面概述的任何内容都不起作用,请随时告诉我。多数是理论上的,但从相关问题的先前经验来看,它应该起作用。

关于java - 用于打开PlayerInventory的Minecraft Bukkit API事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24962864/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com