让 Python SDK 的 TCP transport 与当前 macOS 客户端的已验证帧格式一致,替换旧的“24 字节裸头 + 可选 XXTEA”假设。此改造只完成可由二进制和心跳样本证明的帧层;不宣称游戏登录或任务协议已经可用。
客户端发送与接收路径分别调用 Encode6BitBuf 和 Decode6BitBuf。
outbound = Encode6BitBuf(header36) + body
inbound = Decode6BitBuf(first_48_bytes) + remaining_body
明文头固定为 9 个 little-endian int32(36 字节):
| offset | 字段 | 证据/用途 |
|---|---|---|
| 0 | pre |
心跳样本为 0 |
| 4 | msg_id |
接收实现从该偏移读取消息号 |
| 8 | recog |
心跳样本为时间值 |
| 12 | param1 |
业务参数 |
| 16 | param2 |
业务参数 |
| 20 | param3 |
业务参数;心跳样本为 149 |
| 24 | codec |
接收路径据此决定是否解压 body |
| 28 | body_len |
接收路径据此读取 body |
| 32 | sequence |
客户端发送时递增写入 |
Encode6BitBuf 的输出为 48 字节。其字符范围为 0x3c..0x7b;因此 TCP 帧没有 XXTEA 加密,也没有独立的“高两位 SData”位流。
transport/encode6.py提供无运行时 Lua 依赖的、表驱动的 encode6 与 decode6。置换表从已归档的客户端提取表复制为 Python 常量或受版本控制的二进制资源。该模块只负责字节转换。
transport/framing.py定义真实 FrameHeader:9 个字段、36 字节明文、48 字节线头。
encode_frame(...) 产生完整 wire frame:48 字节编码头加 body。decode_header(...) 只接收 48 字节 wire header,返回真实头。decode_frame(...) 检查 body_len 后返回真实 header 和 body。PLAIN_HEADER_SIZE = 36 与 WIRE_HEADER_SIZE = 48,防止调用方再把 24 当作线上边界。codec != 0 的 body 解压行为不在本变更实现;接收端应明确报出不支持的 codec,而不能把压缩数据误当业务 body。
transport/tcp.pyTcpClient 使用 48 字节作为读缓冲边界,并在发送时分配递增 sequence。保留现有 send(msg_id, recog, param1, param2, param3, body) 业务接口;增加可选的 pre、codec 和显式 sequence 测试入口仅供低层诊断。
不再调用 xxtea.encrypt。transport/xxtea.py 保留为资源/历史兼容模块,但文档必须明确它不属于游戏 TCP 帧。
初始 sequence 的生产取值尚未由可复现会话证明,因此 transport 构造器显式接收 initial_sequence,默认 0;真实登录调用必须传入被抓包验证的初值,不能伪装为已知常量。
login/game.py 与高层入口不修改或扩展推测的 CS 104/100/101 序列。相关 docstring 和错误信息改为:帧层已实现;CS2001 body、sequence 初值、服务器 body codec 和后续业务状态机仍待同版本真实会话验证。高层入口继续拒绝将 mock 流程当成实际游戏登录。
body_len 与 body 字节数一致。sequence 递增;指定初值可复现。此改造会改变 encode_frame 的返回格式,从旧的 24B 裸头改为真实 wire 格式。该 SDK 当前没有已验证的真实 TCP 使用者,且高层入口本来就会拒绝进入 TCP 登录,故以协议正确性优先。若调用方仍需要旧 mock 格式,应在测试中使用本地 fixture,而不是保留错误的生产 transport。