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

Paper插件开发:如何获取玩家执行命令后的输出内容?

问题:捕获Paper服务器插件中玩家命令执行后的所有输出内容

开发Paper服务器插件时,需要获取玩家执行命令后看到的输出内容(例如玩家执行/tpa命令时收到的「传送请求已发送给XXX」提示,而服务器控制台仅显示「用户执行了命令:/tpa」)。目前实现的代码仅能处理/tell和/msg命令,需要改进以支持所有玩家命令的输出捕获。

当前代码实现

package cherry.plugins;

import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerChatEvent;

public class CommandLogger implements Listener {
    private final CherryCostume plugin;

    // 用于在命令输出发送到聊天栏前捕获输出的变量
    private String currentCommandSender;
    private String lastCommandOutput;

    public CommandLogger(CherryCostume plugin) {
        this.plugin = plugin;
        Bukkit.getPluginManager().registerEvents(this, plugin);
    }

    @EventHandler
    public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
        String command = event.getMessage().substring(1); // 移除开头的斜杠
        String playerName = event.getPlayer().getName();

        // 检查命令是否为/tell或/msg类型
        if (command.startsWith("tell") || command.startsWith("msg")) {
            String[] commandParts = command.split(" ", 3);
            if (commandParts.length >= 3) {
                String targetPlayer = commandParts[1]; // 目标玩家
                String message = commandParts[2]; // 命令消息

                // 保存发送命令的玩家,用于后续对比
                currentCommandSender = playerName;

                // 记录执行的命令
                plugin.getLogger().info("执行的命令: " + command);
                plugin.getLogger().info("输出: 已向" + targetPlayer + "发送消息: " + message);
            }
        }
    }

    @EventHandler
    public void onPlayerChat(AsyncPlayerChatEvent event) {
        String message = event.getMessage();

        // 检查消息是否为悄悄话格式
        if (message.startsWith("§7[§8[.*§r")) {
            String sender = event.getPlayer().getName();

            // 仅记录刚执行完命令的玩家发送的聊天消息
            if (sender.equals(currentCommandSender)) {
                // 捕获聊天栏中显示的精确输出
                lastCommandOutput = message;

                // 记录命令生成的聊天栏精确输出
                plugin.getLogger().info("输出: " + lastCommandOutput);
            }
        }
    }
}

改进方案

直接监听玩家接收消息的事件,并结合命令执行前的记录,就能捕获所有命令的输出。具体步骤如下:

1. 替换监听事件

移除原有的AsyncPlayerChatEvent监听,改用Paper API提供的PlayerReceiveMessageEvent——这个事件会捕获所有发送给玩家的消息,包括命令执行后的反馈提示。

2. 使用线程安全的存储记录最近执行命令的玩家

因为命令执行事件和消息接收事件可能在不同线程触发,所以用ConcurrentHashMap存储最近执行命令的玩家及其命令内容,避免线程安全问题,同时设置定时清理机制防止内存泄漏。

3. 改进事件处理逻辑

  • 在PlayerCommandPreprocessEvent中记录所有执行命令的玩家,不再限制命令类型
  • 在PlayerReceiveMessageEvent中检查当前玩家是否刚执行过命令,若是则捕获消息作为命令输出,记录后清除该玩家的标记

改进后的代码

package cherry.plugins;

import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerReceiveMessageEvent;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class CommandLogger implements Listener {
    private final CherryCostume plugin;
    // 存储最近执行命令的玩家:键为玩家名,值为命令内容
    private final ConcurrentHashMap<String, String> recentCommandPlayers = new ConcurrentHashMap<>();

    public CommandLogger(CherryCostume plugin) {
        this.plugin = plugin;
        Bukkit.getPluginManager().registerEvents(this, plugin);
        // 定时清理过期的记录(10秒后移除,可根据需求调整)
        Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> {
            recentCommandPlayers.clear();
        }, 200L, 200L); // 200 ticks = 10秒
    }

    @EventHandler
    public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
        String command = event.getMessage().substring(1);
        String playerName = event.getPlayer().getName();

        // 记录玩家和执行的命令
        recentCommandPlayers.put(playerName, command);
        plugin.getLogger().info("执行的命令: " + command);
    }

    @EventHandler
    public void onPlayerReceiveMessage(PlayerReceiveMessageEvent event) {
        String playerName = event.getPlayer().getName();
        String commandOutput = event.getMessage();

        // 检查该玩家是否刚执行过命令
        if (recentCommandPlayers.containsKey(playerName)) {
            String executedCommand = recentCommandPlayers.remove(playerName);
            // 记录命令和对应的输出
            plugin.getLogger().info("命令[" + executedCommand + "]的输出: " + commandOutput);
        }
    }
}

补充说明

  • 如果某些命令会发送多条输出消息,可调整定时清理的时间,或者修改逻辑收集一段时间内的所有消息再统一记录
  • PlayerReceiveMessageEvent是Paper专属事件,若需要兼容Spigot,可改用PlayerSendMessageEvent(注意Spigot的该事件可能需要额外处理)
  • 定时清理任务的周期可根据实际需求调整,避免无关消息被误捕获

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 18:14:57