Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-管理器同生态位替代策略-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

11 KiB
Raw Blame History

M2:KVMgr与PlayFabMgr同生态位替代策略(主会话意见,待完整方案审查)

日期:2026-09-29。用户要求KVMgr与PlayFabMgr处于同一生态位,考虑先把PlayFabMgr所有函数替代并切换调用,最后处理保护。本意见基于当前源码和架构复审裁决,用于修订R1完整方案;没有批准代码修改。

2026-09-29 用户纠偏,以下为本文件最新有效顺序:我先前把管理器迁移与Guard归属、目录移动、生命周期重构放进同一实施批,增加了难以核对的变化;撤回这种混做安排。本阶段以现有行为为基线,逐成员列“旧入口→新入口→调用方→等价/必要差异”,完成存档能力承接和调用迁移。PlayerKVGameLoadGuard、现有KV Handle/Owner和旧PF路径在迁移期间保持原行为;不新造Guard、不移动Guard、不搬KVMgr文件、不顺手重写定时器或Awake。只有被具体旧调用阻断、且有用例证明的最小兼容改动才能列入迁移白名单。Guard归属、物理分层和代码清理留到迁移完成后的独立优化方案;此前正文中相反的“先归位”“同批生命周期适配”建议不再作为当前实施要求。生产代码仍须等修订后完整方案评审、逐项裁决。

Guard事实与处理

问题 已核实事实与本阶段决定
新KVMgr为何有Guard 是本次upserver迁移引入的保护。9月15日前的仓库版本无Guard。9月28日提交0eb43b2b4同时新增PlayerKVGameLoadGuard.cs、PlayFabMgr旧方法中的Guard调用及四个KV入口;之后B02只是把四个带Guard的入口原样移到KVMgr。此前我只说“搬迁前已有”,遗漏了真正来源,现予纠正。
旧PlayFabMgr是否有Guard 原始旧类没有。9月15日的仓库树f40a816cc和该文件上一次提交7a7c1e9f5均未出现PlayerKVGameLoadGuard;当前旧类有11处,全部来自9月28日迁移提交。
它是否必须存在 对原始PlayFabMgr不是必须的,也不是KVMgr天然职责。当前受控KV加载实现在31模块Apply期间持有它,并用Violations==0判副作用;四KV入口的拒绝结果和计数已有验证用例。因此现在直接删除会改变已审加载合同;先保持到迁移完成,再以实际运行/测试证据判断能否移除或换成更小的边界保护,不预设永久保留。
KVMgr调用Guard是否等于反向调用业务Service 否。Guard实现仅依赖System/Threading,由加载协调器进入与释放;KVMgr没有调用UserService或加载协调器。文件放在Services/PlayerData目录造成的层级疑虑是真实设计问题,但目录调整不是迁移前置。

“先移植”的验收口径:先实现旧对外存档能力在KV路径的对应入口,按业务域替换调用,逐项核对保存结果和旧行为;旧PF专属协议逻辑在切换完成前维持原路径。CompareDatas、Awake、40秒PF定时器不得未经对照就复制到KVMgr,也不得在同一迁移补丁里悄悄删除;需分别列原触发时机、作用与KV路径的必要替代,评审后作为迁移检查点处理。把PF SDK代码直接改名塞进KVMgr,只会把第二次后端迁移推迟,不算已完成KV替代。读失败/缺键、写拒绝/不确定、原票据确认及固定handle属于新旧后端合同的必要差异,必须在替换相应调用时显式适配;这不是可后置的“优化”。

结论与边界

同一生态位成立,含义是“业务存档入口由KVMgr承接,最终PlayFabMgr运行调用归零”,不是把旧类逐字复制、保留Instance全局当前账号或维持旧返回类型。当前KVMgr只搬了四个显式KV入口,尚未替换旧PF存档调用,之前按B01/B02小段推进没有达到用户期待的管理器完整接管。

PlayFabMgr.cs实际上混了存档门面、PF本地与远端合并、定时同步、Unity生命周期、PF SDK发送和Editor测试循环。KVMgr只拥有存档门面;其余能力必须分别归到会话加载/迁移政策、现有KV Owner同步、上层生命周期或测试宿主。目标是完全替代运行能力与调用,保留旧文件但停用自动执行,不是要求每个私有函数都有一个KVMgr同名函数。

flowchart LR
  B[业务/玩法] -->|旧存档调用:待清零| P[PlayFabMgr]
  B -->|显式原handle与结果| K[KVMgr:存档门面]
  L[登录/加载/退出Service] -->|发布、撤销原handle| B
  L -->|认证/加载阶段| H[KV Handle/Owner]
  K -->|读/写/Flush/票据| H
  P -->|旧PF分支:切换前保留| PF[PlayFab SDK]

PlayFabMgr方法逐类去向

现有成员 真正职责 替代去向与能否同名
GetLocalData(key): string/null 存档键读取 已有KVMgr.ReadKV(handle,key),必须保留成功/缺键/失败三态;不能同签名返回null掩盖读错。
UpdateUserDataValue(key,data,remoteStore): void 本地编辑+远端待同步 已有KVMgr.WriteKV(handle,key,data,remoteStore),调用方要处理Accepted、拒绝、本地提交不确定及票据;旧void合同不可直接复制。
SyncDataImmidiate(key,data): Task<bool> 单键立即远端提交 用同一原handle的WriteKV+FlushKVAsync+WaitForKVTicketAsync观察该次原票据;bool不足以表达Unknown/部分确认。消费/领奖须按业务动作收口。
CompareDatas(remoteDatas,userId) PF远端与PlayerPrefs时间戳合并 归历史档迁移/正式加载来源政策,不进入KVMgr常规读写API;不能对KV档再做PF式双源合并。
SyncData、私有定时循环、InitBuffer、UpdateFaild 旧PF缓冲和SDK发送 现有KV Owner/Coordinator负责缓存、同步和状态;不复制第二套队列、计时器或PF失败回调。
Awake、OnApplicationPause/Quit、OnDestroy Unity自动生命周期 上层会话Service在登录、暂停、退出时协调Flush/Close;KVMgr保持非MonoBehaviour。全部旧调用迁出后停止创建旧组件。
Instance与Editor Start/StopKVGameplayLoopSync 隐式全局入口/受控测试循环 正式业务显式传原handle;测试循环继续受Editor许可限制,收口时改测试宿主,不变成KVMgr生产单例。

CompareDatas、Awake与40秒PF定时器的具体落点

现状:三者都没有迁走。 CompareDatas仍在Core/PlayFabMgr.cs,由Services/UserService.cs::InitData在PF登录取得UserData后调用;它读写按userId命名的PlayerPrefs时间戳/本地字典,可能回写PF。Awake仍在PlayFabMgr,注册_instance、建立5个PF缓冲区并启动40秒UpdateUserDataValue(CancellationToken)循环。SubBoostrap.RegisterServices仍AddComponent<PlayFabMgr>()。KV PlayerKVSessionOwner.FlushAsync已能执行一次固定Owner同步,但没有自动40秒调度;PlayerKVGameLoadCoordinator.Revoke负责关闭,并不等同“退出前远端Flush确认”。Editor限定玩法还借PlayFabMgr.StartKVGameplayLoopSync启动旧计时器。

逐项审查问题(非预先选定的R1实现):

  1. CompareDatas不复制到KVMgr。正常KV登录改走现有PlayerKVLoginContext.PrepareAsync读取KV快照→PlayerKVSaveSchema.Decode→31模块PlayerKVGameLoadTransaction.Apply,由上层UserService/加载协调器决定何时发布成功。PF历史档与PlayerPrefs双源合并只属于一次性历史迁移政策:若其他账号确实需要,另列受控LegacyPlayFabSaveImport工具/流程,读取原数据、校验目标和备份后导入;未确定政策前保留旧PF分支,不在正常KV登录自动调用CompareDatas。
  2. Awake目前创建旧单例、缓冲并启动PF循环;迁移方案先列出其实际触发链、KV现存能力和停用旧组件的条件,不在本轮先建新组件。正式切换时必须证明登录、暂停、退出仍有对应行为,再决定是否需要最小接线。
  3. 40秒PF定时器目前仍在旧Mgr;先核对KV Owner现有同步触发与实际需求,列定时/暂停/退出的可观察行为和验证条件。不在移植时预设新的定时器或生命周期组件;如果现有能力不能满足切换条件,再单列必要替代并评审。

KVMgr仍只占存档API生态位,不反向调用UserService。正式默认启用前,Unity生命周期和Editor测试循环是否仍依赖旧Mgr必须逐项核实并完成替代验收,不能把停用旧组件等同于行为已替代。

现有Roslyn矩阵为224语法候选/81文件,其中UpdateUserDataValue164、GetLocalData50、SyncDataImmidiate2、CompareDatas1,其余是旧Mgr内部及SDK调用。数字是语法候选,不是224项独立审批,也不含所有Unity序列化、反射或别名引用。最终须另查组件创建、Prefab/UnityEvent、动态调用与直接PlayFab SDK。

两处SyncDataImmidiate分别在钓鱼鱼饵消费和转盘券消费;它们必须跟所属扣费/奖励动作一起验证,不能仅以“新管理器同名方法返回true”证明替换完成。

可执行的顺序:先能力,后调用,保护分层处理

  1. 一次完整方案评审后,先定KVMgr的存档合同:保持现有文件位置与单一Guard,明确固定handle来源和Read/Write/Flush/Wait如何覆盖旧三个对外存档方法。优先复用四个现成入口;不为了名字相似新增薄转发函数。
  2. 在同一实施批按业务域成组替换调用方:先已关闭B01代表链,然后核心进度及奖励、活动/商业、身份/加载边界;每组列key、首次模型修改、结果处理和回归。可以先把全部管理器能力实现好,再连续替换调用方;不能只做PlayFabMgr.Instance→KVMgr的文本替换后统一补错。每检查点更新日志,合同内错误直接修复,不按单函数反复审批。
  3. 不可后置的最低保护与替换同时生效:原handle归属/撤销、加载期拒绝、读三态、写Rejected/uncertain、原票据确认、迟到回调旧epoch隔离。否则新调用会把读失败当空档、把未知提交当成功或重复扣费。现有保护优先复用,不新建一套。
  4. 可在调用替换后、正式默认切换前收尾的保护:T08多键部分远端保存检测、差异对账和受控恢复、长期票据容量、历史账号/新账号政策、完整登录/退出组合回归。这些不能延到已经正式启用之后。
  5. 全部需要保留的存档业务改用KVMgr且正式路径通过后,令PlayFabMgr的运行引用、组件创建和自动同步归零;直接PF SDK外围依赖另清理,最后才移除包。保留PlayFabMgr.cs历史文件按用户目标处理其编译依赖。

不建议“一次性实现旧类全部同名函数→全局替换81文件→最后才加任何保护”。它看似减少决策,实际让旧null/void/bool隐藏KV错误,并使失败定位横跨业务。可接受的粗粒度是一份总架构/合同评审+业务域连续实施检查点,而不是每个调用点各审一次。

下一步在主会话:把此方法去向表和修复Todo合入R1完整方案,列精确白名单和验收矩阵后送原ZCode审查。固定地址http://s101.local:18080/;使用Unity CLI Loop,不使用UnitySkills,暂不打包。