README 为了防冗余做了精简,被移出的详细内容写成文档放这里:
- 功能票使用说明.md 买票/用票的完整原理、报文依据、playCredit 公式、实测记录
- 账号转生说明.md reincarnate 完整流程、分批规则(避开 2 首)、
续关登录依据、登出 type=1 依据、playlog 合成、迁移范围
- 常见坑与注意事项.md 二维码 / 账号状态 / 功能票 / 副作用 / 日志 / 网络 /
不入库清单 + .gitignore 行尾注释的坑
- netdelivery说明.md DLI 协议、amdaemon.exe 字符串出处、用法与实测结论
6.4 KiB
功能票使用说明(购买 / 消耗)
本文是
README.md里功能票部分的详细版。 购买接口的完整排查过程见 购买功能票流程.md 与 购买功能票_存档与交接.md。
一、票的种类
机台可购买的是付费票(ticketKind=1,api.PAID_TICKET_IDS = (2,3,4,5,6)):
| chargeId | 票 | 上报的 price(= 票的 creditNum) |
|---|---|---|
| 2 | 2倍チケット | 1 |
| 3 | 3倍チケット | 2 |
| 5 | 5倍チケット | 4 |
完整表(含 4/6 倍与活动票)见
api/upsert_user_chargelog.py的TICKET_CREDIT_NUM。⚠️ 不同店铺上架的票不一样。 买之前先
--list看一眼 —— 服务端对未上架的票会直接returnCode=0(实测过)。
二、购买:buy_ticket.py
python buy_ticket.py "<二维码>" --list # 只列当前票券和可买的票
python buy_ticket.py "<二维码>" --charge-id 2 # 预检:只看报文
python buy_ticket.py "<二维码>" --charge-id 2 --send # ★ 真实购买
| 参数 | 默认 | 说明 |
|---|---|---|
--charge-id N |
5 | 买哪张票 |
--list |
— | 只列票券与可购买清单,不做任何写操作 |
--send |
关 | ★ 真实发包 |
--debug |
关 | 日志里带完整收发报文 |
--skip-entry |
关 | 跳过登录后的「入场全量下载」 |
--wait-before N |
30 | 买票前等待秒数(真机 login→买票是 26 秒) |
三道安全检查
- 买之前先查
GetGameChargeApi—— 目标票不在当前可购买清单里就直接退出, 连登录都不做(不浪费二维码)。 依据source/Assembly-CSharp/Manager/TicketManager.cs:137-150。 - 跑前查一次
GetUserChargeApi:账号已经持有功能票时直接拒绝购买 (重复购买有封号风险)。想清掉手里的票用use_ticket.py。 - 跑后再查一次,确认票券状态符合预期。
报文形态
{
"userId": <uid>,
"userChargelog": {
"chargeId": 2, "price": 1,
"purchaseDate": "2026-10-06 22:23:00.0",
"playCount": 1268, "playerRating": 15534,
"placeId": 1007, "regionId": 8, "clientId": "A63E01C3047"
},
"userCharge": {
"chargeId": 2, "stock": 1,
"purchaseDate": "2026-10-06 22:23:00.0",
"validDate": "2027-01-04 04:00:00"
},
"loginDateTime": 1791325354
}
| 字段 | 依据 |
|---|---|
price |
= ticket.creditNum(不是 GetGameChargeApi 里的 money price)。VOExtensions.cs:410 |
stock |
购买后的库存(原值 + 1) |
purchaseDate |
TimeManager.DateTimeFormat = yyyy-MM-dd HH:mm:ss.f,带一位小数 |
validDate |
TicketManager.cs:562-565:Now.AddHours(-4).AddDays(90).ToString("yyyy-MM-dd 04:00:00"),不带小数 |
⚠️ 这两个日期格式不一样 ——
purchaseDate带.f而validDate不带。 少写.0服务端直接returnCode=0(踩过)。
三、消耗:use_ticket.py
python use_ticket.py "<二维码>" --list # 只列当前持有的票
python use_ticket.py "<二维码>" --ticket-id 2 # 预检:只看报文
python use_ticket.py "<二维码>" --ticket-id 2 --send # ★ 真实消耗
| 参数 | 默认 | 说明 |
|---|---|---|
--ticket-id N |
第一张持有的 | 消耗哪一张 |
--list |
— | 只列票券,不做任何写操作 |
--send |
关 | ★ 真实发包 |
--music-id N / --level N |
config.PLAY_MUSIC_ID / PLAY_MUSIC_LEVEL |
「这一局」打的歌 |
--skip-entry |
关 | 跳过入场全量下载 |
--debug |
关 | 日志里带完整收发报文 |
原理
GetUserChargeApi 与 UpsertUserAllApi 里的 userChargeList
格式完全相同(UserCharge.cs:chargeId / stock / purchaseDate / validDate),
所以只要把该节点里目标票的 stock 减 1,再让 playlog 带上
useTicketId / playCredit,服务端就认为这张票被这一局用掉了。
真机实证(2026-10-06 22:23 那局正好用了 2 倍券):
userChargeList: chargeId=2 stock 1 -> 0 ← 票被消耗
userGamePlaylogList[0].useTicketId = 2 ← 用了哪张票
userGamePlaylogList[0].playCredit = 2
userPlaylogList[0].playCredit = 2
userData.lastPlayCredit = 2
(报文里**没有** isNewChargeList 这个节点)
playCredit 的算法(VOExtensions.cs:838-845):
int num = Credit.GameCostEnoughPlay(); // 投币数,普通局 = 1
int useTicketId = TicketManager.GetUseTicketId(playerId);
int ticketCredit= TicketManager.GetTicketCredit(useTicketId); // = 票的 creditNum
int playCredit = num + ticketCredit;
2 倍券的 creditNum = 1 → playCredit = 1 + 1 = 2,与抓包吻合。
实测记录(2026-10-07 00:24)
[消耗前] chargeId=2 2倍チケット stock=1 ← 持有中
选定要消耗的票:chargeId=2 creditNum=1 -> playCredit=1+1=2
userChargeList 里 chargeId=2 的 stock:1 -> 0
★ 上传成绩成功:returnCode=1
落库回查:
playCount 1269 -> 1269 (PLAY_USER_PLAY_COUNT_GAIN=0,不再 +1)
currentPlayCount 197 -> 197
lastPlayDate 2026-10-06 22:23:56 -> 2026-10-07 00:24:07 ← 落库成功
musicId=143 level=4 playCount -> 11 (曲目次数不变)
★ 票券已消耗:chargeId=2 不再持有 ✅
四、典型用法:票挂在手里,想再买一张
buy_ticket.py 检测到已持票会拒绝购买。想再买一张:
- 用
use_ticket.py --send把手里那张消耗掉,或 - 直接在机台上打一局并选用它
然后再跑 buy_ticket.py。
五、购买接口曾经的坑(摘要)
UpsertUserChargelogApi 曾经一直返回 returnCode=0,报文与真机逐字节一致也照样失败。
根因:请求头多发了 Accept 与 Connection。
真机(Unity Mono 的 HttpWebRequest)实际只发这几个头:
number / Content-Type / User-Agent / charset / Mai-Encoding / Content-Encoding / Cookie
删掉这两个头之后一次通过(stock 0 → 1,落库确认)。
完整排查过程(12 项已排除假设、账号对照实验、真机抓包对比)见 购买功能票流程.md 与 购买功能票_存档与交接.md。