6.8 KiB
6.8 KiB
PVP 跨服战斗消息 cmd/data 转发改造说明
背景
巅峰赛跨服战斗原本已经有 Redis pub/sub 通道,但战斗中的客户端操作转发仍然偏间接:
- 本服 controller 先把客户端请求翻译成
FightI调用。 RemoteFightProxy再把FightI调用翻译成battle_command。- 宿主服收到
battle_command后,再翻译回FightI调用。
这样一来,跨服战斗链路和本地战斗链路之间多了一层“动作语义映射”,调试时不容易直接看到前端到底发了哪个 cmd、带了什么 data。
本次改造只处理“战斗中的消息转发”,不改匹配/加入队列逻辑。
保持不变的部分
- 巅峰赛加入和取消匹配仍然走原有 RPC:
MatchJoinOrUpdateMatchCancel
- Ban/Pick、MatchFound、SessionClose 等 PVP 服务内消息仍兼容旧外层格式。
battle_command旧语义消息仍保留,作为兼容兜底,不强制一次性切掉。
对应原因:
- 匹配加入需要同步返回结果,RPC 更直接。
- 现网如果已有旧 envelope 或旧 battle command 发送方,不能直接断。
改造目标
把跨服战斗中的“客户端操作转发”改成直接发送:
{
"cmd": "battle_client_command",
"data": {
"sessionId": "xsvr-...",
"userId": 10001,
"cmd": 2405,
"data": {
"SkillId": 1234
}
}
}
核心含义:
- 外层
cmd:PVP 内部 Redis 消息类型。 - 内层
data.cmd:真实客户端战斗协议 cmd。 - 内层
data.data:该 cmd 对应的 JSON 业务字段。
这样宿主服拿到消息后,可以直接按真实战斗 cmd 处理,而不是先理解一层额外的“跨服动作协议”。
本次代码落点
1. 新增 cmd/data envelope 能力
文件:
logic/service/fight/pvpwire/types.go
改动:
Envelope新增:Cmd stringData json.RawMessage
- 保留旧字段:
Type stringBody []byte
- 新增辅助方法:
NewEnvelopeMessageCmdMessageData
作用:
- 新消息可以直接用
cmd/data。 - 旧消息仍可通过
type/body解析。
2. controller 层遇到远端跨服战斗时,直接转发原始战斗 cmd
文件:
logic/controller/fight_unified.gologic/controller/fight_base.go
改动:
- 在
fight_unified.go增加relayRemoteFightCommand。 - 当
c.FightC是*pvp.RemoteFightProxy时,不再走本地dispatchFightActionEnvelope分发,而是直接转发当前请求的:data.Head.CMD- 当前请求结构体
data
已接入的战斗入口包括:
2404准备7556旧组队准备2405单战位技能7505多战位技能7558旧组队技能2406使用道具7562旧组队道具2407切宠7563旧组队切宠2410逃跑7565旧组队逃跑2441加载进度50002战斗聊天
3. RemoteFightProxy 新增“直接转发客户端命令”能力
文件:
logic/service/fight/pvp/proxy.go
改动:
- 新增
RelayClientCommand(cmd uint32, data any) bool - 新增
marshalClientCommandData(data any) ([]byte, error)
说明:
marshalClientCommandData会把请求结构体转成 JSON。- 会删除
Head/head字段,避免把协议头、二进制字段一起塞进跨服消息。 - 这样发出去的
data.data只保留业务字段。
4. PVP 宿主服新增 battle_client_command 处理
文件:
logic/service/fight/pvp/service.gologic/service/fight/pvpwire/types.go
改动:
- 新增消息类型:
MessageTypeBattleClientCommand
- 新增载荷:
BattleClientCommandPayload
handleRedisMessage增加battle_client_command分支。- 新增
handleBattleClientCommand。
处理方式:
- 宿主服根据内层真实
cmd解出data。 - 再直接调用当前
FightI/FightC。
当前支持的映射:
2404/7556->ReadyFight2405/7505/7558->UseSkillAt2406/7562->UseItemAt2407/7563->ChangePetAt2410/7565->Over(PlayerEscape)2441->LoadPercent50002->Chat
5. 战斗发包转发也统一改成 cmd/data
文件:
logic/service/player/rpc.go
改动:
PacketRelayPayload外层 envelope 从type/body改为cmd/data。
原因:
- 它本质也是跨服战斗中的消息转发。
- 和新的
battle_client_command保持一致,便于抓包和日志排查。
当前实际链路
客户端操作转发
- 客户端发战斗包到本服 controller。
- controller 判断当前
FightC是否为RemoteFightProxy。 - 如果是:
- 直接发布
battle_client_command - 内层携带真实
cmd和 JSONdata
- 直接发布
- 宿主服
pvp.service收到后按cmd分发回战斗逻辑。
战斗结果/下行发包转发
- 宿主服在
RPC_player.SendPackCmd中组包。 - 发布
packet_relay消息,外层使用cmd/data。 - 客服端所在服收到后解包并发回真实客户端。
兼容策略
为了避免一次性改动过大,这次保留了两层兼容:
1. 旧 envelope 兼容
Envelope 同时支持:
- 新格式:
cmd/data - 旧格式:
type/body
handleRedisMessage 统一通过:
MessageCmd()MessageData()
读取消息。
2. 旧 battle_command 兼容
RemoteFightProxy 旧的:
UseSkillUseSkillAtUseItemChangePetChat
这些方法仍然保留,并继续发送原来的 battle_command。
新接入的 controller 优先走 battle_client_command,旧路径仍可兜底。
这次改造的收益
好处
- 跨服战斗消息更直观,日志里能直接看到真实客户端 cmd。
- controller 到宿主服之间不需要再先翻译成一套额外动作协议。
- 宿主服调试时更容易复现前端输入。
- 新老消息可并存,改造风险相对可控。
仍然保留的复杂度
- 宿主服仍然需要针对不同战斗 cmd 做一次 decode 和分发。
- 这是必要复杂度,因为真实战斗入口本来就有多种协议包。
验证
本次已执行:
cd logic && go test ./service/fight/pvp ./service/fight/pvpwirecd logic && go test -c ./controller
结果:
pvp、pvpwire包通过。controller可以完成编译检查。
补充说明:
go test ./controller ./service/player ...在当前环境会因为依赖初始化读取/proc/sys/kernel/osrelease失败而 panic,这属于运行环境问题,不是这次改动引入的编译错误。
后续建议
如果后面继续收敛这块逻辑,可以按下面顺序做:
- 把
battle_command的旧语义层逐步下线,只保留battle_client_command。 - 把
handleBattleClientCommand里的 cmd 映射抽成独立表,避免switch继续膨胀。 - 如果后续战斗协议继续统一,可考虑把 controller 入站结构和跨服转发 decode 共享同一套注册表。