Spigot InventoryClickEvent 仅在第二次单击时在库存中查找项目

Spigot InventoryClickEvent only find item in inventory on second click

我想创建一个插件,其中我有一个 table 并且我可以制作常规的制作食谱和一些自定义的。 我知道我可以默认创建自定义食谱,但我只想在这个“特殊”table.

中制作它们 craftable

所以我得到了我的 InventoryClickEvent 的代码 https://hastebin.com/konehibohi.cs

我的问题是,当我将一个物品放入指定的制作槽之一时,这会抛出一个 NullPointerException,但当我将第二个物品放入同一个槽时,它会输出正确的配方。

控制台输出:

[02:21:51] [Server thread/INFO]: VanillaRL | Enabling VanillaRL...
[02:21:51] [Server thread/INFO]: VanillaRL | Enabled VanillaRL
[02:21:51] [Server thread/INFO]: Server permissions file permissions.yml is empty, ignoring it
[02:21:51] [Server thread/INFO]: F4LS3_: Reload complete.
[02:21:53] [Server thread/INFO]: [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
[02:21:53] [Server thread/ERROR]: Could not pass event InventoryClickEvent to VanillaRL v1.0-SNAPSHOT
org.bukkit.event.EventException: null
        at org.bukkit.plugin.java.JavaPluginLoader.execute(JavaPluginLoader.java:319) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:589) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:576) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.PlayerConnection.a(PlayerConnection.java:2191) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.PacketPlayInWindowClick.a(SourceFile:32) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.PacketPlayInWindowClick.a(SourceFile:10) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.PlayerConnectionUtils.lambda[=10=](PlayerConnectionUtils.java:19) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.TickTask.run(SourceFile:18) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.IAsyncTaskHandler.executeTask(SourceFile:144) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.IAsyncTaskHandlerReentrant.executeTask(SourceFile:23) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.IAsyncTaskHandler.executeNext(SourceFile:118) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.MinecraftServer.aZ(MinecraftServer.java:943) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.MinecraftServer.executeNext(MinecraftServer.java:936) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.IAsyncTaskHandler.awaitTasks(SourceFile:127) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.MinecraftServer.sleepForTick(MinecraftServer.java:920) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.MinecraftServer.v(MinecraftServer.java:852) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at net.minecraft.server.v1_16_R1.MinecraftServer.lambda[=10=](MinecraftServer.java:164) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: java.lang.IllegalArgumentException: Result cannot be null
        at org.apache.commons.lang.Validate.notNull(Validate.java:192) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at org.bukkit.craftbukkit.v1_16_R1.CraftServer.getRecipesFor(CraftServer.java:1214) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at org.bukkit.Bukkit.getRecipesFor(Bukkit.java:717) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        at de.f4ls3.vanillarl.events.InventoryClickEvent.onInventoryClick(InventoryClickEvent.java:34) ~[?:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
        at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
        at org.bukkit.plugin.java.JavaPluginLoader.execute(JavaPluginLoader.java:315) ~[spigot-1.16.1.jar:git-Spigot-0287a20-7560f5f]
        ... 18 more
[02:21:54] [Server thread/INFO]: [null, null, null, null, null, null, null, null, null, null, ItemStack{OAK_PLANKS x 1}, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
[02:21:54] [Server thread/INFO]: [org.bukkit.craftbukkit.v1_16_R1.inventory.CraftShapelessRecipe@c41eafb]

有关所有 类 的更多信息,您可以查看我的回购协议 https://github.com/F4LS3/VanillaRL

提前致谢!

我找到问题的根源了。它依赖于 Spigot/Bukkit API 处理事件的方式。


为什么会这样?

在这里,您可以解释为什么会发生这种情况。

事件的基本定义

在 Minecraft 中,事件包装客户端执行的操作并将其发送到服务器。这样,当您执行某个操作时,服务器就会知道并能够将此事件传递给其他客户端(其他在线玩家)。

事件处理程序未捕获事件时会发生什么情况?

如果您不实现事件处理程序,则事件将按以下方式处理:

Your client  ---------------->  The server --------------> All clients (including you)
Requests an action          Applies the action             Recieve the action

当您在事件处理程序中捕获事件时会发生什么?

另一方面,如果您实现事件处理程序,您将在服务器端拦截事件,因此事件流将是:

Your client  ----------\                     /---> The server -----> All clients
                        \-- Event handler --/                 

在事件处理程序中,服务器在将事件应用到所有客户端之前等待您处理该事件。

InventoryClickEvent 上,您可能正在修改库存内容,但这些更改在您的处理程序结束处理该事件之前不会应用。

所以如果你在活动期间寻找库存内容,你会在用户点击之前得到库存内容。由于服务器尚未处理该事件。

如果您随后获取先前空槽的内容,内容将为 null,因为服务器尚未处理新项目。然后,当你取出物品时,内容将 return 物品,因为服务器还不知道该插槽是空的。


我的解决方案

安排异步延迟任务,以便您的事件处理程序退出并且服务器将在您检查用户放置的项目之前处理该事件:

Your client  ----------\                     /---> The server -----> All clients
                        \-- Event handler --/                 
                         \- Schedule async task -> Actual handling

我实现的代码如下:

public class InventoryClickEvent implements Listener
{

    private final VanillaRL plugin;

    public InventoryClickEvent(VanillaRL plugin) {
        this.plugin = plugin;
    }

    @EventHandler
    public void onInventoryClick(org.bukkit.event.inventory.InventoryClickEvent event)
    {
        new BukkitRunnable(){
            @Override
            public void run() {
                delayedHandler(event);
            }
        }.runTaskLaterAsynchronously(plugin, 2);
    }

    private void delayedHandler(org.bukkit.event.inventory.InventoryClickEvent event) {
        if(event.getWhoClicked() instanceof Player)
        {
            Player player = (Player) event.getWhoClicked();

            if (CraftingManager.hasInventory(event.getClickedInventory()))
            {
                CraftingInventory craftingInventory = CraftingManager.getCraftingInventories().stream().filter(craftingInventory1 -> craftingInventory1.getInventory().equals(event.getClickedInventory())).findFirst().get();
                System.out.println(Arrays.toString(craftingInventory.getInventory().getContents()));
                if (craftingInventory.getCraftingSlots().contains(event.getSlot()))
                {
                    if (event.getAction().equals(InventoryAction.PLACE_ALL) || event.getAction().equals(InventoryAction.PLACE_ONE) || event.getAction().equals(InventoryAction.PLACE_SOME))
                    {
                        System.out.println(Bukkit.getRecipesFor(craftingInventory.getItem(event.getSlot())));
                    }
                }
            }
        }
    }
}

然后在主 class 注册事件侦听器时:

Bukkit.getPluginManager().registerEvents(new InventoryClickEvent(this), this);

服务器输出:

[12:48:34] [Craft Scheduler Thread - 0/INFO]: [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, ItemStack{REDSTONE_BLOCK x 61}, null, null, null, null, null, null, null, null]
[12:48:34] [Craft Scheduler Thread - 0/INFO]: [org.bukkit.craftbukkit.v1_16_R1.inventory.CraftShapedRecipe@709b045a]

让我知道这是否适合您!