保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
74 lines
10 KiB
Markdown
74 lines
10 KiB
Markdown
# M2 CP3A 原句柄正式业务发布与 B01 提示接线完整方案(待独立审查)
|
||
|
||
日期:2026-09-29。本文是**编码前方案,不是编码批准**。受审基线为 [`M2-CP3A-受审基线.json`](M2-CP3A-受审基线.json),18 项 SHA256/长度;清单自身 SHA256 `F7311D9D923A61E8F665B021184C9C969C551EC1DDD96D2F23BF2F9CD1C98D49`。独立审查须先重算基线,再逐条判断合同、白名单和验收是否充分;主会话裁决后才可修改生产代码。
|
||
|
||
## 1. 目的、边界和顺序
|
||
|
||
CP2A 已有正式 101/102 现存 KV 档的登录与**原** `PlayerKVGameSessionHandle` 内部交接,`LoginStage` 仍阻止进入 `FishingStage`;其 Player 目标真实脚本编译尚未验证,CP2A 未关闭。B01 已在限定路径验证 `ShowNotice`、`IsOpenClubRed` 的固定句柄读写,但普通正式注册中的 `DefaultSettingService` 与 `ClubService` 仍是无句柄实例。CP3A 只把 CP2A 交接的原句柄一次性送达这两个**现有实例**,为后续按业务动作迁移提供一个真实、可拒绝陈旧调用的入口。
|
||
|
||
本批保持登录阶段停留,不公开 `IsLogin`/`IsAuthorized`,不发布身份事件,不开放 Mail/IAP、普通 API 路由、Survey、排行榜、Friend、AntiCheat,不进入首场景;不声称 101/102 正式游戏可玩。只承认两项提示键 `ShowNotice` 与 `IsOpenClubRed` 的 B01 语义。Club 的 `LureDonateData`、`GetSelfClub` 等其他调用仍未迁移。地址固定 `http://s101.local:18080/`;仅 Unity CLI Loop;不 Play、不操作正式账号、不打包。现有工作树全部保留。
|
||
|
||
## 2. 源码事实与选择
|
||
|
||
1. `UserService.KVLoad.cs` 在 `GameLoaded`、同一 epoch、同一原对象核验后才设 `kvHandoffHandle`;注销同步清空它,再启动异步撤销。`PlayerKVGameSessionHandle` 的读写自身会在撤销后返回拒绝;不需要第二次登录或从全局账号重新找 Owner。
|
||
2. `SubBoostrap` 在登录前注册并初始化一个 `DefaultSettingService` 与 `LocalNotificationService`。通知服务在登录前执行 `Init()`,未来授权回调会持有设置服务。`LoginStage` 注册一个 `ClubService`,31 模块绑定时会解析并加载这个对象。故登录后**替换容器实例**会遗留旧引用,不能作为接线办法。
|
||
3. B01 两个服务的固定句柄字段目前 `readonly`,仅带句柄构造进入 KV 分支;无参正式实例走旧 PF 分支。通知服务的 `promptSettings` 同样只在带句柄构造时设置;将设置实例稍后绑定而不更新通知服务,会触发现有实例一致性拒绝。
|
||
4. 采用**同实例、单次、失败关闭绑定**:在已加载原对象上完成只读预检后,固定原 handle;任何部分失败撤销本会话,保留失败证据,不尝试换新实例或继续 PF。此选择只服务 CP3A;不把它扩展成静态当前 Owner、通用服务定位器、全局存储路由或可热换号的绑定接口。
|
||
|
||
## 3. 时序和合同
|
||
|
||
### 3.1 正式模式出生即拒绝旧路径
|
||
|
||
`DefaultSettingService` 创建时从 `SubBoostrap` 已冻结的运行选择捕获是否要求正式 KV。非正式 PF 与 B01 显式带 handle 构造的既有语义不变。正式 KV 的无句柄实例,在 `ShowNotice` 读/写、`CheckRequestPermission` 开始或继续时必须报可诊断的未交接错误,**不调用** `PlayFabMgr.Instance`;本地音乐/语言等 `PlayerPrefs` 设置不属于 KV 提示键,不受此规则改变。
|
||
|
||
`ClubService` 构造时只读容器中由 `SubBoostrap.RegisterServices` 先行注册的 `PlayerKVRuntimeSelection`,捕获其 `IsKV`;不得依赖后续可变配置或新增 `ClubService → SubBoostrap` 反向引用。其 `SetOpenClubRed` 在正式 KV 尚无句柄时同样失败关闭,不调用 PF,也不先变更红点/内存。31 模块的 `Reset/Replace` 只负责加载数据,绝不自动绑定或重绑句柄。两服务在正式撤销后继续保留原句柄引用,下一次读写由原句柄拒绝,绝不能落入无句柄 PF 分支。
|
||
|
||
### 3.2 GameLoaded 后一次性交接
|
||
|
||
`LoginKVFormalAsync` 现有 `KVHandoffReady` 核验成功后、向 `PFLoginAct` 返回 `Done` 前,在 Unity 主线程解析**当前容器中的原** `ISettingService`、`ILocalNotificationService`、`ClubService`。类型及引用不符、通知初始化仍在进行、授权回调正在等待、已有不同句柄绑定、任一提示键读取非成功、Guard 拒绝或 Owner 已陈旧,均失败关闭。预检必须使用 CP2A 的 `kvHandoffHandle` 原对象;不得调用 `TryAcquireGameHandle`、创建新 Context 或重新发 S101 认证。
|
||
|
||
预检通过后,按同一原句柄一次性固定 Setting、Club 与 Notification 的引用关系。绑定方法只供该登录交接调用,不在业务 UI 中公开“取当前句柄”。已有同一原句柄的重复调用可作检查后幂等;不同句柄一律拒绝。`LocalNotificationService` 沿用登录前已完成的 `Init()` 实例,不重复初始化、订阅或请求系统权限;后续回调必须同时验证原 Setting 实例和其原句柄。绑定过程中不发送 KV 写入或业务 API。
|
||
|
||
任何绑定或后置一致性检查失败:不得返回登录成功;同步撤销 `kvHandoffHandle`/身份,调用现有 Coordinator 关闭并保留关闭失败屏障。即便 Setting 已绑定而 Club 未绑定,也只能让旧句柄失效,不能把同实例改绑另一个 epoch 或退回 PF。正式同进程重登在已绑定后拒绝,要求新的应用生命周期;不以重新注册服务“修复”部分失败。该限制须进入用户可见错误与测试事实,不能在 `finally` 清掉关闭故障。
|
||
|
||
### 3.3 存储及副作用
|
||
|
||
保持 B01 原合同:读区分 `Success`、`Found` 和值;读失败不是缺键。`ShowNotice` 只有值 `"1"` 为真;Club 提示键任何**已存在**的字符串都不重写。写必须检查 `Accepted` 并保留原 `PlayerKVEditResult`、票据和 `LocalCommitMayHaveSucceeded`;失败或撤销后不更新提示缓存、Club 红点,也不自动重试、Flush、回 PF。通知权限回调恢复时仍检查固定 Setting 与 Owner,不能作用于同账号新 epoch 的服务。
|
||
|
||
## 4. 精确实施白名单与禁止项
|
||
|
||
在完整独立评审与主会话裁决后,**最多**修改以下既有生产文件的限定位置:
|
||
|
||
| 文件 | 限定修改 |
|
||
|---|---|
|
||
| `Services/UserService.KVLoad.cs` | GameLoaded 后原 handle 发布、失败撤销与同进程再绑拒绝;不改 S101 协议/账号名单 |
|
||
| `Services/SettingService.cs` | 正式模式出生标志、同实例一次绑定、提示键绑定前/撤销后拒绝;不改其他设置 |
|
||
| `Services/ClubService.cs` | 仅 Club 提示键的同实例绑定、绑定前拒绝;不改 Club API/捐赠/签到 |
|
||
| `Services/ILocalNotificationService.cs` | 已初始化实例绑定固定 Setting、异步恢复检查;不重复系统权限或事件注册 |
|
||
| `SubBoostrap.cs` | 创建 Setting 时传递冻结的正式 KV 选择;不改启动模式选择或 PF 组件策略 |
|
||
|
||
测试白名单:可在 `KVGameLoadTests`、`KVGameLoadTests/ServiceHost` 与 B01 当前验证项目增添/调整**真实生产源码**的合成验收;B01 的 `ClubSlice.cs` 是历史切片,不能单独代表当前 `ClubService.cs` 已验收。若宿主无法编译真实通知/Club 代码,增加固定 Unity CLI Loop Editor 验证入口须单独列明文件与边界,不能用切片绿测冒充。除表内文件及本批测试外,`KVMgr`、Guard/Owner/Handle、Coordinator、账号、Schema、场景/Prefab、API 后端、cfg、旧 `PlayFabMgr` 均不得改。`LoginStage.RequestNextAct` 的 KV 停留闸必须保留。
|
||
|
||
## 5. 合成验收与发布门
|
||
|
||
| 用例 | 必须证明 |
|
||
|---|---|
|
||
| V01 | PF 缺省和既有 B01 带句柄构造行为不变;正式无句柄提示读/写及授权均拒绝,PF 请求 0、KV 写 0 |
|
||
| V02 | CP2A 同一原 handle、epoch、OwnerInstanceId 从 GameLoaded 交到三原实例;没有第二次认证/Get/租约或新 handle |
|
||
| V03 | 设置/通知/Club 原容器引用保持;通知 `Init()` 只一次,已有订阅与系统权限请求不重复;在途回调不能跨绑定 |
|
||
| V04 | 两键不存在、空串、`"0"`、`"1"`、其他值的 B01 语义、去重、读失败和 Accepted/Unknown/本地不确定结果保持 |
|
||
| V05 | 预检失败、两服务间部分绑定失败、logout、Quit、401/403、同账号新 epoch、跨账号回调、Close 故障都先撤销可用状态,后续无 PF 回退或新 Owner 污染 |
|
||
| V06 | 旧 B01 专项、CP2A 加载/ServiceHost 必要回归,Unity CLI Loop Editor 0 新错误;真实 Player 脚本编译若仍无不打包的 CLI Loop 方法,继续标未验证,不能宣称 CP2A/CP3A 发布通过 |
|
||
| V07 | `LoginStage` 不进入 `FishingStage`;Mail/IAP/Survey/排行榜/Friend/AntiCheat 的正式请求、旧 PF 存档调用仍为 0;真实 101/102 不操作 |
|
||
|
||
本批代码即使验收通过也只算业务句柄交付能力。首场景需要独立处理直接 PF 排行榜、Survey 读写、Friend/AntiCheat 与动态初始化及精确 API 契约;当前正式 Swagger 路径无可用契约,不可猜测路由响应或通过屏蔽功能开放场景。CP3 其余约 200 个候选仍按实际业务动作/失败语义分批,不由本方案自动批准。多键部分成功后的检测、差异对账和受控恢复仍为正式玩法前置条件。最终 CP4/CP5 必须清理闲置实现、旧 Manager 运行引用与 SDK 依赖,并核流程图。
|
||
|
||
## 6. 独立审查需明确回答
|
||
|
||
1. 登录前已初始化的 `LocalNotificationService` 是否允许按第 3 节原实例绑定?需要哪些额外竞态屏障,才能保证在途权限回调不拿到新句柄?
|
||
2. `ClubService` 构造时从容器捕获先行注册的 `PlayerKVRuntimeSelection` 是否可靠?若容器构造/解析时序使其不可靠,请指出最小修订及需补审的精确文件,不得在实施中猜。
|
||
3. 部分绑定失败后的“原对象保留陈旧句柄 + 本进程拒绝重登”是否完整闭合,或还需把所有实例同时置为不可用?
|
||
4. 白名单和 V01–V07 是否足够;真实 `ClubService` 与通知服务如何在 Unity CLI Loop/合成宿主中验证?若任何一项不够,请给阻断项和最小修订边界。
|
||
|
||
评审报告只读写独立文件;审查者不要改生产源码、共享入口、状态、TODO 或本方案。主会话收到报告后先复算 18/18 hash,再逐条裁决和冻结最终代码白名单,之后才进入连续实施与每检查点日志。
|