Java实现异步等待指定秒数 适配Minecraft插件开发场景需求
解决方案
核心注意事项
- Minecraft服务端绝大多数API不是线程安全的,直接调用原生异步线程操作Player对象会引发并发异常、数据损坏甚至服务端崩溃,所有和玩家相关的操作必须在服务端主线程执行
- 不要用
Thread.sleep()实现等待,会直接阻塞当前线程,如果在主线程调用会卡住整个服务端
实现步骤
1. 给自定义对象添加状态标记
首先在你的自定义绑定类里添加苏醒状态标记,用来判断玩家是否已经自行苏醒:
// 你的自定义绑定类 public class CustomPlayerData { // 新增苏醒状态标记,默认初始为true private boolean isAwake = true; private final Player player; // 玩家自行苏醒时调用这个方法 public void wakeUpManually() { this.isAwake = true; // 其他自行苏醒的逻辑 player.wakeUp(); } // 你的faint方法 public void faint() { this.isAwake = false; // 此处添加给玩家施加昏迷效果的逻辑 // 接下来实现延迟自动唤醒,以最常用的Spigot/Paper服务端为例 int waitSeconds = 5; // 你要等待的秒数,可自行修改 long delayTicks = waitSeconds * 20L; // 服务端调度单位为游戏刻,1秒=20刻 // Spigot/Paper 通用调度写法 Bukkit.getScheduler().runTaskLater(YourPlugin.getInstance(), () -> { // 延迟结束后先做校验:玩家在线、还没醒,才执行唤醒 if (player.isOnline() && !isAwake) { player.wakeUp(); isAwake = true; // 其他自动唤醒的逻辑 } }, delayTicks); } }
注意:代码中的
YourPlugin.getInstance()需要替换为你自己插件主类的实例,如果你是在主类里写的可以直接传this。
2. 高版本Paper服务端优化写法
如果你使用的是Paper 1.17+版本,更推荐用区域调度器,性能更好:
// 把上面的调度部分替换成这个即可 GlobalRegionScheduler.get().runDelayed(YourPlugin.getInstance(), task -> { if (player.isOnline() && !isAwake) { player.wakeUp(); isAwake = true; } }, delayTicks);
可选:通用Java异步实现(不推荐用于MC插件)
如果你确实需要脱离服务端API实现异步等待,一定要确保最终的玩家操作切回主线程执行,示例如下:
public void faint() { this.isAwake = false; // 施加效果逻辑 int waitSeconds = 5; // 用CompletableFuture实现异步等待,不会阻塞当前线程 CompletableFuture.runAsync(() -> { try { Thread.sleep(waitSeconds * 1000L); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } // 等待结束后必须切回服务端主线程执行玩家操作 Bukkit.getScheduler().runTask(YourPlugin.getInstance(), () -> { if (player.isOnline() && !isAwake) { player.wakeUp(); isAwake = true; } }); }); }
内容的提问来源于stack exchange,提问作者dev-mirzabicer
相关产品推荐
相关产品推荐

