Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-CP2B-T独立方案审查.md
Liubing\LB 6813da149a feat(upserver): 保存M2客户端KV迁移至本人统计写检查点
保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。
保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。

验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。
包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
2026-09-30 19:19:05 +08:00

118 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# M2 CP2B-T独立方案审查:正式KV普通业务请求恢复(含第6–8项补审)
日期:2026-09-29。审查者:ZCode会话(M2全链审查者)。本轮只读+hash重算+源码核对。
## 受审hash核对
`M2-CP2B-T受审基线.json`:**7/7全部一致**。
## 总裁决
**通过。** 可批准CP2B-T实施(主会话裁决后)。无P1/P2;P3三条不阻塞。
## 五问逐答
### 1. 同一Context租约与运输就绪时序:**闭合**
- 正式模式通过`PlayerKVAccountLease`扩展(区别于验证两路由),复用同一CookieContainer与Close排水——无第二HttpClient、无第二登录;
- **运输就绪时点正确定义为CP3A三原实例交接后**——不是Apply前(`BindKVForGame`时仍标未就绪),也不是GameLoaded即刻(须等CP3A绑定成功)。这防止了加载期间业务副作用;
- 路线B正确(A丢所有权/C新增第四传输面)。
### 2. 路径校验与专用路由保护:**充分**
- 规范相对路径校验列表全面(空/绝对/跨源/`//`/`?`/`#`/`%`/控制字符/`.`/`..`);
- **保留`__kv_validation/*`、`account/Login`、`playerdata/*`拒绝**——防绕过验证路由/重认证/绕过专用KV传输;
- "不能以'只列Mail/Friend'再造临时禁用门"——正确的避免碎片化门控原则;
- 方案诚实声明"客户端路由字符串不冒充线上响应合同"——运输可用≠线上验证。
### 3. 401/Logout/旧响应闭合:**闭合**
- 401/403→同步撤销就绪+身份+原handle→再交Coordinator关闭——与CP2A/CP3A撤销顺序一致;
- Logout/Quit→任何await前同步禁用+递增SessionVersion→租约排水——正确时序;
- 旧响应不发布/不覆盖新身份——同epoch检查;
- **`UserService.cs`不在白名单能否只靠`UserService.KVLoad.cs`完成**:**能**——401事件处理/Logout逻辑/EnableKVBusiness调用均可在此文件实现,不需要改`UserService.cs`的公开签名或构造。
### 4. 合成独立验收:**可行且充分**
- 合成handler可完整验证:就绪前零发送→就绪后正常路径发送→验证租约仍限两路由→路径校验→401/退出/旧epoch→Cookie/SessionVersion;
- **不能证明的**(方案如实声明):线上响应合同、公开身份发布、真实Player编译、SignalR真实连接——均留后续。
### 5. 白名单/回归/闲置清理:**足够**
3文件白名单精确到方法/入口。无新增闲置接口——`EnableKVBusiness`由UserService.KVLoad消费、租 lease扩展由CustomServerMgr消费、Cookie交接由RTService将来消费(G5)。最终清账在CP4/5统一。
## P3
- **P3-1**:§3.5的`Cookie`对RTService开放——建议实现时在Cookie返回前加日志("Formal KV Cookie released to RTService")便于G5 SignalR连接调试时追踪;
- **P3-2**:§3.1的路由校验建议用一个`static readonly HashSet<char>`或类似结构表达禁止字符集,防实现时遗漏;
- **P3-3**:§5验收用例中建议加"验证租 lease尝试发送普通业务路径被拒绝"的显式断言(方案已隐含,落为独立用例更清晰)。
## 结论
**通过CP2B-T方案。** 主会话核hash(已代验7/7)裁决后按3文件白名单实施。这是打通"正式业务API可用"的关键一步——CP2B首场景/身份发布/SignalR留后续。
---
## 第6–8项补审(同批追加,与原五项合并裁决)
### 6. formal/validation模式传递与路径/超时兼容
**模式传递安全**:`PlayerKVControlledLoadCapability`已有`readonly bool formal`字段(:339–340),构造时固定。`PlayerKVAccountLease`可通过同一capability获取`formal`标志来区分路由策略:
- 验证模式→仅两精确路由(原语义不变)
- 正式模式→规范相对路径,但**拒绝**`__kv_validation/*`(防验证入口暴露于正式)+拒绝`account/Login`+`playerdata/*`(保持专用传输)
- 三向隔离闭合
**已知合法调用误拒风险**:源码核实`CustomServerMgr.cs:124`使用`Timeout = TimeSpan.FromSeconds(15)`。方案要求租约保持此超时。当前`PlayerKVLoginContext`中lease的HttpClient尚未设置显式Timeout——**P3级:实施时须为正式模式lease的HttpClient设置15秒超时**(与旧CustomServerMgr一致)。超时返回空结果/错误事件,不误判为401。
**`?`查询参数**:方案§3.1拒绝`?`——但现有业务调用如`mail/fetch`可能含查询参数。**P2级:需在实施前核查全部现有`CustomServerPost`/`GetStringAsync`调用点的实际路径模板**,确认无`?`形式的合法调用被误拒。如有,最小修订为允许`?`后的有效字符但拒绝`%`/`#`。
**结论**:模式传递安全;超时需补设置(P3);路径校验需核查`?`兼容(P2)。
### 7. GameLoaded后发布运输就绪时的实际调用者
**这是"普通请求运输可用"而非"首场景业务可用"**——方案已明确。
源码核实(`UserService.cs:110`):`IsLogin => !kvControlledInstance && !IsFormalKV && PlayFabClientAPI.IsClientLoggedIn() && IsAuthorized`。正式KV下`IsFormalKV=true`→`IsLogin`恒false→`ApiPlayerId`(依赖`IsAuthorized`即`apiSession!=null`)也为null。
**实际调用者风险**:
- `PFLoginAct`停在登录页(CP2A双闸)→不会自动发业务请求
- `FishingStage`被LoginStage阻断→Mail/Survey/排行榜/Friend等不可达
- **但**:任何后台协程/事件订阅可能通过`CustomServerMgr.CustomServerPost`发送请求——CP2A已排除PF组件和旧回调
**结论**:三文件白名单**足够**。`IsLogin`/`ApiPlayerId`的正确身份发布属CP2B(首场景),本批仅开放运输层。当前无实际调用者会提前发请求——阶段闸保障了这一点。
### 8. Cookie/SignalR失效与Close双重关闭
**RTService消费核实**(:69–77):
```csharp
var session = _serverMgr.SessionVersion;
var cookies = _serverMgr.Cookie;
// ... version/session检查 → new HubConnectionBuilder().WithUrl(..., option => option.Cookies = cookies)
```
RTService在Connect时读取Cookie和SessionVersion,每次连接前核版本——SessionVersion递增后旧连接的`CloseConnection()`+新Connect用新Cookie。**失效路径成立**。
**双重Close风险**:
- `PlayerKVAccountLease.CloseAsync`负责租 lease级排水
- `CustomServerMgr`可能也调用清理
- 方案§3.4已明确"任何await前同步禁用普通请求、递增SessionVersion、触发SessionChanged"——**同步禁用先于任何异步操作**,不存在双关窗口
- Close幂等(4A合同已验证)
**401回调线程**:`CustomServerMgr.SendAsync`在HttpClient响应后调用事件——回调可能在.NET线程池非主线程执行。`LogoutFormalKV`的同步撤销部分(禁用businessReady/清handle)不涉及锁内await,可安全从任何线程执行。后续Coordinator关闭(含await)应在主线程或配合同步上下文调度——**P3级:建议实施时在401处理中用`SynchronizationContext.Post`或等效方式调度到主线程**。
**旧响应撤销新会话**:SendAsync返回后核"响应仍属同租约/版本/epoch"——方案§3.3已覆盖。
**结论**:三文件白名单**足够**,不需要改RTService.cs或UserService.cs。401回调线程需注意调度(P3)。
---
## 更新后总裁决
**通过(维持原裁决,附加一项P2和三项P3)**:
- **P2-1**:实施前核查全部现有`CustomServerPost`/`GetStringAsync`调用点的路径模板,确认方案§3.1的路径校验(含`?`拒绝)不误拒已知合法调用。
- **P3-1**:正式模式lease的HttpClient须设置15秒超时(与旧CustomServerMgr:124一致)。
- **P3-2**:401回调建议调度到主线程后执行撤销。
- **P3-3**(原P3-3保留):验证租 lease拒普通路径的独立断言。
原五项结论不变。三文件白名单确认足够。"无线上业务合同也能独立实施运输层、同时阻断FishingStage"**成立**。