本帖最后由 HQcxf 于 2020-5-2 14:46 编辑
新手开发 Bukkit 插件,测试于 Spigot 1.15.2
遇到了一些问题,寻求大家的帮助。
-
每当玩家破坏方块后(众所周知,由于不知道从哪来的bug,这个事件总会被触发两次):
复制代码在玩家破坏方块后创建延时任务,一定时间后将破坏的方块替换为钻石块,在非主线程执行。
本来打算将 e.getBlock() 传入,在非主线程
复制代码执行上面第二行时,引发了如下异常,线程中止。
复制代码这不是我所期望的。而如果这么写:
复制代码是能够正常执行的。
-
问题已经解决,感谢用户
https://www.mcbbs.net/?2388218
-
只需确保修改方块的操作在主线程执行即可。可以这样:
复制代码请参阅沙发。
新手开发 Bukkit 插件,测试于 Spigot 1.15.2
遇到了一些问题,寻求大家的帮助。
-
每当玩家破坏方块后(众所周知,由于不知道从哪来的bug,这个事件总会被触发两次):
- @EventHandler
- public void onBreak(BlockBreakEvent e)
- {
- e.setCancelled(true);
- ... // 创建延时任务,传入 e.getBlock()
- }
本来打算将 e.getBlock() 传入,在非主线程
- Block block = task.getTargetBlock(); // 是上面的 e.getBlock()
- block.setType(Material.DIAMOND_BLOCK);
- [13:19:22] [Thread-19/WARN]: Exception in thread "Thread-19"
- [13:19:22] [Thread-19/WARN]: java.lang.IllegalStateException: Asynchronous block remove!
- [13:19:22] [Thread-19/WARN]: at org.spigotmc.AsyncCatcher.catchOp(AsyncCatcher.java:14)
- [13:19:22] [Thread-19/WARN]: at net.minecraft.server.v1_15_R1.Block.remove(Block.java:396)
- [13:19:22] [Thread-19/WARN]: at net.minecraft.server.v1_15_R1.IBlockData.remove(SourceFile:259)
- [13:19:22] [Thread-19/WARN]: at net.minecraft.server.v1_15_R1.Chunk.setType(Chunk.java:287)
- [13:19:22] [Thread-19/WARN]: at net.minecraft.server.v1_15_R1.World.setTypeAndData(World.java:224)
- [13:19:22] [Thread-19/WARN]: at org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock.setTypeAndData(CraftBlock.java:192)
- [13:19:22] [Thread-19/WARN]: at org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock.setBlockData(CraftBlock.java:177)
- [13:19:22] [Thread-19/WARN]: at org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock.setType(CraftBlock.java:166)
- [13:19:22] [Thread-19/WARN]: at org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock.setType(CraftBlock.java:160)
- [13:19:22] [Thread-19/WARN]: at worldguardstrangemining.WorldGuardStrangeMining.DoWork(WorldGuardStrangeMining.java:62)
- [13:19:22] [Thread-19/WARN]: at java.lang.Thread.run(Unknown Source)
- @EventHandler
- public void onBreak(BlockBreakEvent e)
- {
- e.setCancelled(true);
- e.getBlock.setType(Material.DIAMOND_BLOCK);
- }
-
问题已经解决,感谢用户
https://www.mcbbs.net/?2388218
-
只需确保修改方块的操作在主线程执行即可。可以这样:
- Block block = task.getTargetBlock();
- Bukkit.getScheduler().runTask(getPlugin(), () ->
- {
- block.setType(task.getMaterialAfterBeingChanged());
- });
本帖最后由 William_Shi 于 2020-5-2 14:23 编辑
这不是很正常吗
几乎与mc相关的东西都不是线程安全的
像这种放置方块什么的肯定得放在主线程执行
可以看一下https://www.mcbbs.net/forum.php?mod=viewthread&tid=725571
海螺螺大佬写到了线程安全相关问题
当然这是netty数据传输的例子
但是原理是相通的
你可以使用BukkitRunnable、MinecraftScheduler、BukkitScheduler等多种方式把任务推给主线程
这不是很正常吗
几乎与mc相关的东西都不是线程安全的
像这种放置方块什么的肯定得放在主线程执行
可以看一下https://www.mcbbs.net/forum.php?mod=viewthread&tid=725571
海螺螺大佬写到了线程安全相关问题
关于线程安全
以上接收时的事件全部是在网络线程被触发,所以对于线程不安全的Minecraft来说,线程安全问题需要额外注意。
由于本人对 Forge 的操作并不是很熟练,所以只能以 Bukkit 作为例子,如果你接收到的信息只是一条字符串,并且你只是想将其发送给玩家(Player#sendMessage),那么你可以随意在网络线程中使用,因为这个方法是线程安全的;但是,如果你需要进行踢出(Player#kickPlayer),那么你必须在主线程(Server Thread)进行这个操作,否则可能得到一个报错、崩溃或者意想不到的结果(尽管 Bukkit 会阻止 Async Kick 的行为并发出警告)。
那么,我们可以用以下的方法将数据转交给主线程处理:
使用 Bukkit 或者 Sponge 的调度器
展开 / 收起隐藏文字
Bukkit#getScheduler 返回一个 Bukkit 的调度器,Sponge#getScheduler 返回一个 Sponge 的调度器。
实例如下:
Bukkit 错误的做法:
@Override
public void onPluginMessageReceived(String channel, Player player, byte[] data) {
player.kickPlayer("你因为给服务器发 plugin message 被踢了");
<font color="#808080">// 报错</font>
}
复制代码
Bukkit 的正确做法,将涉及服务器的操作使用调度器交给主线程完成:
@Override
public void onPluginMessageReceived(String channel, Player player, byte[] data) {
Bukkit.getScheduler().runTask(插件实例, () -> player.kickPlayer("被服务器用调度器踢出"));
}
复制代码
Sponge 的调度器操作类似。
利用管道IO
展开 / 收起隐藏文字
管道IO,PipedInputStream 和 PipedOutputStream 用于线程间的通信,主要思想为:
连接管道(PipedOutputStream#connect(PipedInputStream in))-> 线程W写入流(即网络线程) -> 线程R读取流(即Server Thread)
示例用法见帖尾链接
管道流可以用于线程间的通信,使用 byte[] 传输。
PipedInputStream 的 read 方法,如果没有读取到管道流中的数据,将会阻塞线程,请务必注意!
还需要注意的是,Minecraft 使用的网络线程可能有多条,需要额外注意。默认的 Netty IO 线程有 4 条。
当然这是netty数据传输的例子
但是原理是相通的
你可以使用BukkitRunnable、MinecraftScheduler、BukkitScheduler等多种方式把任务推给主线程
本帖最后由 童鞋鞋 于 2020-5-2 14:33 编辑
不能用异步删除块,请使用主线程方式操作
Asynchronous block remove!
William_Shi 发表于 2020-5-2 14:22
这不是很正常吗
几乎与mc相关的东西都不是线程安全的
像这种放置方块什么的肯定得放在主线程执行
问题解决了。谢谢!
但是金粒上限15……有点尴尬……是不是要您多写几条回复