ElaBosak233 / Valkyrie

一个面向中文社区的 Bukkit / Spigot 插件开发教程
https://valkyrie.ela.ac.cn
MIT License
27 stars 3 forks source link

关于创建inventory遇到的问题 #17

Closed L1-An closed 4 years ago

L1-An commented 4 years ago

如题,我在valkyrie章节4.3中按照教程写入了玩家加入打开gui事件,在idea中这样写的 @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Inventory inventory = Bukkit.createInventory(null, InventoryType.CHEST, ChatColor.GOLD+"Valkyrie"); inventory.setItem(11, new ItemStack(Material.DIAMOND, 64)); inventory.setItem(13, new ItemStack(Material.GOLD_BLOCK, 32)); inventory.setItem(15, new ItemStack(Material.EMERALD, 64)); event.getPlayer().openInventory(inventory); } 而我导入服务端并且登入后却无法登入,在游戏内的报错信息为Internal server error 在后台则提示报错信息为 java.lang.IllegalArgumentException: Listener already listening at net.minecraft.server.v1_13_R2.Container.addSlotListener(Container.java:55) ~[spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.EntityPlayer.syncInventory(EntityPlayer.java:302) ~[spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.PlayerList.a(PlayerList.java:215) ~[spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.LoginListener.b(LoginListener.java:149) ~[spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.LoginListener.tick(LoginListener.java:53) ~[spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.NetworkManager.a(NetworkManager.java:230) ~[spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.ServerConnection.c(ServerConnection.java:119) [spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.MinecraftServer.b(MinecraftServer.java:994) [spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.DedicatedServer.b(DedicatedServer.java:417) [spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.MinecraftServer.a(MinecraftServer.java:831) [spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at net.minecraft.server.v1_13_R2.MinecraftServer.run(MinecraftServer.java:729) [spigot-1.13.2.jar:git-Spigot-1a3504a-84f3da3] at java.lang.Thread.run(Unknown Source) [?:1.8.0_211] 求解

ElaBosak233 commented 4 years ago

请问我能看看你的工程源文件吗?这样的少量信息不够让我进行分析,你可以把项目文件打包上传到网盘上或者 https://cowtransfer.com ,多谢~

ElaBosak233 commented 4 years ago

而且无法登陆是个什么概念?是根本没有进入到服务器吗?建议开发的时候暂时移除额外插件

L1-An commented 4 years ago

而且无法登陆是个什么概念?是根本没有进入到服务器吗?建议开发的时候暂时移除额外插件

我测试服务端里面没有任何插件,是可以进入服务器的,是进入以后瞬间就会被踢出

ElaBosak233 commented 4 years ago

那可真的怪了,我想看看项目文件,可以的话我今天晚上解决掉

L1-An commented 4 years ago

请问我能看看你的工程源文件吗?这样的少量信息不够让我进行分析,你可以把项目文件打包上传到网盘上或者 https://cowtransfer.com ,多谢~

额..这里的打包是直接把ideaprojects里面的工程文件打包就可以是吗?

ElaBosak233 commented 4 years ago

yep,因为要看看全部

L1-An commented 4 years ago

yep,因为要看看全部

没问题,链接是https://cowtransfer.com/s/a43aff2e1b2947 密码是vy4p5g

ElaBosak233 commented 4 years ago

image.png 首先就是这个,为什么 "val" 和 "blockplace" 的 Executor 都是 ValCommand(),而且,既然你的 plugin.yml 给 blockplace 的解释是这样的 image.png 既然作为一个监听器,为什么还要命令呢,真的有些奇怪,试试看先删除 Bukkit.getPluginCommand("blockplace").setExecutor((new ValCommand()));,但我总感觉没有这么简单

另附一句:你的项目使用了 Maven,如果你会用的话,Maven 和 Gradle 都同样的,不用担心

L1-An commented 4 years ago

image.png 首先就是这个,为什么 "val" 和 "blockplace" 的 Executor 都是 ValCommand(),而且,既然你的 plugin.yml 给 blockplace 的解释是这样的 image.png 既然作为一个监听器,为什么还要命令呢,真的有些奇怪,试试看先删除 Bukkit.getPluginCommand("blockplace").setExecutor((new ValCommand()));,但我总感觉没有这么简单

另附一句:你的项目使用了 Maven,如果你会用的话,Maven 和 Gradle 都同样的,不用担心

我最开始创建的项目就是gradle,但是gradle不知道怎么的总是不给我补全,所以我才无奈用了maven,想问一下maven和gradle的差别在于何处 其次那个图片不知道是我的问题还是怎么回事没有读取出来

L1-An commented 4 years ago

image.png 首先就是这个,为什么 "val" 和 "blockplace" 的 Executor 都是 ValCommand(),而且,既然你的 plugin.yml 给 blockplace 的解释是这样的 image.png 既然作为一个监听器,为什么还要命令呢,真的有些奇怪,试试看先删除 Bukkit.getPluginCommand("blockplace").setExecutor((new ValCommand()));,但我总感觉没有这么简单 另附一句:你的项目使用了 Maven,如果你会用的话,Maven 和 Gradle 都同样的,不用担心

我最开始创建的项目就是gradle,但是gradle不知道怎么的总是不给我补全,所以我才无奈用了maven,想问一下maven和gradle的差别在于何处 其次那个图片不知道是我的问题还是怎么回事没有读取出来

我已经按照你上文中提到的问题解决了,可是inventory的问题依然存在

CaveNightingale commented 4 years ago

我并不认为PlayerJoinEvent触发的时候玩家已经处于就绪能打开物品栏的状态

CaveNightingale commented 4 years ago

PlayerJoinEvent触发时玩家正处于泥土界面,在这样的情况下openInventory有点异想天开

L1-An commented 4 years ago

 那么既然如此我是否可以使用delay来做到进入游戏打开inventory呢,那么delay具体在插件中又如何写呢?

发自我的iPhone

在 2020年8月11日,上午12:25,CaveNightingale notifications@github.com 写道:



PlayerJoinEvent触发时玩家正处于泥土界面,在这样的情况下openInventory有点异想天开

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/ElaBosak233/Valkyrie/issues/17#issuecomment-671455320, or unsubscribehttps://github.com/notifications/unsubscribe-auth/APAJTX7HOUO7UDEUPZZLH3LSAANQRANCNFSM4P2AKJYA.

ElaBosak233 commented 4 years ago

如果是卡在泥土里面了,先把插件卸载,然后让玩家暴露在空气中,再把插件装上,毕竟这个的解决对策直接想还比较麻烦

CaveNightingale commented 4 years ago

如果是卡在泥土里面了,先把插件卸载,然后让玩家暴露在空气中,再把插件装上,毕竟这个的解决对策直接想还比较麻烦

所谓泥土界面是指加入世界的动画界面,你理解错了吧

ElaBosak233 commented 4 years ago

如果是卡在泥土里面了,先把插件卸载,然后让玩家暴露在空气中,再把插件装上,毕竟这个的解决对策直接想还比较麻烦

所谓泥土界面是指加入世界的动画界面,你理解错了吧

抱歉,确实理解错了,如果是加载界面,那么如果卸载掉制作的插件呢?能否进入服务器?这个问题真的有些棘手了

L1-An commented 4 years ago

如果是卡在泥土里面了,先把插件卸载,然后让玩家暴露在空气中,再把插件装上,毕竟这个的解决对策直接想还比较麻烦

所谓泥土界面是指加入世界的动画界面,你理解错了吧

抱歉,确实理解错了,如果是加载界面,那么如果卸载掉制作的插件呢?能否进入服务器?这个问题真的有些棘手了

那么对于处于泥土界面而无法openinventory的状态下,我是否可以用delay一类的东西来做到我需要的功能,亦或是说用别的listener呢?

loinko commented 4 years ago

那么对于处于泥土界面而无法openinventory的状态下,我是否可以用delay一类的东西来做到我需要的功能,亦或是说用别的listener呢?

delay 的话,感觉不是很灵活,毕竟像我这种上世纪U的渣机,或者部分网络爆炸半天 Loading terrain 进不去的,用 delay 会有不确定性。

异想天开一下,你可以在 PlayerJoinEvent 之后随即开始监听玩家移动,监听到玩家移动后打开物品栏。玩家移动包括WASD也包括头部转向,一般玩家开始移动了就说明已经加载出世界了。

为了确保只有玩家加入后的第一次移动才会触发打开物品栏逻辑,避免重复,还要设置一个 flag。 比如设置一个List,玩家加入后 add(player.getUUID()),监听移动时先判断是否存在这一值,如果存在则打开物品栏,然后 remove 该值。

手机不好打字,瞎写了一段伪代码:

List<UUID> flag = new ArrayList<>();

@EventHandler
void PlayerJoin(PlayerJoinEvent e){
  if ( ! flag.contains(e.getPlayer().getUUID()) )
    flag.add( e.getPlayer().getUUID() );
}

@EventHandler
void PlayerMove(PlayerMoveEvent e){
  Player p = e.getPlayer();
  if ( flag.contains( p.getUUID() ) ) {
    // do something...
    flag.remove( p.getUUID() );
    // 不要忘记 remove
  }
CaveNightingale commented 4 years ago

如果是卡在泥土里面了,先把插件卸载,然后让玩家暴露在空气中,再把插件装上,毕竟这个的解决对策直接想还比较麻烦

所谓泥土界面是指加入世界的动画界面,你理解错了吧

抱歉,确实理解错了,如果是加载界面,那么如果卸载掉制作的插件呢?能否进入服务器?这个问题真的有些棘手了

那么对于处于泥土界面而无法openinventory的状态下,我是否可以用delay一类的东西来做到我需要的功能,亦或是说用别的listener呢?

可以使用delay,有没有别的listener我不清楚,毕竟我不是Bukkit插件开发者,这个问题需要ElaBosak233来回答

由于你执行一个无法完成的操作(在泥土界面开物品栏),Minecraft处理了此错误(因为出现操作无法完成的状况说明玩家处于一个不正常的状况,需要重新登录来解决问题,就跟蓝屏需要重启一个道理),所以退出了服务器并告知玩家内部错误 但是实际上重新登录并不能解决问题,就跟硬件损坏造成的蓝屏重启无用一个道理,这就导致了你无法登录的现象

L1-An commented 4 years ago

如果是卡在泥土里面了,先把插件卸载,然后让玩家暴露在空气中,再把插件装上,毕竟这个的解决对策直接想还比较麻烦

所谓泥土界面是指加入世界的动画界面,你理解错了吧

抱歉,确实理解错了,如果是加载界面,那么如果卸载掉制作的插件呢?能否进入服务器?这个问题真的有些棘手了

那么对于处于泥土界面而无法openinventory的状态下,我是否可以用delay一类的东西来做到我需要的功能,亦或是说用别的listener呢?

可以使用delay,有没有别的listener我不清楚,毕竟我不是Bukkit插件开发者,这个问题需要ElaBosak233来回答

由于你执行一个无法完成的操作(在泥土界面开物品栏),Minecraft处理了此错误(因为出现操作无法完成的状况说明玩家处于一个不正常的状况,需要重新登录来解决问题,就跟蓝屏需要重启一个道理),所以退出了服务器并告知玩家内部错误 但是实际上重新登录并不能解决问题,就跟硬件损坏造成的蓝屏重启无用一个道理,这就导致了你无法登录的现象

明白了,感谢提醒

L1-An commented 4 years ago

那么对于处于泥土界面而无法openinventory的状态下,我是否可以用delay一类的东西来做到我需要的功能,亦或是说用别的listener呢?

delay 的话,感觉不是很灵活,毕竟像我这种上世纪U的渣机,或者部分网络爆炸半天 Loading terrain 进不去的,用 delay 会有不确定性。

异想天开一下,你可以在 PlayerJoinEvent 之后随即开始监听玩家移动,监听到玩家移动后打开物品栏。玩家移动包括WASD也包括头部转向,一般玩家开始移动了就说明已经加载出世界了。

为了确保只有玩家加入后的第一次移动才会触发打开物品栏逻辑,避免重复,还要设置一个 flag。 比如设置一个List,玩家加入后 add(player.getUUID()),监听移动时先判断是否存在这一值,如果存在则打开物品栏,然后 remove 该值。

手机不好打字,瞎写了一段伪代码:

List<UUID> flag = new ArrayList<>();

@EventHandler
void PlayerJoin(PlayerJoinEvent e){
  if ( ! flag.contains(e.getPlayer().getUUID()) )
    flag.add( e.getPlayer().getUUID() );
}

@EventHandler
void PlayerMove(PlayerMoveEvent e){
  Player p = e.getPlayer();
  if ( flag.contains( p.getUUID() ) ) {
    // do something...
    flag.remove( p.getUUID() );
    // 不要忘记 remove
  }

可是如果照你这么写会不会因为插件一直在监听玩家移动事件而造成较大的性能损耗呢?

ElaBosak233 commented 4 years ago

监听 move 确实有可能造成损耗,但是执行的内容不多,也不一定会造成很多损耗,但重点是,这都快成 Minecraft 服务器问题了,我在 1.13(注意是 1.13 不是 1.13.2) 上测试这段代码的时候,未曾出现过错误啊,所以棘手的就是这个问题了,请问能将你使用 Maven 构建出来的插件 Jar 文件发送上来看看吗,我可以回去的时候开自己的服务器测试一遍

loinko commented 4 years ago

可是如果照你这么写会不会因为插件一直在监听玩家移动事件而造成较大的性能损耗呢?

按理来说,不会。登陆插件 Authme 也在做这件事,而且计算量比上面的多很多,但是咱们不也好好用着嘛 PlayerListener.java#L319 因为一开始判断玩家不在列表内之后就已经中断了,所以会小很多。如果非常注重性能,可以试试查找性能更高的其它实现类

L1-An commented 4 years ago

监听 move 确实有可能造成损耗,但是执行的内容不多,也不一定会造成很多损耗,但重点是,这都快成 Minecraft 服务器问题了,我在 1.13(注意是 1.13 不是 1.13.2) 上测试这段代码的时候,未曾出现过错误啊,所以棘手的就是这个问题了,请问能将你使用 Maven 构建出来的插件 Jar 文件发送上来看看吗,我可以回去的时候开自己的服务器测试一遍

我用的是1.13.2,在这个版本上的确会出现问题,而我按照上面提到过的用flag来解决这个问题就可以做到

loinko commented 4 years ago

我用的是1.13.2,在这个版本上的确会出现问题,而我按照上面提到过的用flag来解决这个问题就可以做到

所以在 openInventory 的时候报错是啥呢

L1-An commented 4 years ago

我用的是1.13.2,在这个版本上的确会出现问题,而我按照上面提到过的用flag来解决这个问题就可以做到

所以在 openInventory 的时候报错是啥呢

应该是在openinventory的时候报错,最开始的报错信息就在最上面

CaveNightingale commented 4 years ago

我用的是1.13.2,在这个版本上的确会出现问题,而我按照上面提到过的用flag来解决这个问题就可以做到

所以在 openInventory 的时候报错是啥呢

应该是在openinventory的时候报错,最开始的报错信息就在最上面

上面那个信息似乎和openInventory关系不大

loinko commented 4 years ago

应该是在openinventory的时候报错,最开始的报错信息就在最上面

Google 了一下报错,从 1.8 开始就有人遇到过了,应该不是版本的问题,解决方案都是延迟打开 inventory。

You can't open the player's inventory instantly when a player joins. So try to delay your code (see linked thread), which opens the player's inventory.

为啥 @ElaBosak233 没有遇到这个问题可能是他那边环境不同,MC 加载够快的情况下,理论上是可以玩家加入后立刻 openInventory 的。但是如果加载不那么快或者受制于网速,就会出现上面的问题。

CaveNightingale commented 4 years ago

监听 move 确实有可能造成损耗,但是执行的内容不多,也不一定会造成很多损耗,但重点是,这都快成 Minecraft 服务器问题了,我在 1.13(注意是 1.13 不是 1.13.2) 上测试这段代码的时候,未曾出现过错误啊,所以棘手的就是这个问题了,请问能将你使用 Maven 构建出来的插件 Jar 文件发送上来看看吗,我可以回去的时候开自己的服务器测试一遍

//1.16.1测试,你的openInventory不能正常运行 抱歉,搞错了

CaveNightingale commented 4 years ago

那么对于处于泥土界面而无法openinventory的状态下,我是否可以用delay一类的东西来做到我需要的功能,亦或是说用别的listener呢?

delay 的话,感觉不是很灵活,毕竟像我这种上世纪U的渣机,或者部分网络爆炸半天 Loading terrain 进不去的,用 delay 会有不确定性。 异想天开一下,你可以在 PlayerJoinEvent 之后随即开始监听玩家移动,监听到玩家移动后打开物品栏。玩家移动包括WASD也包括头部转向,一般玩家开始移动了就说明已经加载出世界了。 为了确保只有玩家加入后的第一次移动才会触发打开物品栏逻辑,避免重复,还要设置一个 flag。 比如设置一个List,玩家加入后 add(player.getUUID()),监听移动时先判断是否存在这一值,如果存在则打开物品栏,然后 remove 该值。 手机不好打字,瞎写了一段伪代码:

List<UUID> flag = new ArrayList<>();

@EventHandler
void PlayerJoin(PlayerJoinEvent e){
  if ( ! flag.contains(e.getPlayer().getUUID()) )
    flag.add( e.getPlayer().getUUID() );
}

@EventHandler
void PlayerMove(PlayerMoveEvent e){
  Player p = e.getPlayer();
  if ( flag.contains( p.getUUID() ) ) {
    // do something...
    flag.remove( p.getUUID() );
    // 不要忘记 remove
  }

可是如果照你这么写会不会因为插件一直在监听玩家移动事件而造成较大的性能损耗呢?

用HashSet代替List或许会好一些

CaveNightingale commented 4 years ago

应该是在openinventory的时候报错,最开始的报错信息就在最上面

Google 了一下报错,从 1.8 开始就有人遇到过了,应该不是版本的问题,解决方案都是延迟打开 inventory。

You can't open the player's inventory instantly when a player joins. So try to delay your code (see linked thread), which opens the player's inventory.

为啥 @ElaBosak233 没有遇到这个问题可能是他那边环境不同,MC 加载够快的情况下,理论上是可以玩家加入后立刻 openInventory 的。但是如果加载不那么快或者受制于网速,就会出现上面的问题。

这个可能真的是版本问题,我刚才做了个实验,虽然说PlayerJoinEvent确实是在客户端处于泥土界面时触发的,并且泥土界面也确实不能执行开物品栏动作,但是高版本Minecraft会将开物品栏的行为自动延后处理,所以说如果玩家最终能进到游戏的话,应该都是可以正常执行打开物品栏动作的

我上面的说法不够严谨,对不起各位

ElaBosak233 commented 4 years ago

所以到最后这个问题都没有解开,理论上使用 1.13 bukkit 作为开发环境,都可以向上兼容,我建议重新开个项目,使用 1.13

L1-An commented 4 years ago

所以到最后这个问题都没有解开,理论上使用 1.13 bukkit 作为开发环境,都可以向上兼容,我建议重新开个项目,使用 1.13

L1-An commented 4 years ago

所以到最后这个问题都没有解开,理论上使用 1.13 bukkit 作为开发环境,都可以向上兼容,我建议重新开个项目,使用 1.13

L1-An commented 4 years ago

所以到最后这个问题都没有解开,理论上使用 1.13 bukkit 作为开发环境,都可以向上兼容,我建议重新开个项目,使用 1.13

刚刚不小心给close了,不过我用1.13的开发环境我就怕出现一些问题,而我需要的客户端版本也正好是1.13.2,所以我还是想在1.13.2的环境下把这个问题给解决掉

CaveNightingale commented 4 years ago

所以到最后这个问题都没有解开,理论上使用 1.13 bukkit 作为开发环境,都可以向上兼容,我建议重新开个项目,使用 1.13

刚刚不小心给close了,不过我用1.13的开发环境我就怕出现一些问题,而我需要的客户端版本也正好是1.13.2,所以我还是想在1.13.2的环境下把这个问题给解决掉

能不能发一下你最新的文件呢?

L1-An commented 4 years ago

所以到最后这个问题都没有解开,理论上使用 1.13 bukkit 作为开发环境,都可以向上兼容,我建议重新开个项目,使用 1.13

刚刚不小心给close了,不过我用1.13的开发环境我就怕出现一些问题,而我需要的客户端版本也正好是1.13.2,所以我还是想在1.13.2的环境下把这个问题给解决掉

能不能发一下你最新的文件呢?

上面我似乎有发过,我现在的项目里面没什么改动

loinko commented 4 years ago

所以到最后这个问题都没有解开,理论上使用 1.13 bukkit 作为开发环境,都可以向上兼容,我建议重新开个项目,使用 1.13

刚刚不小心给close了,不过我用1.13的开发环境我就怕出现一些问题,而我需要的客户端版本也正好是1.13.2,所以我还是想在1.13.2的环境下把这个问题给解决掉

感觉这是一个「客户端如何处理在Loading屏上打开物品栏动作」的问题,最终应该还是要看客户端的处理方式的吧,光更换服务端应该是无法解决的。其实解决方案之前已经有了,比如延迟几个 tick 或者监听玩家移动。

L1-An commented 4 years ago

所以到最后这个问题都没有解开,理论上使用 1.13 bukkit 作为开发环境,都可以向上兼容,我建议重新开个项目,使用 1.13

刚刚不小心给close了,不过我用1.13的开发环境我就怕出现一些问题,而我需要的客户端版本也正好是1.13.2,所以我还是想在1.13.2的环境下把这个问题给解决掉

感觉这是一个「客户端如何处理在Loading屏上打开物品栏动作」的问题,最终应该还是要看客户端的处理方式的吧,光更换服务端应该是无法解决的。其实解决方案之前已经有了,比如延迟几个 tick 或者监听玩家移动。

那么问题就算解决了吧,主要问题还是出在minecraft本身上面

ElaBosak233 commented 4 years ago

兼容的方法很简单,就是在 plugin.yml 上不要写 api-version,只要不使用 nms,都没问题

CaveNightingale commented 4 years ago

感觉事情没这么简单,既然1.13和1.16客户端都能处理在loading屏幕的OpenScreenS2CPacket(yarn反混淆名),那么1.13.2不能就有点诡异了,改天弄个1.13.2来调试一下

CaveNightingale commented 4 years ago

应该是在openinventory的时候报错,最开始的报错信息就在最上面

Google 了一下报错,从 1.8 开始就有人遇到过了,应该不是版本的问题,解决方案都是延迟打开 inventory。

You can't open the player's inventory instantly when a player joins. So try to delay your code (see linked thread), which opens the player's inventory.

为啥 @ElaBosak233 没有遇到这个问题可能是他那边环境不同,MC 加载够快的情况下,理论上是可以玩家加入后立刻 openInventory 的。但是如果加载不那么快或者受制于网速,就会出现上面的问题。

不,不能,你去看看CraftBukkit的代码,PlayJoinEvent发布时刚完成身份验证,客户端还啥都不知道呢