Spigot InventoryClickEvent仅在第二次点击时在库存中找到物品。

huangapple go评论69阅读模式
英文:

Spigot InventoryClickEvent only find item in inventory on second click

问题

我想创建一个插件,在其中我有一个表格,在该表格中,我可以制作常规的合成配方和一些自定义的配方。
我知道我可以默认创建自定义配方,但我希望它们只能在这个“特殊”表格中制作。

所以我有了我的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$1.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$0(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$0(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.DelegatingMethod
英文:

I want to create a plugin where I have a table and in that I can craft regular crafting recipes and some custom ones.
I know that I can create custom Recipes by default but I want to make them only craftable in this "special" table.

So I've got this code for my InventoryClickEvent
https://hastebin.com/konehibohi.cs

And my problem is that this is throwing me a NullPointerException when I place an item in one of the specified crafting slots but when I put a second item in the same slot it outputs me the right recipe.

Console Output:

[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$1.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$0(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$0(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$1.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]

For some more info about all the classes you can check my repo
https://github.com/F4LS3/VanillaRL

Thanks in advance!

答案1

得分: 2

我已经找到了问题的根源。它与Spigot/Bukkit API处理事件的方式有关。

为什么会发生这种情况?

这里有一个关于可能发生的原因的解释。

事件的基本定义

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

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

如果您没有实现事件处理程序,那么事件将按以下方式处理:

您的客户端  ----------------->  服务器 ---------------> 所有客户端(包括您)
请求一个动作           应用该动作             接收该动作

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

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

您的客户端  ----------\                     /---> 服务器 -----》所有客户端
                        \-- 事件处理程序 --/                 

在事件处理程序中,服务器在您处理事件之前等待您处理事件。

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

因此,如果您在事件期间查找库存内容,您将获得用户点击之前的库存内容,因为服务器尚未处理事件。

然后,当您取出以前为空的槽中的物品时,内容将为null,因为服务器尚未知道槽为空。然后,当您取出物品时,内容将返回该物品,因为服务器不知道槽为空。

我的解决方案

安排一个异步延迟任务,以便您的事件处理程序将退出,服务器将在您检查用户放置的物品之前处理事件:

您的客户端  ----------\                     /---> 服务器 -----》所有客户端
                        \-- 事件处理程序 --/                 
                         \- 安排异步任务 -> 实际处理

我实施的代码如下:

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

然后在主类中注册事件监听器时:

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]

请告诉我这对您是否有效!

英文:

I have found the source of the problem. It relies on the way the Spigot/Bukkit API handles events.


Why does this happen?

Here you have an explanation of why this may be happening.

Basic definition of an event

In Minecraft, an event wraps an action that the client has performed and it sends it to the server. In this way when you perform an action the server will know it and will be able to pass this event to other clients (other online players).

What happens when an event is not caught by an event handler?

If you don't implement an event handler, then an event will be processed in the following way:

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

What happens when you catch an event in an event handler?

On the other hand, if you implement an event handler, you are intercepting an event on the server side so the event flow will be:

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

In the event handler, the server is waiting for you to handle the event before applying it to all clients.

On an InventoryClickEvent you may be modifying the inventory contents, but those changes aren't applied until your handler ends handling the event.

So if you look for the inventory contents during the event, you will be given the inventory contents before the user clicked. As the server hasn't processed the event yet.

If you then take the contents of a previously empty slot, the content will be null as the new Item hasn't been processed by the server yet. Then, when you take out the item, the content will return the item as the server doesn't know that the slot is empty yet.


My solution

Schedule an asynchronous delayed task so that your event handler will exit and the server will process the event before you checking the items the user placed:

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

The code I have implemented is the following:

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

Then on the main class when you register the event listener:

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

Server output:

[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]

Let me know if this works for you!

huangapple
  • 本文由 发表于 2020年8月9日 08:27:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/63321407.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定