保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
54 lines
9.1 KiB
Markdown
54 lines
9.1 KiB
Markdown
# 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、主会话逐条裁决并写出精确白名单前,**不修改生产代码或测试代码**。
|