Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-CP2正式接线设计输入与新增阻断-2026-09-29.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

39 lines
8.9 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 CP2 正式接线设计输入与新增阻断(待完整方案审查)
日期:2026-09-29。继承修订2及 CP1 限定关闭裁决。本文件记录只读源码核查和拟议边界,**不是 CP2 编码批准**;不得据此改生产代码。正式地址固定 `http://s101.local:18080/`,Unity 仅 CLI Loop,不用 UnitySkills,暂不打包。
## 首批账号裁决
用户本轮明确选定“仅已核准且已有 KV 档的账号”,并确认首批名单**仅 101、102**。因此正式 KV 入口须在认证前核准规范化十进制 ID 为 101/102,认证后再核同一 ID、`newCreate == false`、现存 KV 档及完整 Schema;任一失败停止并排水关闭,不创建空档、不迁移其他账号历史档、不覆盖未知数据。101/102 的既有受控许可不是正式入口的授权机制;正式清单的来源、变更方式和校验文件须在完整方案中单列。其他账号遇 `SaveBackend=kv` 应报错停止,不能静默回退到 PF 或为其自动建档。实际正式账号请求/写入仍不在本检查点。
## 方案选择
| 方案 | 结果 | 裁决方向 |
|---|---|---|
| A:启动前捕获 backend,KV 分支不创建 PF 组件;同一 Context 的 S101 认证、KV Owner 和 API 租约;加载后发布身份与原 handle | 沿用 CP1 原对象/关闭屏障;需完成正式实例准入、网络路由和旧调用审计 | 推荐作为完整 CP2 方案主体,待审查 |
| B:仍创建 PF 组件但在 Awake 内按 backend 跳过 | 保留组件引用可掩盖旧调用,生命周期和旧定时器容易复活;不能证明运行引用归零 | 不采用 |
| C:KV 配置直接调用 Editor 许可循环/验证工厂并开放全部 API | 测试专用权限与正式账号混用,路由无边界 | 不采用 |
用户本轮选定 CP2 首段验收仅含非 AGG 的正式 Bootstrap→登录→31 模块 Apply→原 handle 交接→退出排水,**在进入 `FishingStage` 前停住**;Mail/IAP、业务 API 和公开授权事件先不触发。首场景可运行是下一独立验收门。`FishingStage` 内还有即时 PF 与 API 调用,见下表,不能让登录成功后自然切场景再靠空引用暴露漏项。AGG 渠道路径仍需独立完整设计;在该设计获批前,AGG 构建的 `kv` 必须显式拒绝、PF 默认路径维持。此首段仍须完整独立审查,不能仅凭本文件视为获批。
## 源码证据与必须写入完整方案的合同
1. **启动捕获**:`Boostrap.AfterAssetUpdateV2` 在已取得 `config` 后先调用 `SubBoostrap.RegisterServices(ctx.gameObject)`,后调用 `AfterAssetUpdate(config,...)`。后者当前无条件 `AddComponent<PlayFabMgr>()`。正式方案应把捕获的 `SaveBackend` 传给注册入口,在创建组件前分流;只接受精确 `playfab`/`kv`,缺省 PF,未知值停止。运行期间配置变更不切换当前组件或身份。Editor 的 `PlayerKVGameplayLoop.BeforeBootstrap` 属许可流程,不替代正式捕获。
2. **正式实例与加载**:`PlayFabUserService()` 的 `kvControlledInstance=false`,而 `ResetForKVLoad` / `ReplaceFromKVDecoded` 只接受受控实例或 Editor loop;`RejectSaveBackend("kv")` 直接拒绝。完整方案必须定义互斥的正式 KV 实例准入,不能把 `kvControlledInstance` 当成正式标志随意改义;第 31 模块的初始化通知/归一化意图、失败清理和 `OnInitData` 接收者都要有正式合同。正式登录仅调用 `LoadForGameAsync` 一次,读取 `TryGetCurrentGameHandle(epoch)` 得到 CP1 同一原对象;`TryAcquireGameHandle` 仅由 Coordinator 内部消费。不可再调 `LoginApiServerCore` 建第二个 S101 会话。
3. **身份发布**:当前 `IsLogin` 必须有 PF SDK 登录,`IsAuthorized` 必须 `apiSession` 认证,`ApiPlayerId` 取 `apiSession.PlayerId`,而正式 KV Context 持有的是另一会话。CP2 首段仅在 GameLoaded 后形成内部“已认证且原 handle 已交接”的状态,阶段切换、Mail/IAP 初始化、`EvtAPISvrLoginSuccess` 和对玩法公开 `IsAuthorized` 均保持关闭;后续首场景门才一次性发布 `InternalId`、API PlayerId、`CreateTime` 和事件。失败/Logout 同步撤销内部状态和 handle,再异步排水;旧 epoch 响应不得恢复身份。`UserId` 当前代表 PF ID,Mail 缓存和若干埋点会消费它;不得把 S101 十进制 ID 偷换成“PF ID”而不逐调用审计。`SetMMTestValue` 把 UserId 按十六进制解析并写 PF,KV 分支必须使用已解码 MMTest 且不调用该旧函数。`OnYinDunLogin`/TapJoy、资料更新及 AGG 头像/关联账号写入须列逐项行为边界。
4. **工厂与文件**:`PlayerKVGameLoadCapability` 目前只有 `UNITY_EDITOR` 的 `CreateForValidation`,其验证 handler 明确拒绝真实传输。需另增正式工厂,从启动捕获的 API 校验恰为 `http://s101.local:18080/`,使用同一 CookieContainer、三个互不共享且可独立 Dispose 的 auth/KV/lease handler;禁止重用 `S101AccountSession` 的旧 Cookie 替换流。CacheRoot 必须按账号隔离且路径固定,表快照取已加载的真实 `cfg.Tables`,不能沿用 Bindings 的硬编码 `"kv-loop"` 当正式版本。观察超时保持既有范围,Close 失败不得启动新 Context。
5. **31 模块**:`PlayerKVGameplayBindings.cs` 整文件仅 Editor 编译,`Snapshot`/`Bind` 要在 Player 目标可用;`LoginStage` 的 `EventShootingRangeData` 注册仅 Editor loop 下存在。需逐模块核 1–31 的容器注册、加载前静止点、Apply 主线程和版本快照;任一缺失失败关闭,不造空对象替代。原 `PFLoginAct` 登录成功后马上 `MailService.Init()` 与 `iapService.Init()`;`InGameMailService.Init()` 在已授权时马上异步 `mail/fetch`。`DevIAPService.Init()` 和 `AggRTIAPService.Init()` 目前只注册实时事件,首批不能以此推断全部购买流程已迁移。
6. **API 借用**:`CustomServerMgr` 的 `kvOnly` 仅放行两条 `__kv_validation`,普通 `CustomServerPost` 直接抛错;`PlayerKVAccountLease.SendAsync` 也只有这两条验证路径。正式租约应独立于验证路由,先定义确切 `(Method, canonical route)` 白名单。已核自动请求是 `POST mail/fetch`;若允许进入 `FishingStage`,还会由 `InitEvent` 触发 `POST friend/GetInvitedListCount`、由 `AntiCheatManager.CheckBan` 触发 `POST anti-cheat/CheckBan`;HomePanel 条件触发 `POST club/GetSelfClub`。这些是**候选清单而非完整首场景白名单**,其余 Club 路由仅按实际验收场景逐项批准,不能开 `club/*`。路由不得接受绝对 URL、编码绕过、查询、片段、路径穿越或重定向。绑定期间至 GameLoaded 前不发送普通业务请求;响应返回前后核 SessionVersion、同一租约、epoch、授权与关闭状态;401/403 只撤销当前身份,不重登 PF;Close 排水后才允许新租约。`Cookie` 旧属性不可把正式 KV Cookie 暴露为可替换流。
7. **退出和故障**:CP1 独立复核未重跑三类 cleanup-failure,也没有 GameLoaded 后 Close 失败的专项用例。CP2 必须注入/验证成功发布后的 Close 故障、旧 handler 无法排水、401/403、迟到响应、同账号新 epoch、切账号、Unity Quit;公开状态先撤销、屏障故障保持阻断,不声称底层资源已释放。卸载/重试/退出均观察同一 Registry;不可用 `async void` 异常吞掉失败。
## 审查前仍未解决的硬点
- 正式核准账号清单如何发放与防止“只改配置就扩大范围”,以及初次 KV 档存在性的无写判定(`newCreate=false` 本身不充分)。
- `UserId` 的全调用点分类:哪些要求 PF 十六进制 ID,哪些只需稳定玩家标识;Mail 缓存键、统计与好友/社团请求不能盲目替换。
- 登录后初始场景还会发起的所有 API 和 PF 管理器调用;目前只核到下述若干入口,不能推断首个界面已可无错运行。
- `FishingStage.StartAsync` 必走 `SurveyService` 构造(直接 `PlayFabMgr.Instance.GetLocalData`)、`LeadboardData.SetLvData`(PlayFab 排行榜调用);还触发上述 Friend/AntiCheat API。`SurveyService.GetSurveyMatchlist` 又把 `UserId` 按十六进制解析。进入场景前必须完成这几条业务转换或明确阻断阶段跳转,不能仅空值兜底。非 AGG 的 `DevIAPService.GetOrderUnreceived` 目前返回空字典,不能据此推断 AGG 支付路径可用。
- AGG SDK 登录与 S101 账号映射、关联账号归一化、头像写入及埋点键名;在独立方案前保持 KV 显式拒绝。
- Player 目标原有 `_cheatingScore` 编译错误的来源和独立修复白名单;正式验收必须 0 错误,不把 Editor 编译等同 Player。
以上硬点任一未在 CP2 完整方案给出可执行合同与测试矩阵,则 CP2 保持待评审,**不修改生产代码**。最终 CP4/CP5 要核旧 `PlayFabMgr` 自动执行及运行引用归零、删掉没有正式消费者的迁移接口和闲置实现,给最终流程图;本检查点不提前清理。