Skip to content

通信协议

本文参照《RoboMaster 2026 机甲大师高校系列赛通信协议 V2.0.0(20260626)》编写,包含机器人侧串口协议和自定义客户端协议。

RMUC 与 RMUL 共用的兵种、机制和场地道具适用本协议;仅属于某一赛项的条目不自动适用于另一赛项。

版本与文档

注意:此为2026赛季通信协议文档,等待27赛季通信协议发布

1 串口协议

1.1 串口协议格式

串口统一使用 8 位数据位、1 位停止位、无硬件流控、无校验位(8N1)。

物理接口 波特率
电源管理模块 ↔ 机器人 115200
自定义控制器 ↔ 选手端 115200
图传发送端 ↔ 机器人 921600
帧组成 长度 说明
frame_header 5 byte 帧头,含 CRC8
cmd_id 2 byte 命令码
data n byte 命令对应的数据段
frame_tail 2 byte CRC16,整包校验

完整帧长为 n + 9 byte,data_length 仅表示 data 长度。

帧头字段 字节偏移 长度 说明
SOF 0 1 固定为 0xA5
data_length 1 2 数据段长度
seq 3 1 包序号
CRC8 4 1 帧头校验

三类数据链路:

链路 数据路径 / 接口
常规链路 裁判系统服务器与主控模块转发,经电源管理模块的 User 串口收发
图传链路 选手端与图传模块转发,经图传发送端串口与机器人交互
雷达无线链路 裁判系统信号发射源发送电磁波,由雷达接收并解析

通信性能参考:正常情况下延迟约 130 ms、丢包率小于 1%;网络较差时延迟约 200 ms、丢包率约 3%。这些是参考测量值,并非实时性保证。

实现建议:接收端先寻找 0xA5,收齐帧头并检查 CRC8,再根据长度收齐完整帧并检查 CRC16,最后按命令码解析;同时处理半包、粘包、超长长度和校验失败后的重新同步。CRC 参数见附录一。

1.2 命令码 ID 和常规链路数据

长度单位均为 byte,表示数据段长度。标有“上限”的频率为发送限制。

命令码 长度 用途 发送方 → 接收方 频率
0x0001 11 比赛状态 服务器 → 全体机器人 1 Hz
0x0002 1 比赛结果 服务器 → 全体机器人 比赛结束触发
0x0003 20 机器人血量及双方伤害差 服务器 → 全体机器人 3 Hz
0x0101 4 场地事件 服务器 → 己方全体机器人 1 Hz
0x0104 3 裁判警告 服务器 → 被判罚方全体机器人 判罚/判负触发,其余 1 Hz
0x0105 3 飞镖发射相关数据 服务器 → 己方全体机器人 1 Hz
0x0201 17 机器人性能体系 主控模块 → 对应机器人 10 Hz
0x0202 14 底盘缓冲能量和射击热量 主控模块 → 对应机器人 10 Hz
0x0203 16,见下方差异说明 机器人位置 主控模块 → 对应机器人 1 Hz
0x0204 8 增益与底盘能量 服务器 → 对应机器人 3 Hz
0x0206 1 伤害状态 主控模块 → 对应机器人 伤害发生后
0x0207 7 实时射击数据 主控模块 → 对应机器人 弹丸发射后
0x0208 8 允许发弹量与剩余金币 服务器 → 己方英雄、步兵、哨兵、空中机器人 10 Hz
0x0209 5 RFID 模块状态 服务器 → 己方装有 RFID 模块的机器人 3 Hz
0x020A 6 飞镖选手端指令 服务器 → 己方飞镖 3 Hz
0x020B 40 地面机器人位置 服务器 → 己方哨兵 1 Hz
0x020C 2 雷达标记、激光瞄准和反制状态 服务器 → 己方雷达 1 Hz
0x020D 14 哨兵自主决策信息同步 服务器 → 己方哨兵 1 Hz
0x020E 1 雷达自主决策信息同步 服务器 → 己方雷达 1 Hz
0x0301 最大 118 机器人交互、UI、自主决策 按子内容 ID 和接收者 ID 路由 上限 30 Hz
0x0303 15,见下方差异说明 小地图交互下发 选手端 → 服务器 → 选定己方机器人 触发,另有重复发送机制
0x0305 48 雷达向小地图上传坐标 雷达 → 服务器 → 己方所有选手端 上限 5 Hz
0x0307 103,见下方差异说明 小地图路径 哨兵/半自动控制机器人 → 对应选手端 上限 1 Hz
0x0308 34 小地图自定义消息 己方机器人 → 己方选手端 上限 3 Hz

长度待核对:0x0203、0x0303、0x0307 的标注长度分别为 16、15、103 byte,而对应字段及结构体分别合计为 12、12、105 byte。接入时需核对实际裁判系统数据,确认数据段长度后再解析。

常规状态数据关键字段

命令码 字段与解析要点
0x0001 首字节低 4 位为比赛类型,高 4 位为比赛阶段;偏移 1 为 2 字节阶段剩余秒数,偏移 3 为 8 字节 UNIX 时间,连接裁判系统 NTP 后生效
0x0002 0=平局,1=红方胜,2=蓝方胜
0x0003 10 个 2 字节字段:己方 1、2、3、4 号血量,双方总伤害差,己方 7 号、前哨站、基地血量,对方前哨站、基地血量;伤害差为 int16_t,其余为 uint16_t
0x0101 补给区、能量机关、高地、飞镖命中、中心增益点、堡垒及前哨站增益点等位域
0x0104 判罚等级、违规机器人 ID、对应判罚等级累计次数
0x0105 飞镖发射剩余时间,以及命中目标、累计命中次数、选定目标等位域
0x0201 ID、等级、当前/最大血量、热量冷却值、热量上限、底盘功率上限;偏移 12 为 float bullet_speed_limit,偏移 16 的 bit 0~2 为云台/底盘/发射机构供电状态
0x0202 前 8 字节为保留位;偏移 8、10、12 分别为 2 字节缓冲能量(J)、17 mm 热量、42 mm 热量
0x0203 包含 float x、float y(m)和 float angle(度,正北为 0)
0x0204 回血、热量冷却、防御、负防御、攻击增益,以及剩余能量阈值位图;冷却增益为直接值,其余上述增益为百分比
0x0206 装甲 ID 与扣血原因;属于本地判定,实际是否扣血仍取决于规则
0x0207 弹丸类型、发射机构 ID、射频(Hz)、弹丸初速度(m/s)
0x0208 17 mm 允许发弹量、42 mm 允许发弹量、剩余金币、堡垒储备允许发弹量
0x0209 RFID 场地交互点状态位图
0x020A 飞镖闸门状态、保留位、切换目标时间、最近确认发射时间
0x020B 己方英雄、工程、3/4 号步兵的 x/y 坐标和保留字段
0x020C bit 0~5 为对方 1/2/3/4/6/7 号标记状态,bit 6~11 为己方对应状态;bit 12~15 为双方空中机器人的激光瞄准与反制状态
0x020D uint32_t sentry_info、uint16_t sentry_info_2、uint64_t sentry_info_3:兑换/复活状态、姿态与强化标记、各姿态剩余时长
0x020E bit 0~1 为双倍易伤机会数,bit 2 为是否生效,bit 3~4 为己方加密等级,bit 5 为能否修改密钥

0x0301 机器人交互与 UI

数据段前 6 字节依次为 uint16_t data_cmd_id、uint16_t sender_id、uint16_t receiver_id,之后是最多 112 字节内容。完整包不超过 127 字节,所有子内容共享 30 Hz 上行频率上限。

每 1000 ms,英雄、工程、步兵、空中机器人和飞镖可接收数据上限为 3720 字节,雷达和哨兵为 5120 字节。机器人间仅限己方且需满足规则允许的接收者范围;发送到选手端时,除雷达外只能发送到自身对应的选手端,雷达可发送到任意己方选手端。

子内容 ID 内容长度(不含 6 字节交互头) 用途
0x0200~0x02FF 最大 112 机器人之间通信
0x0100 2 删除图层:操作类型与图层号
0x0101 15 绘制 1 个图形
0x0102 30 绘制 2 个图形
0x0103 75 绘制 5 个图形
0x0104 105 绘制 7 个图形
0x0110 45 15 字节字符配置与 30 字节字符数据
0x0120 4 哨兵自主决策指令
0x0121 8 雷达自主决策指令

UI 单个图形由 3 字节图形名和三组 4 字节配置组成。配置包含操作、图形类型、图层、颜色、线宽、起点及类型相关参数。图层为 0~9;图形名用于修改和删除索引。图形坐标原点在屏幕左下角,右上角为 (1920, 1080);角度 0° 为 12 点钟方向,顺时针绘制。浮点显示值为所传 32 位整型数除以 1000。

0x0120 的 32 位哨兵指令:

位 含义
bit 0 确认复活
bit 1 确认兑换立即复活
bit 2~12 在补血点兑换的累计发弹量请求,开局为 0,必须单调递增
bit 13~16 远程兑换发弹量请求次数,开局为 0,每次递增 1
bit 17~20 远程兑换血量请求次数,开局为 0,每次递增 1
bit 21~23 姿态:1/2/3 为进攻/防御/移动,4/5/6 为对应强化姿态,默认 3
bit 24 确认使能量机关进入正在激活状态
bit 25~31 保留

服务器从低位到高位依次处理,遇到不能处理的指令会停止处理本组后续指令。

0x0121 包含 1 字节双倍易伤请求计数、1 字节密钥指令类型和 6 字节密钥。请求计数从 0 开始、每次递增 1;密钥指令 1 为更新己方密钥,2 为提交破解的对方密钥。只有开局或对方破解成功导致己方加密等级提高时可修改己方密钥;提交验证密钥后 10 秒内再次更新无效。

1.3 小地图交互数据

  • 0x0303 下发标记:云台手两次操作间隔至少 0.5 s;半自动机器人对应操作手两次操作间隔至少 3 s。字段为目标 x/y(m)、键盘键值、对方机器人 ID、信息来源 ID。发送目标 ID 时坐标为 0,发送坐标时目标 ID 为 0。
  • 重复发送机制:一次触发后额外按 100 ms 间隔发送 4 次,共 5 次;之后以 1 Hz 持续发送最近一次内容,直到下一次触发。接收端应考虑重复指令处理。
  • 0x0305 雷达坐标:按对方 1/2/3/4/6/7 号、己方 1/2/3/4/6/7 号顺序发送,每台机器人 x/y 各 2 字节,单位 cm,共 48 字节。x、y 都为 0 表示未发送该机器人坐标,越界坐标显示在边缘。
  • 0x0307 路径:意图 1=攻击、2=防守、3=移动;起点 x/y 为分米,后跟各 49 个 int8_t 增量和发送者 ID。串口表说明增量相对上一个点计算,共形成 49 个新点。小地图左下角为原点,向右为 X 正方向,向上为 Y 正方向。
  • 0x0308 自定义消息:发送者和接收者 ID 各 2 字节,字符区 30 字节,采用 UTF-16,支持中文,需注意编码字节序。

1.4 图传链路数据

命令码 最大数据长度 方向 频率上限 客户端对应消息
0x0302 30 byte 自定义控制器 → 选手端图传连接的机器人 30 Hz —
0x0309 30 byte 机器人 → 对应选手端连接的自定义控制器 10 Hz —
0x0310 300 byte 机器人 → 图传链路 → 自定义客户端 50 Hz CustomByteBlock
0x0311 30 byte 自定义客户端 → 图传链路 → 机器人 75 Hz CustomControl

自定义客户端与机器人交互协议无重传机制。0x0310 的 300 byte 与客户端所写的 2.4 kbit 对应;不要将其与下行 0x0311 的 30 byte 混用。

1.5 非链路数据

0x0306 用于自定义控制器模拟键鼠操作选手端,数据长 8 byte,发送频率上限 30 Hz。

字节偏移 长度 内容
0 2 两个通用键值,各占 1 字节,支持 2 键无冲
2 2 bit 0~11:鼠标 X;bit 12~15:左键状态
4 2 bit 0~11:鼠标 Y;bit 12~15:右键状态
6 2 保留

这里使用绝对像素坐标,左上角为 (0, 0),与 UI 绘图的原点不同。鼠标状态 1 为按下,其余为未按下。一次移动点击依次发送“目标位置未按下 → 同位置按下 → 同位置未按下”。无新按键信息时保持上一帧按下状态;鼠标信息仅在鼠标图标出现时响应。

1.6 雷达无线链路数据

数据由信号发射源发送至雷达,以下消息均以 10 Hz 持续发送。

命令码 长度 内容与排列
0x0A01 24 对方 1/2/3/4/6/7 号位置,x/y 各 2 字节,单位 cm
0x0A02 12 对方 1/2/3/4 号血量、2 字节保留位、7 号血量,各项 2 字节
0x0A03 10 对方 1/3/4/6/7 号允许发弹量,各 2 字节
0x0A04 8 剩余金币 2 字节、累计金币 2 字节、场地交互状态位图 4 字节
0x0A05 41 对方 1/2/3/4/7 号增益、哨兵姿态与各机器人主要状态
0x0A06 6 对方干扰波密钥,各字节为 ASCII 字母或数字

0x0A05 前 35 字节按 1/2/3/4/7 号各占 7 字节排列:回血增益 1 字节、热量冷却增益 2 字节、防御增益 1 字节、负防御增益 1 字节、攻击增益 2 字节。偏移 35 为哨兵姿态(1~6,含强化姿态);偏移 36~40 为上述五台机器人的主要状态:0=存活、1=战亡、2=无敌但不虚弱、3=无敌且虚弱。

2 自定义客户端协议

概述

自定义客户端的数据流格式为 Protobuf v3(Protocol Buffers,支持 3.15 以上版本),采用 MQTT 作为发布/订阅消息传输协议。MQTT 直接传输 Protobuf 序列化的二进制流。topic 为对应的指令名。

  • 服务器 IP 地址:192.168.12.1
  • 端口:3333
  • 自定义客户端 IP 地址:192.168.12.2(固定地址)

通信流程

  1. 按照下文的 Protobuf 消息格式编写 .proto 文件,用 protoc 生成不同语言的类代码;
  2. 在发布者端,将对象序列化成二进制;
  3. 通过 MQTT publish 把二进制发送到对应 Topic;
  4. 在订阅者端,接收消息并用 Protobuf 反序列化。

示例代码见附录三,其中 MyMqttClient 的 clientID 需填入所连接机器人对应的 ID 编号。

图传码流数据

自定义客户端可通过 UDP 监听 3334 端口 获取图传码流数据。帧率为 60 fps,分辨率为 1920 × 1080,编码格式为 HEVC / H.265,无重传机制。每个 UDP 包的码流数据前 8 个字节固定为:

  • 帧编号(递增):2 byte
  • 当前帧内分片序号:2 byte
  • 当前帧总字节数:4 byte

这 8 字节按大端传输。每个 UDP 包最大 1400 字节,去掉头部后码流数据最多 1392 字节。


2.1 指令概览

指令(message)名 指令用途 发送方/接收方 最高 qos 等级 频率
KeyboardMouseControl 传输鼠标键盘输入 自定义客户端 → 图传链路 → 机器人 1 75 Hz
CustomControl 最大 30 字节的自定义数据 自定义客户端 → 图传链路 → 机器人 1 75 Hz
GameStatus 同步比赛全局状态信息 服务器 → 自定义客户端 1 5 Hz
GlobalUnitStatus 同步基地、前哨站和所有机器人状态 服务器 → 自定义客户端 1 1 Hz
GlobalLogisticsStatus 同步全局后勤信息 服务器 → 自定义客户端 1 1 Hz
GlobalSpecialMechanism 同步正在生效的全局特殊机制 服务器 → 自定义客户端 1 1 Hz
Event 全局事件通知 服务器 → 自定义客户端 1 触发式发送
RobotInjuryStat 机器人一次存活期间累计受伤统计 服务器 → 自定义客户端 1 1 Hz
RobotRespawnStatus 机器人复活状态同步 服务器 → 自定义客户端 1 1 Hz
RobotStaticStatus 机器人固定属性和配置 服务器 → 自定义客户端 1 1 Hz
RobotDynamicStatus 机器人实时数据 服务器 → 自定义客户端 1 10 Hz
RobotModuleStatus 机器人各模块运行状态 服务器 → 自定义客户端 1 1 Hz
RobotPosition 机器人空间坐标和朝向 服务器 → 自定义客户端 1 1 Hz
Buff Buff 效果信息 服务器 → 自定义客户端 1 获得增益时触发发送,此后 1 Hz 定频发送直到失去增益
PenaltyInfo 判罚信息同步 服务器 → 自定义客户端 1 触发式发送
RobotPathPlanInfo 哨兵轨迹规划信息 服务器 → 自定义客户端 1 触发式发送
MapClickInfo 地图点击标记信息同步 服务器 → 自定义客户端 1 触发式发送,限制见 1.3 小地图交互数据
MapClickCmd 地图点击标记指令 自定义客户端 → 服务器 1 触发式发送,限制见 1.3 小地图交互数据
RadarInfoToClient 雷达发送的机器人位置信息 服务器 → 自定义客户端 1 触发式发送
CustomByteBlock 机器人自定义上传数据流(对应 0x0310) 机器人 → 图传链路 → 自定义客户端 1 50 Hz
AssemblyCommand 工程装配指令 自定义客户端 → 服务器 1 触发式发送,最高 10 Hz
TechCoreMotionStateSync 科技核心运动状态同步 服务器 → 自定义客户端 1 1 Hz
RobotPerformanceSelectionCommand 地面机器人选择性能体系或控制方式 自定义客户端 → 服务器 1 触发式发送,最高 10 Hz
RobotPerformanceSelectionSync 步兵/英雄性能体系状态同步 服务器 → 自定义客户端 1 1 Hz
CommonCommand 机器人多种常用指令 自定义客户端 → 服务器 1 触发式发送,最高 10 Hz
HeroDeployModeEventCommand 英雄部署模式相关指令 自定义客户端 → 服务器 1 触发式发送,最高 10 Hz
DeployModeStatusSync 英雄部署模式状态同步 服务器 → 自定义客户端 1 1 Hz
RuneActivateCommand 能量机关激活指令 自定义客户端 → 服务器 1 触发式发送,最高 10 Hz
RuneStatusSync 能量机关状态同步 服务器 → 自定义客户端 1 1 Hz
SentryStatusSync 哨兵姿态相关信息同步 服务器 → 自定义客户端 1 1 Hz
DartCommand 飞镖控制指令 自定义客户端 → 服务器 1 触发式发送,最高 10 Hz
DartSelectTargetStatusSync 飞镖目标选择状态同步 服务器 → 自定义客户端 1 1 Hz
SentryCtrlCommand 哨兵控制指令请求 自定义客户端 → 服务器 1 触发式发送,最高 10 Hz
SentryCtrlResult 哨兵控制指令结果反馈 服务器 → 自定义客户端 1 触发式发送
AirSupportCommand 空中支援指令 自定义客户端 → 服务器 1 触发式发送
AirSupportStatusSync 空中支援状态反馈 服务器 → 自定义客户端 1 1 Hz

2.2 详细协议定义

以下消息定义使用 optional 声明可选字段,创建 .proto 文件时需添加 syntax = "proto3";。消息名同时作为 MQTT Topic,字段编号、类型和方向应与对应定义一致。

2.2.1 KeyboardMouseControl

用途:传输鼠标键盘输入。该数据将通过相机图传模块(接收端)发送给机器人。实际比赛时,操作手需提前选择键鼠控制指令来源为官方选手端或自定义客户端,同时仅能通过一个来源生成发送给机器人的键鼠指令。

数据编号 数据类型 数据用途
1 int32 鼠标 x 轴移动速度,负值标识向左移动
2 int32 鼠标 y 轴移动速度,负值标识向下移动
3 int32 鼠标滚轮移动速度,负值标识向后滚动
4 bool 左键是否按下(false=抬起, true=按下)
5 bool 右键是否按下(false=抬起, true=按下)
6 uint32 键盘按键位掩码(bit0~bit15)
7 bool 中键是否按下(false=抬起, true=按下)

键盘按键位掩码(每个 bit 对应一个按键,0=未按下,1=按下):

  • bit0 : W
  • bit1 : S
  • bit2 : A
  • bit3 : D
  • bit4 : Shift
  • bit5 : Ctrl
  • bit6 : Q
  • bit7 : E
  • bit8 : R
  • bit9 : F
  • bit10: G
  • bit11: Z
  • bit12: X
  • bit13: C
  • bit14: V
  • bit15: B
message KeyboardMouseControl {
    optional int32 mouse_x = 1;
    optional int32 mouse_y = 2;
    optional int32 mouse_z = 3;
    optional bool left_button_down = 4;
    optional bool right_button_down = 5;
    optional uint32 keyboard_value = 6;
    optional bool mid_button_down = 7;
}

2.2.2 CustomControl

用途:最大 30 字节的自定义数据。该信息将通过图传链路以 0x0311 命令字发送给机器人。

数据编号 数据类型 数据用途
1 bytes 最大 30 字节的自定义数据
message CustomControl {
    optional bytes data=1;
}

2.2.3 GameStatus

用途:同步比赛全局状态信息。

数据编号 数据类型 数据用途
1 uint32 当前局号(从 1 开始)
2 uint32 总局数
3 uint32 红方得分
4 uint32 蓝方得分
5 uint32 当前阶段(枚举值见下表)
6 int32 当前阶段剩余时间(秒)
7 int32 当前阶段已过时间(秒)
8 bool 是否暂停
9 uint32 当局胜者:0=平局,1=红方,2=蓝方;仅结算阶段有意义,其余发送 255
10 uint32 比赛结束原因;仅结算阶段有意义,其余发送 255

current_stage 枚举值:

  • 0:未开始比赛
  • 1:准备阶段
  • 2:十五秒裁判系统自检阶段
  • 3:五秒倒计时
  • 4:比赛中
  • 5:比赛结算中

end_reason 枚举值:1=基地被摧毁,2=基地剩余血量,3=前哨站剩余血量,4=前哨站是否被击毁过,5=全队总攻击伤害,6=全队机器人总剩余血量,7=平局,8=异常终止,9=主裁判手动判定胜负。

message GameStatus {
    optional uint32 current_round = 1;
    optional uint32 total_rounds = 2;
    optional uint32 red_score = 3;
    optional uint32 blue_score = 4;
    optional uint32 current_stage = 5;
    optional int32 stage_countdown_sec = 6;
    optional int32 stage_elapsed_sec = 7;
    optional bool is_paused = 8;
    optional uint32 game_result = 9;
    optional uint32 end_reason =10;
}

2.2.4 GlobalUnitStatus

用途:同步基地、前哨站和所有机器人状态。

数据编号 数据类型 数据用途
1 uint32 己方基地当前血量(0=已摧毁)
2 uint32 己方基地状态(枚举值见下)
3 uint32 己方基地当前护盾值(0=无护盾)
4 uint32 己方前哨站当前血量(0=已摧毁)
5 uint32 己方前哨站状态(枚举值见下)
6 uint32 对方基地当前血量(0=已摧毁)
7 uint32 对方基地状态
8 uint32 对方基地当前护盾值(0=无护盾)
9 uint32 对方前哨站当前血量(0=已摧毁)
10 uint32 对方前哨站状态
11 repeated uint32 所有机器人血量(己方 1/2/3/4/7 号,再对方 1/2/3/4/7 号)
12 repeated int32 己方机器人剩余累计发弹量
13 uint32 己方累计总伤害
14 uint32 对方累计总伤害

base_status 枚举值:

  • 0:无敌
  • 1:解除无敌,护甲未展开
  • 2:解除无敌,护甲展开

outpost_status 枚举值:

  • 0:无敌
  • 1:存活,解除无敌,中部装甲旋转
  • 2:存活,解除无敌,中部装甲停转
  • 3:被击毁,不可重建
  • 4:被击毁,可重建
  • 5:被击毁,重建中
message GlobalUnitStatus {
    optional uint32 base_health = 1;
    optional uint32 base_status = 2;
    optional uint32 base_shield = 3;
    optional uint32 outpost_health = 4;
    optional uint32 outpost_status = 5;
    optional uint32 enemy_base_health = 6;
    optional uint32 enemy_base_status = 7;
    optional uint32 enemy_base_shield=8;
    optional uint32 enemy_outpost_health = 9;
    optional uint32 enemy_outpost_status = 10;
    repeated uint32 robot_health = 11;
    repeated int32 robot_bullets = 12;
    optional uint32 total_damage_ally = 13;
    optional uint32 total_damage_enemy = 14;
}

2.2.5 GlobalLogisticsStatus

用途:同步全局后勤信息。

数据编号 数据类型 数据用途
1 uint32 己方当前经济
2 uint64 己方累计总经济
3 uint32 己方科技等级(工程机器人曾装配最高难度)
4 uint32 己方加密等级(雷达解析信息波机制)
message GlobalLogisticsStatus {
    optional uint32 remaining_economy = 1;
    optional uint64 total_economy_obtained = 2;
    optional uint32 tech_level = 3;
    optional uint32 encryption_level = 4;
}

2.2.6 GlobalSpecialMechanism

用途:同步正在生效的全局特殊机制。

数据编号 数据类型 数据用途
1 repeated uint32 正在生效的机制 ID 列表
2 repeated int32 对应的时间参数(秒)

mechanism_id 枚举值:

  • 1:己方堡垒被对方占领计时
  • 2:对方堡垒被己方占领计时
message GlobalSpecialMechanism {
    repeated uint32 mechanism_id = 1;
    repeated int32 mechanism_time_sec = 2;
}

2.2.7 Event

用途:全局事件通知。

数据编号 数据类型 数据用途
1 int32 事件编号(枚举值见下)
2 string 事件参数(含义随 event_id 变化)

event_id 枚举值:

  • 1:击杀事件,参数依次为被击杀者、击杀者,如 1,101 表示蓝方英雄击毁红方英雄。
  • 2:前哨站被摧毁,参数为被击毁的目标 ID,ID 差异见下。
  • 3:大能量机关成功激活灯臂数、平均环数,仅大能量机关激活时触发。
  • 4:能量机关进入已激活状态,参数 1=小能量机关、2=大能量机关。
  • 5:己方英雄造成狙击伤害,参数为累计狙击伤害数量。
  • 6:对方英雄造成狙击伤害,参数为累计狙击伤害数量。
  • 7:对方呼叫空中支援,无参数。
  • 8:对方空中支援被反制,参数为己方剩余可反制次数。
  • 9:飞镖命中,参数一为命中方(1=红方、2=蓝方),参数二为目标(1=前哨站、2=基地固定目标、3=基地随机固定目标、4=基地随机移动目标、5=基地末端移动目标)。
  • 10:对方飞镖闸门开启,无参数。
  • 11:基地遭到攻击,无参数,内置冷却 5 s。
  • 12:对方前哨站停转,无参数。
  • 13:对方基地护甲展开,无参数。
  • 14:装配过程中对方请求四级装配,进入强制退出缓冲期,无参数。
  • 15:装配结果,参数值见下。

V2.0.0 补充:事件 3 的参数为激活灯臂数和平均环数,例如 10,9.6;事件 9 的命中目标还包括 5=基地末端移动目标;事件 15 的结果为 0=成功、1=拔出、2=超时、3=离开装配区过久、4=工程战亡、5=四级协作超时、6=主动退出、7=结算未检测到能量单元、8=强制退出缓冲期到期。事件 2 的前哨站 ID 标注为 11/111,与附录二的 10/110 不一致,接入时需核对。

message Event {
    optional int32 event_id = 1;
    optional string param = 2;
}

2.2.8 RobotInjuryStat

用途:机器人一次存活期间累计受伤统计。

数据编号 数据类型 数据用途
1 uint32 该次存活累计受伤总计
2 uint32 撞击伤害
3 uint32 17mm 弹丸伤害
4 uint32 42mm 弹丸伤害
5 uint32 飞镖溅射伤害
6 uint32 模块离线扣血
7 uint32 异常离线扣血
8 uint32 判罚扣血
9 uint32 服务器强制使其战亡扣血
10 uint32 击杀者 ID(若未检测到击杀者,值为 0)
message RobotInjuryStat {
    optional uint32 total_damage = 1;
    optional uint32 collision_damage = 2;
    optional uint32 small_projectile_damage = 3;
    optional uint32 large_projectile_damage = 4;
    optional uint32 dart_splash_damage = 5;
    optional uint32 module_offline_damage = 6;
    optional uint32 offline_damage = 7;
    optional uint32 penalty_damage = 8;
    optional uint32 server_kill_damage = 9;
    optional uint32 killer_id = 10;
}

2.2.9 RobotRespawnStatus

用途:机器人复活状态同步。

数据编号 数据类型 数据用途
1 bool 是否处于待复活状态
2 uint32 复活所需总读条
3 uint32 当前复活读条进度
4 bool 是否可以免费复活
5 uint32 花费金币复活所需金币数
6 bool 是否允许花费金币复活
message RobotRespawnStatus {
    optional bool is_pending_respawn = 1;
    optional uint32 total_respawn_progress = 2;
    optional uint32 current_respawn_progress = 3;
    optional bool can_free_respawn = 4;
    optional uint32 gold_cost_for_respawn = 5;
    optional bool can_pay_for_respawn = 6;
}

2.2.10 RobotStaticStatus

用途:机器人固定属性和配置。

数据编号 数据类型 数据用途
1 uint32 连接状态(0=未连接,1=已连接)
2 uint32 上场状态(0=已上场,1=未上场)
3 uint32 存活状态(0=未知,1=存活,2=战亡)
4 uint32 机器人编号(见附录二)
5 uint32 机器人类型
6 uint32 性能体系-发射机构(枚举值见下)
7 uint32 性能体系-底盘(枚举值见下)
8 uint32 当前等级
9 uint32 最大血量
10 uint32 最大热量
11 float 热量冷却速率(每秒)
12 uint32 最大功率
13 uint32 最大缓冲能量
14 uint32 最大底盘能量

performance_system_shooter 枚举值:

  • 1:冷却优先
  • 2:爆发优先
  • 3:英雄近战优先
  • 4:英雄远程优先

performance_system_chassis 枚举值:

  • 1:血量优先
  • 2:功率优先
  • 3:英雄近战优先
  • 4:英雄远程优先
message RobotStaticStatus {
    optional uint32 connection_state = 1;
    optional uint32 field_state = 2;
    optional uint32 alive_state = 3;
    optional uint32 robot_id = 4;
    optional uint32 robot_type = 5;
    optional uint32 performance_system_shooter = 6;
    optional uint32 performance_system_chassis = 7;
    optional uint32 level = 8;
    optional uint32 max_health = 9;
    optional uint32 max_heat = 10;
    optional float heat_cooldown_rate = 11;
    optional uint32 max_power = 12;
    optional uint32 max_buffer_energy = 13;
    optional uint32 max_chassis_energy = 14;
}

2.2.11 RobotDynamicStatus

用途:机器人实时数据。

数据编号 数据类型 数据用途
1 uint32 当前血量
2 float 当前热量
3 float 上一次弹丸射速
4 uint32 当前剩余底盘能量
5 uint32 当前缓冲能量
6 uint32 当前经验值
7 uint32 距离下一次升级仍需获得的经验
8 uint32 累计已发弹量
9 uint32 剩余允许发弹量
10 bool 是否处于脱战状态
11 uint32 脱战状态倒计时
12 bool 是否可以远程补血
13 bool 是否可以远程补弹
message RobotDynamicStatus {
    optional uint32 current_health = 1;
    optional float current_heat = 2;
    optional float last_projectile_fire_rate = 3;
    optional uint32 current_chassis_energy = 4;
    optional uint32 current_buffer_energy = 5;
    optional uint32 current_experience = 6;
    optional uint32 experience_for_upgrade = 7;
    optional uint32 total_projectiles_fired = 8;
    optional uint32 remaining_ammo = 9;
    optional bool is_out_of_combat = 10;
    optional uint32 out_of_combat_countdown = 11;
    optional bool can_remote_heal = 12;
    optional bool can_remote_ammo = 13;
}

2.2.12 RobotModuleStatus

用途:机器人各模块运行状态(0=离线,1=在线,2=因安装不规范被视为离线)。

数据编号 数据类型 数据用途
1 uint32 电源管理模块状态
2 uint32 RFID 模块状态
3 uint32 灯条模块状态
4 uint32 17mm 发射机构状态
5 uint32 42mm 发射机构状态
6 uint32 定位模块状态
7 uint32 装甲模块状态
8 uint32 图传模块状态
9 uint32 电容模块状态
10 uint32 主控状态
11 uint32 激光检测模块状态
message RobotModuleStatus {
    optional uint32 power_manager = 1;
    optional uint32 rfid = 2;
    optional uint32 light_strip = 3;
    optional uint32 small_shooter = 4;
    optional uint32 big_shooter = 5;
    optional uint32 uwb = 6;
    optional uint32 armor = 7;
    optional uint32 video_transmission = 8;
    optional uint32 capacitor = 9;
    optional uint32 main_controller = 10;
    optional uint32 laser_detection_module = 11;
}

2.2.13 RobotPosition

用途:机器人空间坐标和朝向。

数据编号 数据类型 数据用途
1 float 世界坐标 X 轴
2 float 世界坐标 Y 轴
3 float 世界坐标 Z 轴
4 float 本机器人测速模块的朝向,单位:度,正北为 0 度
5 uint32 坐标所属机器人:6/106=空中,7/107=哨兵,仅云台手客户端生效
message RobotPosition {
    optional float x = 1;
    optional float y = 2;
    optional float z = 3;
    optional float yaw = 4;
    optional uint32 robot_id = 5;
}

2.2.14 Buff

用途:Buff 效果信息。

数据编号 数据类型 数据用途
1 uint32 机器人 ID
2 uint32 Buff 类型(枚举值见下)
3 int32 Buff 增益值
4 uint32 Buff 最大剩余时间
5 uint32 Buff 剩余时间

buff 类型枚举值:

  • 1:攻击增益
  • 2:防御增益/负防御增益(易伤)
  • 3:射击热量冷却增益
  • 4:底盘功率增益
  • 5:回血增益
  • 6:可兑换允许发弹量
  • 7:地形跨越增益(预备)(指诸如检测到飞坡前半部分的场地交互模块,但未获取实际地形跨越增益的情况)

增益值说明:类型 1、2、4、5 为百分比;类型 3 为直接值,x 表示热量冷却增加 x/s。

message Buff {
    optional uint32 robot_id = 1;
    optional uint32 buff_type = 2;
    optional int32 buff_level = 3;
    optional uint32 buff_max_time = 4;
    optional uint32 buff_left_time = 5;
}

2.2.15 PenaltyInfo

用途:判罚信息同步。

数据编号 数据类型 数据用途
1 uint32 当前受罚类型(枚举值见下)
2 uint32 当前受罚效果时长(秒)
3 uint32 当前判罚数量

penalty_type 枚举值:

  • 1:黄牌
  • 2:双方黄牌
  • 3:红牌
  • 4:超功率
  • 5:超热量
  • 6:超射速
message PenaltyInfo {
    optional uint32 penalty_type = 1;
    optional uint32 penalty_effect_sec = 2;
    optional uint32 total_penalty_num = 3;
}

2.2.16 RobotPathPlanInfo

用途:哨兵轨迹规划信息。

数据编号 数据类型 数据用途
1 uint32 哨兵意图(1=攻击,2=防守,3=移动)
2 uint32 起始点 X 坐标(分米)
3 uint32 起始点 Y 坐标(分米)
4 repeated int32 相对起始点 X 增量数组(-128~+127,长度 49)
5 repeated int32 相对起始点 Y 增量数组(-128~+127,长度 49)
6 uint32 发送者 ID

增量基准待核对:客户端路径增量定义为相对起始点,串口 0x0307 的路径增量定义为相对上一个点。两者描述不一致,转换或绘制路径前需核对实际数据。

message RobotPathPlanInfo {
    optional uint32 intention = 1;
    optional uint32 start_pos_x = 2;
    optional uint32 start_pos_y = 3;
    repeated int32 offset_x = 4 [packed = true];
    repeated int32 offset_y = 5 [packed = true];
    optional uint32 sender_id = 6;
}

2.2.17 MapClickInfo

用途:地图点击标记信息同步,服务器 → 自定义客户端。

数据编号 数据类型 数据用途
1 uint32 发送范围:0=指定客户端,1=除哨兵,2=包含哨兵
2 bytes 目标机器人 ID 列表,固定 7 字节;红方英雄/工程填 1、2,蓝方填 101、102,其余填 0
3 uint32 标记类型:1=攻击,2=防御,3=警戒,4=自定义
4 uint32 标定的对方 ID
5 uint32 自定义图标 ASCII,仅类型 4 有效;允许 C~L、N、O、Q~Z
6 uint32 标记模式:1=地图,2=对方机器人
7 float 地图坐标 X
8 float 地图坐标 Y
message MapClickInfo {
    optional uint32 is_send_all = 1;
    optional bytes robot_id = 2;
    optional uint32 mode = 3;
    optional uint32 enemy_id = 4;
    optional uint32 ascii = 5;
    optional uint32 type = 6;
    optional float map_x = 7;
    optional float map_y = 8;
}

2.2.18 MapClickCmd

用途:地图点击标记指令,自定义客户端 → 服务器。字段定义与 MapClickInfo 相同,但 Topic 和方向不同。频率限制见 1.3 小地图交互数据。旧消息 MapClickInfoNotify 不再使用,屏幕坐标字段已移除,地图坐标编号改为 7、8。

数据编号 数据类型 数据用途
1 uint32 发送范围:0=指定客户端,1=除哨兵,2=包含哨兵
2 bytes 目标机器人 ID 列表,固定 7 字节;红方英雄/工程填 1、2,蓝方填 101、102,其余填 0
3 uint32 标记类型:1=攻击,2=防御,3=警戒,4=自定义
4 uint32 标定的对方 ID
5 uint32 自定义图标 ASCII,仅类型 4 有效;允许 C~L、N、O、Q~Z
6 uint32 标记模式:1=地图,2=对方机器人
7 float 地图坐标 X
8 float 地图坐标 Y
message MapClickCmd {
    optional uint32 is_send_all = 1;
    optional bytes robot_id = 2;
    optional uint32 mode = 3;
    optional uint32 enemy_id = 4;
    optional uint32 ascii = 5;
    optional uint32 type = 6;
    optional float map_x = 7;
    optional float map_y = 8;
}

2.2.19 RadarInfoToClient

用途:雷达发送的所有机器人位置信息与特殊标识。每次触发按对方 1/2/3/4/6/7 号、己方 1/2/3/4/6/7 号顺序发送。

子数据编号 数据类型 数据用途
1 uint32 位置 X,单位 cm
2 uint32 位置 Y,单位 cm
3 uint32 特殊标识:0=否,1=是,2=是但目标定位模块离线

外层消息定义待核对:repeated uint32 RadarSingleRobotInfo; 缺少字段编号,且与下方消息结构存在类型歧义,不能直接编译。实现前需确认外层字段的类型和编号。

message RadarSingleRobotInfo {
    optional uint32 target_pos_x=1;
    optional uint32 target_pos_y=2;
    optional uint32 is_high_light=3;
}

2.2.20 CustomByteBlock

用途:自定义数据流(机器人端对应 0x0310)。

数据编号 数据类型 数据用途
1 bytes 最大为 2.4 kbit 的自定义数据包
message CustomByteBlock {
    optional bytes data = 1;
}

2.2.21 AssemblyCommand

用途:工程装配指令。

数据编号 数据类型 数据用途
1 uint32 装配操作类型(0=开始兑换,1=确认装配,2=取消装配)
2 uint32 选中的装配难度
message AssemblyCommand {
    optional uint32 operation = 1;
    optional uint32 difficulty = 2;
}

2.2.22 TechCoreMotionStateSync

用途:科技核心运动状态同步。

数据编号 数据类型 数据用途
1 uint32 当前可选择的最高装配难度等级
2 uint32 基础状态:1=初始位置,2=运动中,3=已到达对应位姿
3 uint32 能量单元放入状态:0=未放入,1=已放入
4 uint32 能量单元平移状态:0=未平移,1=已平移
5 uint32 能量单元旋转状态:0=未旋转,1=已旋转
6 uint32 对方科技核心状态,仅己方四级装配可用时有效:0=未装配,1=非四级装配,2=四级装配
7 uint32 己方装配总剩余时长,仅己方正在四级装配时有效
8 uint32 己方单步骤装配间隔剩余时长,仅己方正在四级装配时有效
message TechCoreMotionStateSync {
    optional uint32 maximum_difficulty_level = 1;
    optional uint32 basic_state = 2;
    optional uint32 putin_state = 3;
    optional uint32 move_state = 4;
    optional uint32 rotate_state = 5;
    optional uint32 enemy_core_status = 6;
    optional uint32 remain_time_all = 7;
    optional uint32 remain_time_step = 8;
}

2.2.23 RobotPerformanceSelectionCommand

用途:地面机器人选择性能体系或控制方式。

数据编号 数据类型 数据用途
1 uint32 发射机构性能体系(枚举值同 RobotStaticStatus)
2 uint32 底盘性能体系(枚举值同 RobotStaticStatus)
3 uint32 哨兵控制方式(0=自动控制,1=半自动控制)
message RobotPerformanceSelectionCommand {
    optional uint32 shooter = 1;
    optional uint32 chassis = 2;
    optional uint32 sentry_control = 3;
}

2.2.24 RobotPerformanceSelectionSync

用途:步兵/英雄性能体系状态同步。

数据编号 数据类型 数据用途
1 uint32 发射机构性能体系
2 uint32 底盘性能体系
3 uint32 哨兵控制方式选择
message RobotPerformanceSelectionSync {
    optional uint32 shooter = 1;
    optional uint32 chassis = 2;
    optional uint32 sentry_control = 3;
}

2.2.25 CommonCommand

用途:机器人多种常用指令。

数据编号 数据类型 数据用途
1 uint32 指令类型(枚举值见下)
2 uint32 参数

cmd_type 枚举值:

  • 1:兑换 17mm 发弹量(仅剩余金币足以兑换时有效;参数值必须为 10 的倍数)
  • 2:兑换 42mm 发弹量(仅剩余金币足以兑换时有效)
  • 3:确认复活(若此时复活读条完成将立即复活)
  • 4:兑换立即复活(若此时符合规则要求,则会立即消耗金币兑换立即复活)
  • 5:远程兑换允许发弹量(若此时符合规则要求,则会立即消耗金币远程兑换允许发弹量)
  • 6:远程兑换血量(若此时符合规则要求,则会立即消耗金币远程兑换血量)
message CommonCommand {
    optional uint32 cmd_type = 1;
    optional uint32 param = 2;
}

2.2.26 HeroDeployModeEventCommand

用途:英雄部署模式指令。

数据编号 数据类型 数据用途
1 uint32 模式(0=退出,1=进入)
message HeroDeployModeEventCommand {
    optional uint32 mode = 1;
}

2.2.27 DeployModeStatusSync

用途:英雄部署模式状态同步。

数据编号 数据类型 数据用途
1 uint32 当前部署模式状态(0=未部署,1=已部署)
message DeployModeStatusSync {
    optional uint32 status = 1;
}

2.2.28 RuneActivateCommand

用途:能量机关激活指令。

数据编号 数据类型 数据用途
1 uint32 激活(1=开启)
message RuneActivateCommand {
    optional uint32 activate = 1;
}

2.2.29 RuneStatusSync

用途:能量机关状态同步。

数据编号 数据类型 数据用途
1 uint32 当前能量机关状态(枚举值见下)
2 uint32 当前已激活的灯臂数量
3 float 平均环数,仅大能量机关正在激活时有效,其余为 0

rune_status 枚举值:

  • 1:未激活
  • 2:正在激活
  • 3:已激活
message RuneStatusSync {
    optional uint32 rune_status = 1;
    optional uint32 activated_arms = 2;
    optional float average_rings = 3;
}

2.2.30 SentryStatusSync

用途:哨兵姿态、弱化和强化状态。

数据编号 数据类型 数据用途
1 uint32 姿态 ID(1=进攻姿态,2=防御姿态,3=移动姿态)
2 bool 是否弱化
3 bool 是否强化
message SentryStatusSync {
    optional uint32 posture_id = 1;
    optional bool is_weakened = 2;
    optional bool is_powered = 3;
}

2.2.31 DartCommand

用途:飞镖控制指令。

数据编号 数据类型 数据用途
1 uint32 目标 ID(1=前哨站,2=基地固定目标,3=基地随机固定目标,4=基地随机移动目标,5=基地末端移动目标)
2 bool 闸门开关
3 bool 是否确认发射(默认为 0,1=确认发射)
message DartCommand {
    optional uint32 target_id = 1;
    optional bool open = 2;
    optional bool launch_confirm = 3;
}

2.2.32 DartSelectTargetStatusSync

用途:飞镖目标选择状态同步。

数据编号 数据类型 数据用途
1 uint32 目标 ID
2 uint32 闸门状态(0=关闭,1=开启中,2=已开启)
message DartSelectTargetStatusSync {
    optional uint32 target_id = 1;
    optional uint32 open = 2;
}

2.2.33 SentryCtrlCommand

用途:哨兵控制指令请求。

数据编号 数据类型 数据用途
1 uint32 指令编号(枚举值见下)

指令编号枚举值:

  • 0:无效
  • 1:补血点补弹
  • 3:远程补弹
  • 4:远程回血
  • 5:确认复活
  • 6:确认花费金币复活
  • 7:切换为进攻姿态
  • 8:切换为防御姿态
  • 9:切换为移动姿态
  • 10:切换为强化进攻姿态
  • 11:切换为强化防御姿态
  • 12:切换为强化移动姿态
message SentryCtrlCommand {
    optional uint32 command_id = 1;
}

2.2.34 SentryCtrlResult

用途:哨兵控制指令结果反馈。

数据编号 数据类型 数据用途
1 uint32 对应的指令编号
2 uint32 执行结果码(0=成功,其他=失败)
message SentryCtrlResult {
    optional uint32 command_id = 1;
    optional uint32 result_code = 2;
}

2.2.35 AirSupportCommand

用途:空中支援指令。

数据编号 数据类型 数据用途
1 uint32 指令类型(枚举值见下)

command_id 枚举值:

  • 0:取消空中支援
  • 1:免费呼叫空中支援
  • 2:花费金币呼叫空中支援(仍优先使用免费时长)
  • 3:花费金币解除反制(仅在规则描述的情况下有效)
message AirSupportCommand {
    optional uint32 command_id = 1;
}

2.2.36 AirSupportStatusSync

用途:空中支援状态反馈。

数据编号 数据类型 数据用途
1 uint32 空中支援状态(0=未进行,1=正在空中支援)
2 uint32 免费空中支援剩余时间
3 uint32 付费空中支援已花费金币
4 uint32 当前激光检测模块是否正在检测到被照射(0=未被照射,1=被照射)
5 uint32 空中机器人被反制状态(0=被锁定且不可解除,1=正常未被反制,2=被锁定且可花费金币解除)
message AirSupportStatusSync {
    optional uint32 airsupport_status = 1;
    optional uint32 left_time = 2;
    optional uint32 cost_coins = 3;
    optional uint32 is_being_targeted = 4;
    optional uint32 shooter_status = 5;
}

附录一:CRC 校验

校验 初值 计算范围 写入位置
CRC8 0xFF 帧头前 4 字节 帧头第 5 字节
CRC16 0xFFFF 完整帧除最后 2 字节外的全部内容 帧尾,低字节在前,高字节在后

CRC8 的生成多项式为 x^8 + x^5 + x^4 + 1。实现时需确保查表运算、初值和收尾方式均符合协议,不能仅凭 CRC 算法名称判断是否兼容。

推荐验证顺序:先调用帧头 CRC8 校验,再按 data_length + 9 收齐帧并校验 CRC16,通过后解析命令数据。移植校验代码时需检查类型拼写、标点和校验结果。


附录二:ID 编号说明

机器人 ID 编号

  • 1 :红方英雄机器人
  • 2 :红方工程机器人
  • 3/4/5:红方步兵机器人(与机器人 ID 3~5 对应)
  • 6 :红方空中机器人
  • 7 :红方哨兵机器人
  • 8 :红方飞镖
  • 9 :红方雷达
  • 10 :红方前哨站
  • 11 :红方基地
  • 101:蓝方英雄机器人
  • 102:蓝方工程机器人
  • 103/104/105:蓝方步兵机器人(与机器人 ID 3~5 对应)
  • 106:蓝方空中机器人
  • 107:蓝方哨兵机器人
  • 108:蓝方飞镖
  • 109:蓝方雷达
  • 110:蓝方前哨站
  • 111:蓝方基地

选手端 ID

  • 0x0101:红方英雄机器人选手端
  • 0x0102:红方工程机器人选手端
  • 0x0103/0x0104/0x0105:红方步兵机器人选手端(与机器人 ID 3~5 对应)
  • 0x0106:红方空中机器人选手端
  • 0x016A:蓝方空中机器人选手端
  • 0x0165:蓝方英雄机器人选手端
  • 0x0166:蓝方工程机器人选手端
  • 0x0167/0x0168/0x0169:蓝方步兵机器人选手端(与机器人 ID 3~5 对应)
  • 0x8080:裁判系统服务器(用于哨兵和雷达自主决策指令)

附录三:自定义客户端示例通信代码

下列示例依赖 Unity、MQTTnet 及项目自己的 Debugger、MqttSubProtoEvent、ThreadPool,并非独立可运行程序。发送协议消息应使用 Protobuf 序列化后的字节数组和对应 Topic。示例默认连接 192.168.12.1,使用 AtLeastOnce(QoS 1),与第 2 章的连接参数及最高 QoS 一致。

using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

/// <summary>
/// MQTT客户端
/// </summary>
public class MyMqttClient {
    private IMqttClient m_MqttClient = null;
    private string m_ClientID;

    /// <summary>
    /// 创建客户端并连接服务器
    /// </summary>
    /// <param name="clientID"></param>
    /// <param name="ip"></param>
    /// <param name="port"></param>
    public MyMqttClient(string clientID, string ip = "192.168.12.1", int port = 3333) {
        m_ClientID = clientID;
        // 客户端选项生成器
        var options = new MqttClientOptionsBuilder()
            .WithClientId(m_ClientID)
            .WithTcpServer(ip, port)
            .Build();
        // 创建客户端
        m_MqttClient = new MqttFactory().CreateMqttClient();
        // 监测客户端连接/断开连接完成
        m_MqttClient.ConnectedAsync += ClientConnected;
        m_MqttClient.DisconnectedAsync += ClientDisConnected;
        // 客户端接收到消息
        m_MqttClient.ApplicationMessageReceivedAsync += ReceiveMsg;
        // 连接服务器
        m_MqttClient.ConnectAsync(options);
    }

    /// <summary>
    /// 接收到消息
    /// </summary>
    private Task ReceiveMsg(MqttApplicationMessageReceivedEventArgs args) {
        Debugger.Log(DEBUGLOGENUM.CustomClient, $"recv topic: {args.ApplicationMessage.Topic}");
        // 消息post出现,以免出现消息堵塞
        MqttSubProtoEvent ev = new MqttSubProtoEvent();
        ev.topic = args.ApplicationMessage.Topic;
        ev.data = args.ApplicationMessage.Payload;
        ThreadPool.CustomClientLoop.PostEvent(ev);
        return Task.CompletedTask;
    }

    /// <summary>
    /// 断开连接完成
    /// </summary>
    private Task ClientDisConnected(MqttClientDisconnectedEventArgs args) {
        return Task.CompletedTask;
    }

    /// <summary>
    /// 连接完成
    /// </summary>
    private Task ClientConnected(MqttClientConnectedEventArgs args) {
        Subscribe("KeyboardMouseControl");
        Subscribe("CustomControl");
        return Task.CompletedTask;
    }

    /// <summary>
    /// 发布消息
    /// </summary>
    public void PublishMsg(string topic, string message, MqttQualityOfServiceLevel level = MqttQualityOfServiceLevel.AtLeastOnce, bool isRetain = false) {
        long unixUs = DateTime.UtcNow.Ticks / 10; // 微秒级
        m_MqttClient.PublishStringAsync(topic, message, level, isRetain);
    }

    /// <summary>
    /// 发布消息(字节数组)
    /// </summary>
    public void PublishBytesMsg(string topic, byte[] message, MqttQualityOfServiceLevel level = MqttQualityOfServiceLevel.AtLeastOnce, bool isRetain = false) {
        long unixUs = DateTime.UtcNow.Ticks / 10; // 微秒级
        m_MqttClient.PublishBinaryAsync(topic, message, level, isRetain);
    }

    /// <summary>
    /// 订阅主题
    /// </summary>
    public void Subscribe(string topic) {
        m_MqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topic).Build());
    }
}