【问题标题】:Minecraft Bukkit API Event for opening PlayerInventory用于打开 PlayerInventory 的 Minecraft Bukkit API 事件
【发布时间】:2014-07-25 19:07:31
【问题描述】:

我需要一个在 PlayerInventory 打开或内容更改时调用的事件。 InventoryOpenEvent 仅适用于箱子、熔炉等,但不适用于 PlayerInventory。 我正在使用带有 Craftbukkit 1.7 API 的 Java 1.6 SDK。

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

我现在的问题是,我需要玩家被传送到出生点的时刻。 我第一次尝试使用 PlayerTeleportEvent,但这还为时过早。在 ProWalls 清空库存并添加他自己的物品之前,这些物品将被添加。

现在我想在玩家的物品栏中添加一个物品时,通过一个名为 Event 来尝试它。然后我可以检查添加的项目是否是ProWalls在游戏开始时添加的项目,如果是,我将添加我自己的项目。

ProWalls 在开始时调用方法 initGameSettings() 来保存库存,清除它并添加他自己的项目。所以另一个可能的解决方案是,当插件 ProWalls 调用他自己的方法 initGameSettings() 时调用一个事件。但我不知道,这是否可能。 使用我当前的代码,我会在我的库存中看到从我的工具包中添加的物品,但几毫秒后,这些物品将被删除。如果没有其他解决方案,我可能不得不用一个计时器来解决它,它会等待几毫秒并添加项目。但是,正如我发现的那样,这不是一个好的解决方案......

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

【问题讨论】:

    标签: java events minecraft bukkit inventory


    【解决方案1】:

    主要编辑:在原始帖子下方包括 NMS / OBC,以展示如何通过反射来做到这一点。

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

    我不太确定为什么除了某些按键触发之外您还需要该事件。如果不调用 InvetoryClickEvent,库存将永远不会更改,因此当玩家加入/进入需要跟踪库存的状态时,只需扫描一次库存并监听 InventoryClickEvent 以处理更改。

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

    使用 NMS/OBC:

    这可以使用反射和 CraftBukkit / NMS 类来实现,但是这样做会经常发生变化,并且会导致您的插件中断每次更新,除非您对自己的反射进行过验证(这方面的教程很丰富)。

    目前的问题:

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

    设置环境:

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

    覆盖玩家库存:

    我们需要做的第一件事是创建玩家库存类的子类。查看CraftPlayer insternals,我们看到它存储了CraftInventoryPlayer 类,而不是PlayerInventory,因此,这是我们必须扩展的类。创建扩展 CraftInventoryPlayer 的新类。确保它有一个接受net.minecraft.server.VERSION.PlayerInventory 的构造函数。 VERSION 应替换为当前 NMS / OBC 版本字符串。当前为 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 方法,但允许您检查返回值并检查实际添加到玩家库存中的内容。通过检查items 和leftovers 中的差异,您可以准确判断哪些物品以及其中添加了多少到库存中,并在发生重要事情时调用您自己的事件和方法。我将让您编写考试代码,因为我不确定您要完成什么。

    用我们的自定义类替换玩家的库存:

    我不确定在哪里进行此替换,但我提供了我能想到的最佳选择。我们想在插件启用时替换玩家的物品栏,但是一旦它被禁用,我们需要撤消所做的更改,以便我们的自定义类可以正确卸载。我们将听取PlayerJoinEvent 和PlayerQuitEvent 进行这些更改。

    @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);
        }
    }
    

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

    如果我上面概述的任何内容都不起作用,请随时告诉我。其中大部分是理论上的,但根据相关问题的先前经验,它应该可以工作。

    【讨论】:

    • 我需要这个,因为我想在玩家库存中添加特定项目时调用一个函数。此项目由另一个插件添加。该插件使用 iventory.addItem(...) 添加项目,然后不调用事件:/。我需要一个事件,例如,我可以使用 /give 或 /item 编辑 Essentials 给出的项目。因此,当另一个插件在 playerinventory 中添加项目时调用的事件。
    • 我在 NMS / OBC 解决方案中进行了编辑,但它会破坏每次更新并且具有一些非常高级的概念。
    • 首先感谢您的努力。我现在换了一种方式。游戏开始时,Prowalls 插件会将我传送到随机的羊毛块上。我现在在这些羊毛块上放置一个压力板并捕捉 PlayerInteraction 事件。当玩家站在木制压力板上并且玩家在游戏中时,他会得到他的物品。 PS:我现在应该将哪个问题标记为解决我问题的答案?
    • 您确实自己想出了一个解决方法,但我相信我想出了原始问题的答案。如果您也相信这一点,请将其标记为这样,否则不要。现在您不能将 cmets 标记为答案。
    【解决方案2】:

    您可以使用 InventoryOpenEvent:

    @EventHandler
    public void onInvOpen(InventoryOpenEvent e)
    

    也许你必须检查播放器:

    if(!(e.getPlayer().equals(anotherplayer)))
        return;
    

    然后你可以检查库存是否是玩家的库存:

    if(e.getInventory() !instanceof PlayerInventory
        return;
    

    (只是检查库存是否是a玩家的库存)

    您还可以通过查看库存持有者来查看是谁的玩家库存:

    Player invholder = (Player) e.getInventory().getHolder();
    if(!invholder.equals(*anotherplayer*))
        return;
    

    那么你有你的活动!

    (我没有测试它,但类似的代码对我有用...)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多