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