孤儿分支,只含 docs/。内容包括: - 项目总览 / 主脚本编写指南 / 日志指南 - 上传成绩开发文档 / UpsertUserAll 踩坑清单(28 条) - 真机 Hook 对照报告 / 实跑验证记录 - 购买功能票流程(含被服务端拒绝的排查过程) · 扫码登录协议技术规范
5.7 KiB
UpsertUserAll 落库排查报告
背景:
python upsert.py "<二维码>" --send修复闸门后能真实发包, 服务端返回returnCode=1,但数据未写入服务器。 本文档记录已完成的全部排查与结论。
一、结论速览
| 项 | 状态 |
|---|---|
| 闸门放行 | 已修复,returnCode=1 |
| 报文与真机对齐 | 非预期差异归零 |
| 数据落库 | 未落库(point/playCount 回查不变) |
| 根因 | 未确定,已排除 6 类可能 |
二、已修复的 5 个 Bug
Bug 1:upsert.py 漏传 confirm(你遇到的直接原因)
upsert.py 原来只传 dry_run=not opts["send"],但 UpsertUserAll 的规则是
confirm=None 时取 config.UPSERT_CONFIRM(默认 False),所以照样被拦。
# 修复前
dry_run=not opts["send"],
# 修复后
dry_run=not opts["send"],
confirm=bool(opts["send"]), # 关键
Bug 2:userExtend 缺 2 个字段
| Get 侧 | Upsert 侧 | |
|---|---|---|
| 键数 | 14 | 16 |
| 差 | — | isPhotoAgree、isGotoCodeRead |
这两个字段只在 Upsert 侧出现,必须补上。
Bug 3:userRating.udemae 有 7 个冗余大写字段
服务器 Get 响应里 npcWinNum 与 NpcWinNum 并存(23 键):
// Get 侧(source/2026-10-05.log:87)
{ "npcWinNum": 0, ..., "NpcWinNum": 0, "NpcLoseNum": 0, ... } // 23 键
// Upsert 侧(source/2026-10-05.log:123)
{ "npcWinNum": 0, ... } // 16 键
源码 Net.VO.Mai2/UserUdemae.cs 只声明了小写的 16 个,所以大写那批是
服务端冗余输出,回传时必须裁掉。修复前请求体比真机大 119 字节。
Bug 4:userCharacterList 排序错
| 顺序 | |
|---|---|
charaSlot |
[395, 101, 400402, 400401, 200401] |
真机 userCharacterList |
[101, 395, 200401, 400401, 400402](按 characterId 升序) |
回传时按 characterId 升序,不是按 charaSlot 顺序。
Bug 5:扫码用的 chipID 形式错
| 用途 | 正确值 | 源码依据 |
|---|---|---|
| aimedb 扫码 | XXXX-XXXXXXXXXXX(完整) |
ChimeReaderManager.cs:250 KeychipId.Value |
| 游戏服务器 | TESTCLIENT1(短值) |
PacketGetGameSetting.cs:23 KeychipId.ShortValue |
我原来扫码也用了短值。已按源码修正为完整形式。
三、报文对齐最终状态
顶层键 : 7/7
36 节点形态 : 36/36
userData : 82/82 键
userPlaylog : 110/110 键
userExtend : 16/16 键
udemae : 16/16 键
非预期差异 : 0
请求体大小 : 17158 vs 17161 字节(差 3 字节,来自数值位数)
深度递归对比(逐字段、逐元素)后,除「不同次登录必然不同」的值外,零差异。
四、已排除的可能(6 类)
| # | 假设 | 验证方式 | 结果 |
|---|---|---|---|
| 1 | 时序问题(写入有延迟) | 发送后 T+0 / T+20s / T+45s 三次回查 | 都不变 |
| 2 | 差分节点为空导致「看不出变化」 | 直接改绝对值(point 4110→4111) |
仍不写 |
| 3 | 字段缺失/多余 | 深度递归键集合 + 值对比 | 已归零 |
| 4 | number 请求头缺失 |
补上 Packet.cs:170 要求的头 |
仍不写 |
| 5 | playSpecial 值不对 |
查源码 CalcSpecialNum = 硬件标志位 |
非根因 |
| 6 | 缺少 GetUserNewItemList 前置 |
完整复刻真机 121→123 流程 | 仍不写 |
关于第 2 项的探测细节
这是最关键的一次探测 —— 直接 patch 把 userData.point 从 4110 改成 4111 发上去:
baseline point = 4110
上传成绩成功:returnCode=1
T+ 0s point = 4110
T+20s point = 4110
T+45s point = 4110
连最简单的整数字段都不写,说明不是「数据不对」而是「整包没被接受」。
五、当前判断
服务端收下了请求(返回正确的 apiName),但没有应用。
可能的方向(未验证):
-
账号/机台绑定校验 —— token 可能绑定了特定 keychip,而
XXXX-XXXXXXXXXXX只是文档里的示例值,不是你实际机台的 keychip。 若服务端校验「token 对应的机台 ≠ 请求里的 chipID」,就会静默拒收。 -
需要真实游玩产生的会话状态 —— 真机的 Upsert 发生在真实游玩之后, 服务端可能记录了「本次 loginId 下有一局真实游玩」才允许结算。 纯网络脚本没有这个前置状态。
-
服务端侧的风控/审计 —— 真机与脚本的 TLS 指纹、请求节奏等特征不同。
六、需要你提供的信息
要继续定位,最有价值的是:
| # | 信息 | 为什么关键 |
|---|---|---|
| 1 | 你机台的真实 Keychip ID | 若 XXXX-XXXXXXXXXXX 不是你的机台,方向 1 就成立 |
| 2 | 该机台在 keychip.csv 里的对应行 |
可确认 keychip 与店铺/区域的绑定关系 |
| 3 | 真机跑一局后立刻导出 Upsert 报文 | 可拿同账号同机台的最新报文做 1:1 对比 |
source/keychip.csv有 229KB 全量机台对照表,如果你能告诉我机台所在的 店铺 ID,我可以从中查出正确的 keychip。
七、本次排查的正面成果
即使尚未落库,这次排查也修掉了 5 个真实 Bug,其中 3 个(userExtend 缺字段、
udemae 冗余字段、userCharacterList 排序)是只有做深度值对比才能发现的:
- 修复前:报文与真机有 11 处非预期差异
- 修复后:0 处
自检 129/129 仍全绿。
八、复现方式
# 1) 真实发送 + 落库验证(会写服务器,不可撤销)
python upsert.py "<二维码>" --send
# 2) 零网络自检
python selftest.py