Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-CP2A正式登录与原句柄交接完整方案-待独立审查.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

58 lines
11 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 CP2A 正式登录与原句柄交接完整方案(待独立审查)
日期:2026-09-29。依据修订2主会话裁决、CP1限定关闭及用户本轮两项明确选择:**首批仅账号 101、102 且已有 KV 档**;**本检查点只验正式登录与原 handle 交接,在进入首场景前停住**。本文是 CP2 的一个完整可独立验证的子边界,尚未获原 ZCode 审查/主会话批准,不能编码。CP2B 首场景、CP3 业务迁移、CP4 旧运行引用清零、CP5 无用代码清理均不因本方案自动通过。
## 1. 可观察目标和停止点
默认/缺省 `SaveBackend=playfab` 的启动、登录、Mail/IAP 和进入 `FishingStage` 行为保持原状。仅 `SaveBackend=kv` 且非 AGG 目标、账号为 101/102、正式 API 恰为 `http://s101.local:18080/` 时,启动前不创建 `PlayFabMgr`,完成**一次** S101 登录、现存 KV 档读取与 31 模块 Apply,返回 CP1 同一原 `PlayerKVGameSessionHandle` 的内部会话交接检查点。此状态不进入 `FishingStage`、不初始化 Mail/IAP、不发布 `EvtAPISvrLoginSuccess`、不开放业务 API 或存档写入 UI。登录失败、无档、新建、Schema 不符、关闭失败均留在登录阶段且撤销身份/句柄,不回退 PF、不自动重试写入。`Logout`/应用退出同步撤销并登记异步排水;排水失败不准下一次 Context。
该“正式”只表示 Bootstrap/非 Editor 专用工厂/普通登录入口可到达,不表示已发布玩法。首场景的 Survey 构造与排行榜旧 PF 调用、Friend/AntiCheat/Club 路由另见[只读清单](M2-CP2首场景自动依赖核查-2026-09-29.md),本检查点以双重阶段阻断保护,不能靠运行时异常当边界。
## 2. 精确设计合同
### 2.1 启动与账号准入
- `Boostrap.AfterAssetUpdateV2` 将已加载 `config` 传入 `SubBoostrap.RegisterServices`。该方法在任何存档组件创建之前一次捕获精确 `playfab`/`kv`,注册不可变 `PlayerKVRuntimeSelection`。未配置为 PF;其他值在启动处停止,运行期间配置被改不切换当前后端。KV 若为 AGG 目标在启动处明确失败,绝不创建 PF 组件后继续。既有 Editor loop 在 `RegisterServices` 内稍后才把配置临时设为 `kv-loop`;正式捕获不得误拒该 Editor 许可流程,其登录分支仍先由原 `UNITY_EDITOR && !SERVICE_HOST` 入口处理。
- `kv` 分支核 API 的规范 URI 字符串恰为 `http://s101.local:18080/`,只创建必要的 KV 退出生命周期接收者,不创建/唤醒旧 `PlayFabMgr`。PF 分支组件顺序及旧 `Awake` 不变。正式入口不读取 Editor permit、claim、备份和 `PlayerKVGameplayLoop.Enabled`。
- 在 S101 请求前规范化 CustomID 为无符号十进制正整数的最短字符串,**仅 101/102**通过;名单为本批受审代码常量,后续扩员必须另审,不从运行时可改配置读取。响应的 `PlayerId` 必须仍等于请求 ID,`NewlyCreated` 必须为 false,`playerdata/Get` 的非空档必须经 31 模块完整 Schema 验证。`newCreate=false` 单独不算已有档。任一失败撤销关闭;不得创建空档或远端写入。
### 2.2 三传输与原对象交接
- 给 `PlayerKVGameLoadCapability` 增**正式**工厂,保留 `CreateForValidation` 原签名和 Editor-only 限制。正式工厂从上述已捕获地址和 `Application.persistentDataPath` 形成根路径;`PlayerKVCacheIdentity` 已按 API 指纹和 PlayerId 隔离,不再自造第二层账号目录。`cfg.Tables` 必须在 `SubBoostrap.AfterAssetUpdate` 注册完成后才取快照;Bindings 对正式路径接受明确的本地 decoder 版本令牌,Editor 默认 `kv-loop` 不变。版本令牌仅校验本进程快照一致,不能冒充服务端 Schema 协议版本。
- 正式 auth、KV、account lease 各自有独立 `HttpClientHandler`,共享**同一个** `CookieContainer`,`AllowAutoRedirect=false`,所有权只转交一次;工厂先验证三者不共享,再允许认证发送。正式 handler 不能走 `CreateForValidation` 的“拒绝真实传输”构造;测试替身不能经 Editor permit 混入正式运行。任何部分构造失败逐个 Dispose,失败保留为关闭屏障。
- 正常 `PlayFabUserService` 在捕获的 KV 模式下获得一次性的正式加载准入;当前 `kvControlledInstance` 专属验证语义保持,不能简单改为 true。Coordinator 的 `unstarted` 前置必须在正式模式检查无旧 PF 实例、未发布 InternalId、尚无前一会话,并继续在 Apply 前复核。第 31 模块 `ResetForKVLoad`/`ReplaceFromKVDecoded` 仅在正式加载窗口内允许,已公开账号/其他后端/第二次 Apply 全拒。非 AGG 身份快照使用 `channel=none`、空 Linked、`NewlyCreated=false`,服务端 `NewlyCreated` 仍须独立拒绝。使用 `LoadForGameAsync` 共用 CP1 主体;`TryAcquireGameHandle` 仍只由 Coordinator 调用,UserService 按返回 epoch 调 `TryGetCurrentGameHandle` **一次**并保持同一对象。CP1 三接口均有真实消费者,不另造全局 handle、缓存、同步器或 PF 同名转发。
- `CustomServerMgr` 增正式租约绑定/清除,但 CP2A 的 API 业务路由集合为空;绑定时立即废弃旧空闲 transport,`Cookie` 不对外提供新 jar,任何 `CustomServerPost` 在 KV 模式仍显式拒绝。正式认证只有 Context 内那一次 `S101AccountSession.LoginAsync`;绝不调 `LoginApiServerCore` 建第二会话。租约和 handle 都由同一 Context/epoch 关闭。CP2B 才对 `mail/fetch` 等逐条路由补审;不能在 CP2A 偷开 `club/*` 或所有 POST。
### 2.3 登录状态、阶段闸与关闭
- `PlayFabUserService.LoginWithBackend` 在原 Editor loop 优先分支之后,用启动捕获选择正式 KV 分支,不走 `LoginPlayFab`、`SetMMTestValue`(其十六进制 PF UserId 解析及 PF 写入均不适用)、`LoginApiServerCore`、TapJoy、YinDun PF 资料路径。构造函数已有的 `EvtAPISvrUnauthorized` 订阅在 KV 模式不得执行“PF 已登录则重登 S101”的旧回调。正式加载通知不得调用未审 `OnInitData` 订阅者;无订阅或唯一经受审登记的接收者才可通过。31 模块 Apply 后仅产生内部 `KVHandoffReady(epoch, originalHandle)`,不将 S101 十进制 ID 写成 PF `UserId`,不宣布 `IsLogin`/`IsAuthorized` 或发布 API 登录成功事件;这些公开状态留 CP2B 随首场景和业务调用一起评审。调用方可取得非权威历史结果,但旧 epoch 不能重新查询“当前账号”换 Owner。
- `PFLoginAct` 的 `rtService.WaitForAPISvrLogin()` 在 KV 首段也不注册(该订阅会一直等尚未发布的事件),收到 KV 内部成功状态后停止加载进度,在登录阶段保留可识别的“准备完成/暂不进入游戏”结果,不调用 `MailService.Init`、`iapService.Init`、`Unload`。`LoginStage.RequestNextAct` 再按捕获模式双重阻断转入 `FishingStage`,防止后续 UI 误调用 `Unload` 绕开。PF 分支完全不变;KV 加载失败不得进入原无限重试/改 ID 对话,应显示失败并要求显式重启或登出,保持关闭屏障。此检查点的用户界面文案不暴露 handle、Cookie 或内部存储细节。
- `Logout`、应用退出/销毁对 KV 分支先同步清内部 ready、账号、handle 和 epoch,再调用 Coordinator.Revoke/Registry 排水;不调用 PF 忘凭据/旧缓冲同步。Close 异常保持阻断状态,不能在故障后创建下一个 Context。后台回调按请求时捕获的 ID/epoch/原句柄判定,旧响应绝不重新发布;不可在锁内 await。
## 3. CP2A 精确生产白名单候选
| 文件 | 只允许的变化 |
|---|---|
| `Client/Assets/Scripts/AOTScripts/Boostrap.cs`、`Client/Assets/Scripts/SubBoostrap.cs` | 传入/捕获配置,PF/KV 组件创建分流与 KV 退出钩子 |
| 新 `Client/Assets/Scripts/Services/PlayerData/PlayerKVRuntimeSelection.cs`(含 `.meta`) | 不可变 backend/固定 API/101、102 准入;不持有 Cookie/handle |
| `Client/Assets/Scripts/Services/PlayerData/PlayerKVControlledLoadCapability.cs`、`PlayerKVLoginContext.cs`、`PlayerKVGameLoadCoordinator.cs` | 正式三 handler 工厂与既有档门;Context/Coordinator 如确需身份/epoch 检查仅增加受限入口,保留 CP1 与验证旧合同 |
| `Client/Assets/Scripts/Services/PlayerData/PlayerKVGameplayBindings.cs`、`Client/Assets/Scripts/Stages/LoginStage.cs` | 同一 31 模块绑定进入 Player 编译、补实际注册、双重阶段闸;Editor loop 路径不变 |
| `Client/Assets/Scripts/Services/UserService.cs`、`UserService.KVLoad.cs`、`Client/Assets/Scripts/Stages/PFLoginAct.cs` | 正式非 AGG 登录窗口、内部交接与明确停留、Logout;PF 分支不变 |
| `Client/Assets/Scripts/Core/CustomServerMgr.cs` | 正式租约绑定/清除与空业务路由拒绝;验证两路由原义不变 |
| 新 `Client/Assets/Scripts/Services/PlayerData/PlayerKVRuntimeLifetime.cs`(含 `.meta`) | 若现有根对象生命周期没有合适位置,仅负责 KV 撤销/登记排水,不另造定时器 |
不在白名单:`PlayFabMgr.cs`、`KVMgr.cs` 四入口/Guard 方法体、AggUserService、FishingStage/Survey/Leaderboard、业务 API/活动/商店、Editor permit/受控循环、实际账号配置或数据文件。若实现证明某白名单项多余,删除它;若必须动白名单外文件,先补方案与独立审查。测试宿主可新增 CP2A 合成测试,不借真实 101/102 操作来填单测空白。
## 4. 验收矩阵和停手条件
| 类别 | 必须验证 |
|---|---|
| PF 不回归 | 缺省及明确 PF 的 Bootstrap、旧组件、PF 登录、Mail/IAP、阶段跳转行为;Editor 旧 loop 与 CP1/B02 回归 |
| KV 准入 | 未知 backend、AGG KV、地址不符、100/103、非规范数字、101/102 新建/空档/Schema 不符全部零业务写且不入场景 |
| 成功交接 | 合成 101/102:仅一次 `account/Login`、一次 KV 开启、31 模块、原对象同一 `OwnerInstanceId`/epoch;Guard 释放后内部读取可用,公开玩法仍关闭;无 PF 组件/凭据/旧40秒循环 |
| 竞态与关闭 | 加载中取消/Logout、Apply/notify 失败、同账号新 epoch、旧登录回包、GameLoaded 后 Close 故障、handler Dispose 故障、退出注册与观察;失败后无旧对象再发布 |
| 网络 | 三 handler 同 jar 不同实例、重定向拒绝、业务路由全拒、无旧 Cookie 替换;没有正式地址请求或账号写入的合成测试 |
| 目标编译 | Editor 与 Player 目标均 0 错误;历史 `_cheatingScore` 另列来源/最小修复审查,不把已有错误算通过;Unity 自动化仅 CLI Loop,不打包 |
只有原 ZCode 对**整个 CP2A** 及白名单作独立方案审查、主会话先复核受审 hash 再逐条裁决后,才能修改生产代码。CP2A 实施完还须批末独立实施复核;其关闭只表示登录与原 handle 交接,不启用首场景。CP2B 完成首场景/路由与身份发布后,CP3–CP5 继续逐域迁移并最终删除闲置实现。现有工作树全部保留。