Bukkit/Java中如何针对特定玩家注销时取消定时重复任务?
代码无法实现需求,问题及修正方案
你的这段代码完全无法实现「仅取消当前注销玩家对应任务」的需求,核心问题如下:
全局变量
stopRepeater冲突:这个变量是类级别的共享变量,所有玩家共用同一个开关。比如玩家A登录后设为true,玩家B登录也会设为true;当玩家A注销时把它设为false,会直接导致玩家B的任务也停止记录坐标,完全不符合「仅影响当前玩家」的要求。taskID作用域错误:taskID是onLogin方法的局部变量,onQuit方法根本无法访问它——代码连编译都通不过。就算强行调整作用域,它也只会保存最后一个登录玩家的任务ID,注销时取消的是最后登录玩家的任务,不是当前注销玩家的。多余的离线判断:
PlayerQuitEvent触发时,玩家已经处于离线状态,!thePlayer.isOnline()这个条件完全多余,永远为真。
正确实现方案
要实现每个玩家任务独立管理,需要用一个映射表把玩家和对应的任务ID绑定起来。以下是修正后的代码:
1. 定义成员变量存储玩家与任务的映射
// 用HashMap存储每个玩家对应的重复任务ID private final Map<Player, Integer> playerTaskMap = new HashMap<>();
2. 登录事件:创建任务并绑定玩家
@EventHandler public void onLogin(final PlayerJoinEvent event) { final Player thePlayer = event.getPlayer(); final Location playerSpawnLocation = thePlayer.getLocation(); getLogger().info(thePlayer.getName() + " 正在登录!"); getLogger().info("欢迎 " + thePlayer.getName() + "。你的当前位置是:" + playerSpawnLocation); // 调度重复任务,每20tick(1秒)记录一次坐标 int taskID = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> { // 直接记录坐标,无需全局开关 this.logToFile(thePlayer, thePlayer.getLocation()); }, 0L, 20L); // 将玩家和任务ID存入映射表 playerTaskMap.put(thePlayer, taskID); }
3. 注销事件:取出任务ID并取消
@EventHandler public void onQuit(final PlayerQuitEvent event) { Player thePlayer = event.getPlayer(); // 从映射表中移除并获取当前玩家的任务ID Integer taskID = playerTaskMap.remove(thePlayer); if (taskID != null) { // 取消该玩家对应的重复任务 Bukkit.getScheduler().cancelTask(taskID); getLogger().info(thePlayer.getName() + " 已退出游戏,坐标记录任务已停止"); } else { getLogger().info(thePlayer.getName() + " 已退出游戏,未找到对应的记录任务"); } }
额外注意事项
- 如果
logToFile是IO操作(写文件),建议使用scheduleAsyncRepeatingTask替代scheduleSyncRepeatingTask,避免阻塞游戏主线程。 - 可以考虑在插件禁用时,遍历映射表取消所有剩余任务,防止内存泄漏。
内容的提问来源于stack exchange,提问作者Caledonian26
相关产品推荐
相关产品推荐

