保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
10 KiB
M2 CP3A 原句柄正式业务发布与 B01 提示接线完整方案(待独立审查)
日期:2026-09-29。本文是编码前方案,不是编码批准。受审基线为 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. 源码事实与选择
UserService.KVLoad.cs在GameLoaded、同一 epoch、同一原对象核验后才设kvHandoffHandle;注销同步清空它,再启动异步撤销。PlayerKVGameSessionHandle的读写自身会在撤销后返回拒绝;不需要第二次登录或从全局账号重新找 Owner。SubBoostrap在登录前注册并初始化一个DefaultSettingService与LocalNotificationService。通知服务在登录前执行Init(),未来授权回调会持有设置服务。LoginStage注册一个ClubService,31 模块绑定时会解析并加载这个对象。故登录后替换容器实例会遗留旧引用,不能作为接线办法。- B01 两个服务的固定句柄字段目前
readonly,仅带句柄构造进入 KV 分支;无参正式实例走旧 PF 分支。通知服务的promptSettings同样只在带句柄构造时设置;将设置实例稍后绑定而不更新通知服务,会触发现有实例一致性拒绝。 - 采用同实例、单次、失败关闭绑定:在已加载原对象上完成只读预检后,固定原 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. 独立审查需明确回答
- 登录前已初始化的
LocalNotificationService是否允许按第 3 节原实例绑定?需要哪些额外竞态屏障,才能保证在途权限回调不拿到新句柄? ClubService构造时从容器捕获先行注册的PlayerKVRuntimeSelection是否可靠?若容器构造/解析时序使其不可靠,请指出最小修订及需补审的精确文件,不得在实施中猜。- 部分绑定失败后的“原对象保留陈旧句柄 + 本进程拒绝重登”是否完整闭合,或还需把所有实例同时置为不可用?
- 白名单和 V01–V07 是否足够;真实
ClubService与通知服务如何在 Unity CLI Loop/合成宿主中验证?若任何一项不够,请给阻断项和最小修订边界。
评审报告只读写独立文件;审查者不要改生产源码、共享入口、状态、TODO 或本方案。主会话收到报告后先复算 18/18 hash,再逐条裁决和冻结最终代码白名单,之后才进入连续实施与每检查点日志。