Skip to content
服务器插件与终端接口

命令系统 ​

继承 AbstractProcessCmd 创建自定义命令。服务端会自动扫描并注册 DLL 中所有非抽象的 AbstractProcessCmd 子类。

命名空间差异

  • 社区源码构建(sc2.2network):using Game.Server; 中的 AbstractProcessCmd。
  • 发行 DLL 构建(插件站):using Game.Server.EssentialCmd; 中的 AbstractProcessCmd。

下文示例以发行构建写法为例,使用 Game.Server.EssentialCmd;若你用源码构建,把该 using 改为 Game.Server 即可,其余代码一致。

创建一个命令 ​

csharp
using Game.Server.EssentialCmd;

public class HelloCommand : AbstractProcessCmd
{
    public override string Cmd => "hello";           // 命令名(玩家输入 /hello)
    public override string Introduce => "打招呼";     // 命令介绍
    public override int AuthLevel => 0;              // 0=所有人可用,1=管理员
    public override DisplayType Display => DisplayType.Private;

    public override void ProcessCmd()
    {
        SendMessage("系统", "你好!");
    }
}

命令属性说明 ​

属性类型说明
Cmdstring命令名称,不含 /
Introducestring命令帮助说明
AuthLevelint权限等级:0=所有人,1=管理员
DisplayDisplayType帮助中是否可见(All/Authority/NoDisplay)

获取命令参数 ​

csharp
public override void ProcessCmd()
{
    // m_messageDatas 是参数数组
    // m_messageDatas[0] 是命令本身
    // m_messageDatas[1] 是第一个参数

    if (m_messageDatas.Length < 2)
    {
        SendMessage("系统", "用法: /hello <名字>");
        return;
    }

    string name = m_messageDatas[1];
    SendMessage("系统", $"你好,{name}!");
}

判断是否为控制台 ​

csharp
public override void ProcessCmd()
{
    if (m_isTerminal)
    {
        SendMessage("系统", "控制台不能使用此命令");
        return;
    }

    // 获取发送命令的玩家
    ComponentPlayer player = m_client.PlayerData.ComponentPlayer;
}
  • m_isTerminal:是否为服务端终端触发。
  • m_client:触发命令的客户端对象,从中可取 PlayerData 与 ComponentPlayer。
  • SendMessage(title, message):向触发者发消息,标题通常填 "系统"。它是 AbstractProcessCmd 提供的便捷方法,对应底层 MessagePackage。

完整示例:传送命令 ​

csharp
using System;
using Engine;
using Game.Server.EssentialCmd;
using Game.NetWork;
using Game.NetWork.Packages;

public class TpCommand : AbstractProcessCmd
{
    public override string Cmd => "tp";
    public override string Introduce => "/tp <玩家名> - 传送到玩家身边";
    public override int AuthLevel => 0;
    public override DisplayType Display => DisplayType.Private;

    public override void ProcessCmd()
    {
        // 检查游戏是否加载
        if (GameManager.Project == null)
        {
            SendMessage("系统", "请先进入游戏");
            return;
        }

        // 控制台检查
        if (m_isTerminal)
        {
            SendMessage("系统", "控制台不能传送");
            return;
        }

        // 参数检查
        if (m_messageDatas.Length < 2)
        {
            SendMessage("系统", "用法: /tp <玩家名>");
            return;
        }

        string targetName = m_messageDatas[1];

        // 查找目标玩家
        ComponentPlayer target = FindPlayer(targetName);
        if (target == null)
        {
            SendMessage("系统", $"玩家 {targetName} 不在线");
            return;
        }

        // 获取自己的玩家对象
        ComponentPlayer me = m_client.PlayerData.ComponentPlayer;

        // 获取目标位置
        Vector3 targetPos = target.ComponentBody.Position;

        // 传送
        me.ComponentBody.Position = targetPos;
        CommonLib.Net.QueuePackage(new ComponentPlayerPackage(me, (PlayerAction)12));

        SendMessage("系统", $"已传送到 {targetName}");
    }

    // 查找玩家的辅助方法
    ComponentPlayer FindPlayer(string name)
    {
        var players = GameManager.Project.FindSubsystem<SubsystemPlayers>();
        foreach (var p in players.ComponentPlayers)
        {
            if (p.PlayerData.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
                return p;
        }
        return null;
    }
}

多参数命令 ​

csharp
public override void ProcessCmd()
{
    if (m_messageDatas.Length < 3)
    {
        SendMessage("系统", "用法: /give <玩家> <物品ID> [数量]");
        return;
    }

    string playerName = m_messageDatas[1];

    if (!int.TryParse(m_messageDatas[2], out int itemId))
    {
        SendMessage("系统", "物品ID必须是数字");
        return;
    }

    int count = 1;
    if (m_messageDatas.Length > 3)
    {
        int.TryParse(m_messageDatas[3], out count);
    }

    // 执行给予物品逻辑...
    SendMessage("系统", $"已给予 {playerName} {count} 个物品({itemId})");
}

管理员命令 ​

设置 AuthLevel = 1 限制只有管理员能用:

csharp
public class KickCommand : AbstractProcessCmd
{
    public override string Cmd => "kick";
    public override string Introduce => "/kick <玩家> [原因] - 踢出玩家";
    public override int AuthLevel => 1;  // 只有管理员能用
    public override DisplayType Display => DisplayType.Private;

    public override void ProcessCmd()
    {
        if (m_messageDatas.Length < 2)
        {
            SendMessage("系统", "用法: /kick <玩家> [原因]");
            return;
        }

        string playerName = m_messageDatas[1];
        string reason = m_messageDatas.Length > 2
            ? string.Join(" ", m_messageDatas.Skip(2))
            : "被管理员踢出";

        // 踢人逻辑...
    }
}

常用代码片段 ​

检查游戏状态 ​

csharp
if (GameManager.Project == null)
{
    SendMessage("系统", "请先进入游戏");
    return;
}

获取在线玩家列表 ​

csharp
var players = GameManager.Project.FindSubsystem<SubsystemPlayers>();
foreach (var p in players.ComponentPlayers)
{
    string name = p.PlayerData.Name;
    // 处理每个玩家...
}

解析数字参数 ​

csharp
if (!int.TryParse(m_messageDatas[1], out int number))
{
    SendMessage("系统", "请输入有效的数字");
    return;
}

设计建议 ​

命令类继承 AbstractProcessCmd,设计时至少考虑:

  • AuthLevel 权限等级;
  • DisplayType 是否在帮助中可见;
  • 参数缺失和非法输入;
  • 玩家上下文与控制台上下文(m_isTerminal);
  • 是否支持 GetSuggestions 自动补全;
  • 长耗时任务不能阻塞主循环。

更多事件与命令的"需求→接口"映射见 事件与命令路线图。

参与共建发现错误或有建议?两种零门槛方式,无需本地环境

玩家共建文档 · 原版、插件版、联机版与插件联机版请注意区分