Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-双提供方独立KV用户服务抽离方案-待完整评审-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

54 lines
9.1 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 双提供方独立 KV 用户服务抽离方案(待完整评审)
日期:2026-09-29。状态:**候选方案;未获生产代码白名单**。本方案只处理正式 KV 登录、加载第 31 模块、原 handle 交接和退出的结构抽离;首场景、业务接口语义、公开身份与存档调用全量替换另批评审。依据[用户最新双提供方约束](M2-双提供方用户服务切换与KV独立实现约束-2026-09-29.md)和[CP2B-T 限定关闭裁决](M2-CP2B-T独立实施复核主会话裁决与限定关闭-2026-09-29.md)。
## 已核源码事实与问题
1. `SubBoostrap.RegisterServices` 已从 `SaveBackend` 捕获一次 `PlayerKVRuntimeSelection`,KV 不创建 `PlayFabMgr`;`LoginStage` 却始终向 `IUserService` 注册 `PlayFabUserService`(AGG 为其子类)。`PFLoginAct` 对其下转,正式 KV 判断、错误和原 handle 就绪均读旧类;`QuitAct` 也向旧类下转。
2. 正式 KV 会话、Coordinator、关闭和第 31 模块状态位于 `PlayFabUserService` 的 `UserService.KVLoad.cs` partial。`PlayerKVGameplayBindings.Bind(PlayFabUserService)` 的第 31 模块直接调用该类的 `ResetForKVLoad`/`ReplaceFromKVDecoded`,事务固定 31 模块并同步主线程 Apply;这些安全约束不能松动。CP2B-T 的普通请求传输在正式原 handle 交接后才发布。
3. `IUserService` 同时包含共用登录资料、`EntityKey` 和以 `playFabId` 命名的其他玩家资料方法。`EntityKey` 的实际非注释读取在 Duel UI;`UserId` 在社交、缓存键等处具有 PlayFab 身份含义。正式 KV 目前不公开 `IsLogin`/`IsAuthorized`/`UserId`,也未取得可代替 `EntityKey` 的可信身份合同。`GetDefaultName` 对长度小于 5 的 ID 使用 `id[0..5]`,不能直接把 101/102 当旧 PlayFab ID。第 2 个加载模块 `SmallGameData` 的构造函数要求 `IUserService`;即使当前 Apply 只调用 Reset/Replace,纯粹不注册该接口也无法在现有 DI 下构造这 31 个模块。
4. 全客户端约有 213 处 `IUserService` 文本引用、223 处 `PlayFabMgr.Instance` 调用分布于 82 个文件;登录、资料、存档不是一次改注册就能全链通过。`AggUserService` 仍继承原类且 KV 明确不准入 AGG。Editor 的 `kv-loop` 依赖 PlayFab 身份,是单独的测试通道,不能充作正式 KV 路线。
5. `LoginStage.RequestNextAct` 对 KV 仍阻止进入 `FishingStage`;这个门在本批保持。正式地址固定 `http://s101.local:18080/`,准入仅 101、102 且有现存 KV 档。本机服务器代码不作合同来源,也不修改。
## 方案比较与建议
| 方案 | 做法 | 取舍 |
|---|---|---|
| A:继续在 PlayFab partial 内分支 | 只改 DI/名称,仍由旧类承载两种登录 | 改动少,但 KV 仍依赖旧具体类和 SDK;不满足双提供方目标,否决。 |
| B:一次性替换全量 `IUserService` 与 82 文件存档调用 | 同批引入独立实现并打开首场景 | 最终状态直达,但线上资料/社交/排行榜/写入合同未核;破坏面过大,当前否决。 |
| C:按登录纵切,随后按业务合同迁移(建议) | 保留原 PF 类及 SDK;本批抽离正式 KV 登录/第 31 模块/退出,冻结启动选择并保留阶段闸;后续批次按功能迁移 `IUserService` 和存档调用 | 每批可验证两模式;保留必要的临时门控,最终按清单销账。需先解决 `IUserService` 注册和暂不支持的成员语义,不能用静默空实现。 |
## 建议的目标边界
```text
SaveBackend 一次捕获 → PlayerKVRuntimeSelection(会话内不可更换)
├─ playfab:PlayFabUserService / AggUserService → PlayFabMgr → PlayFab SDK
└─ kv:独立 KV 用户服务 → PlayerKVGameLoadCoordinator → 原 PlayerKVGameSessionHandle
├─ 第 31 模块接收 KV 身份/初始化意图
└─ CustomServerMgr 正式租约(仅交接就绪后)
```
- 启动选择是唯一来源;登录阶段仅按该选择注册对应用户服务。KV 失败不回退 PF;PF 模式不构造正式 KV 用户服务、不打开正式租约。AGG 维持 PF 且在选择 KV 时启动失败,不静默回退。
- 新 KV 类不得继承 `PlayFabUserService`、调用 PlayFab SDK、创建活动 `PlayFabMgr`,不得制造假的 PlayFab ID、`EntityKey` 或授权态。正式加载成功仍仅显示原 handle 就绪,直至真实业务合同和公开身份另审;当前 `FishingStage` 闸保留。
- 第 31 模块仅抽出“Reset、Replace、Discard 初始化意图”这组已有实际调用者所需的窄契约,让正式 KV 用户服务和既有受控验证入口各自提供接收对象。31 项顺序、Guard、Apply/回滚、主线程、旧 epoch 失效、通知后释放 Guard 与原 handle 发布时点保持。不得新造第二套 Coordinator、开关或存档管理器。
- `PFLoginAct` 用提供方中立的登录结果或窄 KV 会话能力处理就绪/错误,不再以 `PlayFabUserService` 下转判断正式 KV;输入账号写入服务的接口也要明确。`QuitAct` 经同一会话能力观察关闭。PF 原登录、重试、Mail/IAP 和退出继续原行为。正式 KV 401/403、注销、应用退出均先使租约/身份失效,再观察关闭;在途旧回包不能恢复新会话。
- `IUserService` 的字段和方法需要逐项归类:共用身份/资料;PF 专属 Entity/Profile;当前仅 PF 数据查询;尚无 KV 线上合同。**不批准把 PF 专属方法在 KV 类中做空、返回默认成功或转调 PlayFab**。第 2 个加载模块对当前 `IUserService` 的构造依赖必须同时解决;若现阶段无法为某成员给出真实 KV 语义,应以完整审查裁决“独立 KV 类显式失败并保持首场景闸”或“把构造依赖拆成窄契约”的最小方案,过渡路径登记最终清账。
- 存档访问继续沿现有两路:PF 的 `PlayFabMgr` 和 KV 的原 handle/`KVMgr`;本批不把 223 处旧调用机械替换,也不删仍供 PF 模式使用的类。`PlayerKV*` 改名和 `Core/KVMgr → Services/PlayerData` 物理依赖修正按既有延期裁决另批处理。
## 分批实施与验收门
**DP1(拟申请评审的最小批)**:只抽离正式 KV 登录/关闭、31 号模块接收和登录/退出阶段的旧类下转;保留受控验证和 `kv-loop` 原测试通道,但不能让它们成为正式 KV 的身份依赖。精确文件白名单须由审查对源码调用图确认后落盘;候选核心为 `UserService.cs`、`UserService.KVLoad.cs`、`PlayerKVGameplayBindings.cs`、`LoginStage.cs`、`PFLoginAct.cs`、`QuitAct.cs`、新增独立 KV 用户服务与必要合成测试。`SubBoostrap.cs`、现有选择类型只有在审查确认 DI 或冻结缺口时才入白名单。禁止触碰本机服务器、场景、Prefab、付费、社交和未审业务路由。
**DP1 验收**:同一构建分别选 PF/KV;PF 仍经过真实 `PlayFabUserService → PlayFabMgr → PlayFabClientAPI`;KV 只构造独立正式服务且 `PlayFabMgr.HasLiveInstanceForKVLoad` 为假,101/102 有档加载到原 handle 并停在登录阶段。无档/非名单/错误地址/AGG-KV 拒绝,401/403、Logout/退出、失败回滚、旧 epoch/在途响应和双次登录均不串号;第 31 模块与 30 个前序模块仍按原顺序。合成测试、ServiceHost 双配置、Unity CLI Loop Editor 编译与可取得的真实 Player 编译分别记录;真实网络和首场景没有跑就不得标记通过。
**DP2 及以后(不在 DP1 白名单)**:先获得正式线上路由、身份、响应/错误/幂等合同,再按相同业务链成组迁移 `IUserService` 的资料/社交方法及 `PlayFabMgr` 直调;同时测 PF/KV 两模式读写、会话/重启。打开首场景前完成 Survey、排行榜、Mail、Friend、AntiCheat 等自动调用的真实合同和语义裁决。最终解除或删除所有开发期冻结路径、删闲置 KV 过渡 partial/测试入口,保留 PF 能力;按双提供方约束文件末尾清单逐项销账。
## 完整评审需集中裁决的未决点
1. DP1 中 `IUserService` 是否仍必须由 KV 服务实现以满足 `SmallGameData` 与其他 31 模块构造/注入?请追踪 `PlayerKVGameplayBindings.Bind` 所 Resolve 的模块、`PFLoginAct`、`QuitAct` 真实实例化路径;比较显式失败且阶段封锁的 KV 实现与窄依赖拆分,给出不靠空方法、不伪造 PF 身份的最小 DI 方案。
2. 第 31 模块的窄契约应如何定义并被正式 KV 与受控验证复用?核出需要保持的原始状态、通知时机、意图生命周期和回滚语义,避免复制完整 PF 服务。
3. `IUserService.entityKey`、`UserId`、`ApiPlayerId`、`InternalId` 在阶段闸未开前以及未来首场景的明确语义分别是什么?哪些成员必须延期到可信线上合同后才可开放?Duel 的两处 `EntityKey` 读取和短 ID 默认名应列出处理批次。
4. PF/AGG 与 Editor `kv-loop` 保留行为、正式 KV 失败不回退、重复启动选择和场景/Prefab 序列化引用应如何验证?若方案需超出上述候选白名单,请明确原因和新增验证。
在独立报告核实受审 hash、主会话逐条裁决并写出精确白名单前,**不修改生产代码或测试代码**。