外观
自己写私服
一套从零实现 Kards 服务端的系列教程。协议不是猜的,而是从可运行的服务端实现与客户端产物里读出来的。
协议依据以 Go 与 C# 两套实现为准:
| 参考实现 | 语言 / 栈 | 在本系列里的角色 |
|---|---|---|
fyserver | C# / .NET 10 + ASP.NET Core minimal API | 主参考:C# 侧范例;单端口 HTTP+WS;codec、对局、调度、结算最完整 |
kards-server-go | Go / Gin + GORM + gorilla/websocket | 主参考:JWT 鉴权、卡组码、对局状态机、WS 与独立端口方案 |
| 客户端 UHT + 蓝图反编译 | C++ 头文件 + Kismet 字节码 | 字段与行为的权威来源:见附录 A / 附录 B |
word-server(NestJS) | TypeScript / NestJS + ws + level | ⚠️ 已过时,仅作历史参考:个别行为(如结算补刀动作)是早期版本的适配,不代表协议要求 |
编解码算法本身的独立逆向成果见 B64XorDecryption(C 动态库 + C# API + IDA 伪代码存档),本系列第 4 章是它的教程化版本。
教程里每一段样例代码都给 TypeScript 和 C# 两个版本——TypeScript 只是教学语言,不作为协议依据;凡与 Go / C# 实现冲突之处,一律以后者为准。
用途与免责
本系列仅用于协议研究与学习。文中所有接口、字段、常量均来自对第三方实现的阅读与抓包验证,与 1939 Games 无关,也不是官方协议文档。请勿用于商业用途或任何侵权场景;相关参考实现本身即声明为非盈利、禁止商用。生产环境请勿复用文中的默认密钥与口令。
整体拓扑
客户端只认两个东西:一个 HTTP 基址 和 一个 WebSocket 地址。两者都可以由服务端在登录响应里下发,所以整套协议的第一跳是"引导"。
┌──────────────┐ 1. GET / (拿 endpoints 表 + current_user)
│ │ ────────────────────────────────────────────────►
│ 游戏客户端 │ 2. POST /session (登录/自动建号) → SessionResponse
│ │ ────────────────────────────────────────────────►
│ │ 3. GET /lobbyplayers → 排队 → GET /matches/v2 (开局数据)
│ │ ────────────────────────────────────────────────►
│ │ 4. POST /matches/v2/{id}/actions (提交动作,codec 加密)
│ │ ◄──────────────────────────────────────────────►
│ │ 5. ws://host:5232/ws (ping / touchcard / notification)
└──────────────┘要点:
- HTTP 端口 5231、WebSocket 端口 5232 是三个实现共同的默认值(
fyserver把 WS 合并进 HTTP 同端口,走根路径升级,这一点在第 9 章展开)。 - 客户端不会硬编码业务路径,它先读
GET /返回的endpoints字典,再去调用。想加接口不必改客户端。 - 玩家的长期身份是服务端签发的 token,之后所有请求都带
Authorization: JWT <token>。
协议速查
粗体为三个实现都实现的"主干",其余为某一个实现的扩展。
| 方法 | 路径 | 用途 | 章节 |
|---|---|---|---|
GET | / | 引导:endpoints 表 + current_user | 02 |
GET | /.com/config | 客户端静态配置(Go 版) | 02 |
POST | /session | 登录 / 自动建号 → 巨型 SessionResponse | 03 |
GET | /players/{id}/library | 卡牌库(全牌/收藏) | 05 |
GET | /items/{id} / POST | 桌饰、头像、卡背等物件 | 05 |
PUT | /{id}/heartbeat | 心跳与在线状态 | 05 |
POST | /players/{id}/decks | 新建卡组 | 06 |
PUT | /players/{id}/decks/{deckId} | 改卡组(fill / rename / change_card_back / make_favorite) | 06 |
POST | /lobbyplayers | 进匹配队列 | 07 |
DELETE | /lobbyplayers | 退出队列 | 07 |
POST | /singleplayerlobby | 单人/人机队列 | 07 |
GET | /matches/v2/ | 轮询:是否匹配到对手 → 开局数据 | 07 |
GET | /matches/v2/reconnect | 断线重连,回放全部动作 | 08 |
POST | /matches/v2/{id}/actions | 提交动作(codec 加密) | 08 |
PUT | /matches/v2/{id}/actions | 轮询对手动作 | 08 |
POST | /matches/v2/{id}/mulligan | 换牌(只有 POST,PUT 返回 405) | 08 |
PUT | /matches/v2/{id} | 结束对局 | 08 |
WS | ws://host:5232/ws | 实时通道 | 09 |
环境准备
bash
dotnet new web -o mykards
# 目录结构建议与 fyserver 对齐:Endpoints/ Models/ Services/bash
npm i -g @nestjs/cli
nest new mykards # 或 npm init -y && npm i express ws两个语言的选择标准很简单:
- C# / ASP.NET Core:
SessionResponse这种几十个字段的 DTO 用record+ 源生成 JSON 最省事,单端口 HTTP+WS 也最自然。 - TypeScript / NestJS:Controller + DTO 的表达力强,热重载舒服,适合当"协议试验台"快速改字段看客户端反应。
章节导航
- 协议是怎么知道的 —— 抓包、蓝图反编译、UHT 三路取证
- 引导接口与最小服务 ——
GET /、endpoints表、current_user - 登录与会话 ——
POST /session、巨型响应逐字段说明、token 签发 - 消息编解码 codec —— salt 长度表 + 循环 XOR + Base64
- 玩家数据、物品与图书馆 —— library / items / heartbeat
- 卡组与卡组码 ——
%%卡组码的位段与倍数规则 - 大厅、匹配与开局 —— 队列、
matches/v2、发牌 - 对局同步与结算 —— actions 轮询/提交、mulligan、胜负
- WebSocket 实时通道 —— channel 一览(实测 6 个)与帧格式
- 部署与兼容性坑 —— 反代、IP 重定向、字段顺序陷阱
- 物品、装备与卡牌库 ——
items/library的官服实测形态、客户端结构体对照、deck_id为什么不下发 - 参考实现没覆盖的端点 —— 战役 / 抽卡赛 / 锦标赛 / 成就 / 任务的实测形态,以及"操作名即文件名"的客户端结构体索引
- 人机对局与对局内协议 ——
singleplayerlobby、训练机器人特征、开局换牌、location_number的真实语义、一局完整时序、actions 滑动窗口,以及actions数组里其实是 codec 包
附录(产物注解、自检、复现与官服实测)
正文讲"服务端要返回什么",附录讲"客户端为什么这么要"——全部来自游戏包的 UHT 头文件与蓝图反编译:
- 附录 A · UHT 结构体与枚举注解 ——
endpoints/FMatch2/FCardData/FJwtPayload等客户端结构体逐字段注解;EFactionEnum就是卡组码国家位、ECardLocationEnum就是location字符串 - 附录 B · 蓝图反编译注解 ——
server_options的 25 个配置键与默认值、版本闸门Is Client Version OK、dev/live 从endpoints.root推导、五国等级与阵营颜色表 - 附录 C · 客户端如何解析端点与配置 —— 从硬编码基址到进对局的完整链路时序,以及"哪些是实测、哪些是推断"
- 附录 D · 自检脚本与常量速查 —— 常量速查表 + 可复制的 PowerShell 冒烟脚本(10 步逐步
✓/✗并指向对应章节) - 附录 E · 复现指南与待验证清单 —— 四条取证路径与复现步骤,以及 10 项尚未验证的推断(每项附一分钟验证法)
- 附录 F · 官服实测对照 —— 直接调用
kards.live.1939api.com的实测结果:API Key 前置头、真实登录 DTO(provider: "device_id")、/session的 70 个扁平字段、150 键server_options全清单、RS256 令牌 claim、library 与卡组码真实形态,以及六处对既有说法的纠正 - 附录 G · 端点矩阵与数据复用 —— 22 个端点的方法/形态/元素字段一张表;哪些结构被复用(会话内联的卡组、赛事、
current_user= JWT claim);六个"同名不同形"的键;空响应的三种写法([]/null/ 空体) - 附录 H · 真实客户端抓包实录 —— 真客户端打到自建私服的 28 条记录:真实
/sessionDTO、建卡组与fill、换牌请求里整局 82 张牌面、结束回合的match_data快照、两条会话动作通道(含编码在 URL 路径里的玩家级通道)、私服的 404 清单,以及 ping/pong 心跳帧