Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-CP3A原句柄正式业务发布与B01提示接线完整方案-待独立审查.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

10 KiB
Raw Blame History

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. 源码事实与选择

  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,再逐条裁决和冻结最终代码白名单,之后才进入连续实施与每检查点日志。