You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bukkit插件Scoreboard配置scheduler后仍不更新且控制台报错求助

问题根因

  1. 你创建了damage团队,但没有将团队对应的条目添加到计分板的显示列表中,反而直接用了固定文本的伤害行,导致团队本身没有和计分板绑定,后续更新的是团队前缀自然不会生效。
  2. 每次调用showScoreboard方法都会生成新的计分板、重复注册定时任务,同一个玩家可能对应多个定时任务同时运行,当玩家计分板被替换后,旧的定时任务访问旧计分板的团队就会抛出空异常。
  3. 没有对定时任务做生命周期管理,玩家退出服务器后任务仍会继续运行,访问离线玩家的计分板也会触发异常。

修复方案

调整核心逻辑,删除固定文本的伤害行、改用团队对应的条目作为显示项,新增任务ID缓存避免重复注册,简化异常处理逻辑:

public class PlayerScoreboard {
    private static Plugin plugin;
    // 存储玩家对应的定时任务ID,避免重复注册
    private static final Map<UUID, Integer> taskMap = new HashMap<>();

    public static void setPlugin(Plugin plugin) {
        PlayerScoreboard.plugin = plugin;
    }

    public static void showScoreboard(Player player) {
        ScoreboardManager manager = Bukkit.getScoreboardManager();
        if (manager == null) return;
        Scoreboard board = manager.getNewScoreboard();

        Objective objective = board.registerNewObjective("servername", "dummy", ChatColor.translateAlternateColorCodes('&', "&e&lRAY'S &b&lSERVER"));
        objective.setDisplaySlot(DisplaySlot.SIDEBAR);

        objective.getScore("————————————————").setScore(4);
        objective.getScore("").setScore(3);
        // 替换原有固定伤害行为团队对应条目
        String damageEntry = ChatColor.DARK_RED + "" + ChatColor.BLUE;
        objective.getScore(damageEntry).setScore(2);
        objective.getScore(" ").setScore(1);

        Team damage = board.registerNewTeam("damage");
        damage.addEntry(damageEntry);
        damage.setPrefix(ChatColor.RED + "Damage dealt: " + ChatColor.GREEN + player.getStatistic(Statistic.DAMAGE_DEALT));

        player.setScoreboard(board);

        // 取消玩家之前的旧任务,避免重复运行
        UUID playerUuid = player.getUniqueId();
        if (taskMap.containsKey(playerUuid)) {
            Bukkit.getScheduler().cancelTask(taskMap.get(playerUuid));
        }
        // 注册新任务并存储ID
        int taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> {
            // 玩家离线则清理任务
            if (!player.isOnline()) {
                Bukkit.getScheduler().cancelTask(taskMap.get(playerUuid));
                taskMap.remove(playerUuid);
                return;
            }
            updateDamage(player);
        }, 0L, 20L);
        taskMap.put(playerUuid, taskId);
    }

    public static void updateDamage(Player player) {
        Scoreboard board = player.getScoreboard();
        Team team = board.getTeam("damage");
        if (team == null) {
            // 团队不存在直接重建,避免报错
            team = board.registerNewTeam("damage");
            String damageEntry = ChatColor.DARK_RED + "" + ChatColor.BLUE;
            team.addEntry(damageEntry);
        }
        team.setPrefix(ChatColor.RED + "Damage dealt: " + ChatColor.GREEN + player.getStatistic(Statistic.DAMAGE_DEALT));
    }

    // 玩家退出时调用,清理无效任务
    public static void onPlayerQuit(Player player) {
        UUID uuid = player.getUniqueId();
        if (taskMap.containsKey(uuid)) {
            Bukkit.getScheduler().cancelTask(taskMap.get(uuid));
            taskMap.remove(uuid);
        }
    }
}

补充说明

在插件的玩家退出事件监听器中调用PlayerScoreboard.onPlayerQuit(player),可以避免内存泄漏和无效任务占用服务器资源。

内容的提问来源于stack exchange,提问作者amxyargaming

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 03:27:05