Files
ft/CodexOutput/upserver/阶段4存档接线分期与4A方案-2026-09-23.md

15 KiB
Raw Blame History

阶段4存档接线分期与4A方案

最新补充(4B1关闭):4B1独立实施复核已完成,主会话核对6项交付及68项范围hash并逐条裁决三条P3,正式关闭4B1,见关闭裁决。下一步为4B2完整方案及独立评审;4B2/4B3未启动,整个4B/G4未完成。下方待审、待复核均保留为历史时点。

历史补充(4B1交付,现已关闭):4B分期及4B1方案已独立审查双PASS,主会话裁决后完成限定实现与技术验证,见《阶段4B1交付与验收矩阵-2026-09-23.md》。当时下一步为外部独立实施复核;4B2/4B3未启动,游戏/G4启用门不变。

最新补充(2026-09-23):4A独立实施复核已通过并由主会话逐项裁决关闭,见《阶段4A实施复核裁决与关闭-2026-09-23.md》。下一步为《阶段4B分期与4B1登录存档准备方案-待评审-2026-09-23.md》的外部独立审查;仅方案准备、未编码。下文4A“待外部实施复核”均为原设计/交付时点,已被本条覆盖;4B/4C和G4启用门继续有效。

日期:2026-09-23。状态:独立审查B及构建依赖补审通过;4A已实施并完成本轮技术验证,等待外部实施复核。 裁决见《3C补验收与4A方案审查裁决-2026-09-23.md》。最新准入:3C/G3已终审通过;4A构建依赖差异已补审并批准,实施及证据见《阶段4A交付与验收矩阵-2026-09-23.md》;本方案不把3C测试产物等同于游戏存档已迁移。

1. 完成效果和路线选择

最终效果:数字涂游ID登录s101后,游戏从该账号KV加载存档;正常编辑先持久化到本账号缓存,再串行同步;退出、换号、旧响应和不确定写入不会把存档混到另一账号。正式地址固定http://s101.local:18080/。PlayFab身份与其他未迁移功能分阶段保留,不把UserId机械替换为涂游ID。

路线 优点 代价/裁决建议
A:先桥接,再统一切存档,消费路径单独验收 可复用大量现有存档调用,范围可审查,每步可验收 推荐;4A不接游戏,4B/4C需一起满足启用门
B:直接整体重写PlayFabMgr并同时取消PlayFab登录 一次消除旧入口 登录、资料、玩法消费等耦合一起变更,回归面过大,本次不采用
C:按业务key逐个双后端路由 单次变更看似小 同一次游戏初始化会拼接两份存档,跨key依赖和回退难证明,本次不采用

沿用用户分工:主会话编写文档,用户交其他AI审查,主会话裁决后实施;不创建子Agent代替外部审查。

2. 现状证据

已读取另一个会话新增的《阶段3C最终验收-2026-09-23.md》:其结论为G3关闭。本轮检查的Program.cs仍为相同SHA256(3C89DA087AA3841ADBA79A11BB2BAC724FD8F5F429D2124F826185EBBDDA64DA),R1–R5是该版本新增发现,需复裁;不覆盖外部审查原文,不否认其复跑事实。

调用检索快照见《阶段4存档调用盘点-2026-09-23.txt》,236行检索命中,包含定义和注释,并非236个有效调用。

位置 当前行为 接线影响
Services/UserService.cs LoginCore、LoginPlayFab、InitData 先登录PF,从InfoResultPayload取UserData,经CompareDatas初始化,最后登录API KV接入必须把游戏初始化移到API登录与Get/Reconcile之后;失败不触发Done
Services/AggUserService.cs Login OnInitData临时订阅在base.Login结束后释放;使用UserId作为PF埋点身份 新初始化必须在订阅有效期间完成,不能后台延迟触发;身份迁移另审
Core/PlayFabMgr.cs 旧时间戳/PlayerPrefs比较,40秒定时同步,5×10缓冲,Public PF写入 新路径不能继续使用旧缓冲或时间戳;localOnly需保持独立语义
Services/PlayerData/PlayerKVSessionOwner.cs 固定账号/环境/epoch,提供Update/Delete/Flush/Close 尚无公开读取本地合并快照入口;游戏层不能另开State争同一文件锁
DataCenter/DataCenter.PlayerData.cs LoadPlayerData/New 缺数据默认值,分派大量模块;NewPlayerDataDic仅在key存在时赋值 换号不能沿用旧字典;不能把Get失败/老账号空KV当新存档
GampPlay/FishingB/FishingBehaviorB.cs ConsumeBaitOnPlayfab 本地先扣体力,保存bool仅打印 Unknown后不允许重新扣减,须固定本次操作意图
UI/Turntable/FishingTurntablePanel.cs ConsumeOnPlayfab 无论bool结果都设isDataSynced=true 接KV前必须处理确认/未知结果,防止假成功

身份假设已定位:UserService.cs:306的MMTest十六进制解析、SurveyService.cs:56、FriendService.cs:375的邀请码转换、SandDig/model/Data/DinggingSceneData.cs:94的随机种子、UserService.cs:146的5字符截取。另有排行榜、小玩法、水族馆的目标玩家ID,以及活动机器人HexNumber解析;这些来源不同,不得全局替换。正式身份切换前逐项标明本人/他人/机器人、存储兼容与稳定种子规则。

Coordinator目前持有不断追加的tickets字典(PlayerKVSyncCoordinator.cs:68/114/127),未见按确认状态清退。4A短生命周期测试可以沿用;游戏长期会话启用前须完成有界历史设计和评审,不能直接删记录破坏旧票据Wait语义。

3. 阶段顺序与启用门

子阶段 交付物 可验收的效果 不可提前执行
3C补验收 R1–R5闭环、逐行矩阵、外部复核 正式fixture安全边界及隔离证据完整 前置不通过不改生产接线
4A 会话桥接与安全读接口 独立桥接类、Owner受租约保护的只读快照接口、合成测试 单一owner、只读Ready快照、异步关闭、旧句柄失效可验证 不接UserService/PlayFabMgr/游戏,不访问正式业务KV
4B 登录加载与普通存档路由 单次登录固定后端、KV初始化链、旧facade兼容、生命周期接线 普通读写及换号闭环;空档/解析失败不会触发默认写 4C未通过不启用含消费的游戏路径;不PF/KV双写或自动迁移
4C 立即保存与消费流程 两个调用点的结构化结果处理、冻结交互和防重入 未确认不发奖、不重复扣减、不把未知标成功 不以重试业务动作补救Unknown;不宣称KV具备交易原子性
4C后身份专项 涂游UserId契约、hex假设修正、PF依赖分类与降级方案 本人身份一致,数字短ID正常;主登录不依赖PF认证成功 未迁移外围不能把数字ID冒充PF ID;需单独完整评审
4D Unity场景验收 指定账号场景、失败/退出/换号/暂停恢复证据 真实场景使用批准的存档来源,无旧账号数据残留 真实业务key写入需单独测试数据边界;101/102的fixture授权不覆盖游戏全量写

4B/4C/4D目前只是排期边界,不能直接作为可实施任务书。每阶段仍需完整方案、文件边界、独立审查和裁决。打包最后做。

4. 4A完整设计

4.1 职责

新增PlayerKVGameSessionBridge,由未来登录控制器持有。它负责等待旧owner释放、创建新owner、把固定会话封装成不可变句柄,并提供结构化错误。它不负责登录、读取PF、业务默认值、定时器或游戏初始化。

每个桥接实例最多有一个活动owner。持有S101AccountSession引用但不拥有登录session,不主动Dispose它;未来登录控制器负责在桥接Close完成后再销毁session。生产缓存根由未来Unity组合入口捕获Application.persistentDataPath并注入,本类不引用Unity API。

所有延后保存调用必须持有创建时获得的固定句柄,不能在回调时通过“当前账号”查找新owner。4A只实现句柄规则,4B必须盘点并落实到现有全局facade调用链;仅增加全局epoch不足以防止旧业务回调写新账号。

4.2 Owner新增最小读取接口

建议接口:ReadLocalSnapshot()返回独立的PlayerKVLocalReadResult:Success、Error、OwnerInstanceId、PlayerId、Epoch、只读字符串字典Data。Error范围:None、StaleOrClosing、NotReady。

错误优先级:StaleOrClosing优先于NotReady。读前、读后及处理State.ReadLocal的NotReady异常时都需复查归属/关闭状态,避免旧句柄被误报为仍可等待的数据未就绪。

实现边界:进入Owner已有操作租约,验证固定会话/epoch;只有State.ReadLocal成功且读取结束前再次确认owner有效才返回;Data必须是独立不可变快照,不暴露内部字典。生命周期竞争返回结构化失败,不泄漏InvalidOperationException。禁止把拒读包装为空字典;业务key不存在只有在有效快照里才能解读为缺失。

读状态与读取之间有竞争时,以State.ReadLocal的原子状态检查为准;捕获其NotReady情况。不新增第二个State实例,不暴露Cookie、HttpClient或状态可变引用,不改变3A状态机。

4.3 桥接操作契约

操作 输入/结果 规则
OpenAsync 已认证session、规范API、数字playerId、epoch、isCurrent委托、缓存根、生命周期token;结构化打开结果+固定句柄 串行等待前一Close真正完成;入场后重查epoch/token;构造过程不得发布半初始化句柄
句柄ReadLocalSnapshot 只读结果 仅当前且Ready;旧句柄不能转发到新owner
句柄Update/Delete 原有edit result和票据 保留本地持久化接受语义;Accepted不是远端确认
句柄Flush/WaitForTicket 原有结构化结果 保留3B Confirmed/Superseded/Unknown等区别;不增加重试
CloseAsync 可重复等待的Task 同步停止句柄新操作;等待owner Close完成;释放后才允许新Open取得活动位置

Open排队取消只取消该次Open等待,不取消前一个Close。Close异常保留关闭失败状态并拒绝后续Open,不把锁未释放误作可重用。Close没有成功前不得遗失原Task/owner引用。若调用方停止等待,内部收尾继续;不能通过超时返回后立刻再开同一账号。

若Create/初始Get失败,在失败owner正常收尾后必须清空桥接活动位,让下一合法Open可以进入;若收尾失败,沿用关闭失败阻塞规则,不能无条件清空。

CreateAsync成功只说明owner建成;Conflict/Unknown不是Ready。4A不得对非Ready句柄发布可玩数据;返回状态供4B决定阻塞提示。注销先使外部generation失效并停止业务入口,再等待Close;更换session必须经过此屏障。

4.4 测试与文件边界

批准后仅允许:

  • 修改Client/Assets/Scripts/Services/PlayerData/PlayerKVSessionOwner.cs:新增受租约保护的读取接口。
  • 新增PlayerKVLocalReadResult.cs、PlayerKVGameSessionBridge.cs及对应meta。
  • 新增CodexOutput/upserver/KVGameBridgeTests/显式链接源码的合成测试宿主及证据。
  • 主会话维护4A实施记录和入口状态。

禁止修改UserService、PlayFabMgr、玩法、3A State/Cache、3B Coordinator、网络Client、服务器和包;新增接口需求必须先差异评审。允许测试通过internal构造点注入Handler,公开生产默认构造仍使用固定Cookie、禁重定向,不新增公共任意传输入口。

4A不向正式服务发请求。所有缓存写入任务目录,合成值和Cookie仅用于替身测试。项目继续使用Unity CLI Loop,不使用UnitySkills。

4.5 4A验收矩阵

编号 场景 必须观察到的结果
A1 Get完成、Ready读 Data与ReadLocal语义一致,包含pending和localOnly,不能修改内部状态
A2 未认证/ID或API不匹配/Open取消 结构化拒绝,未发出变更请求,无遗留锁;失败完成收尾后下一合法Open成功
A3 Busy/ReadFailed/Conflict/Unknown/StorageError 不返回可用游戏快照,不返回伪空档;初始Get/Create失败不永久占用活动位,收尾失败仍阻塞
A4 101 Close尚有忽略取消的传输,102请求Open 102等待;旧传输及Close完成后才能发布102句柄
A5 同账号快速重复Open、重复Close 至多一个活动owner;Close幂等,没有后台操作遗失
A6 旧101句柄在102已开后Update/Flush/Read 拒绝且102缓存和请求计数不变
A7 排队Open取消,Close仍在进行 取消方收到失败;原收尾正常完成,下一合法Open可用
A8 读取与Close/epoch失效/在途Flush竞争 有效不可变快照或结构化拒绝;StaleOrClosing优先;在途Flush时验证baseline→pending→localOnly视图且不改变批次或发送次数
A9 session提前销毁或重登换Cookie 旧句柄失效;桥接仍能等待Close,不借新Cookie
A10 持久化pending后关闭重开 Close不隐式Flush,原账号经Get恢复后可读pending,无跨账号覆盖
A11 生产默认传输与诊断 默认固定Cookie,无正式请求;输出不含Cookie、值或完整业务key
A12 回归与集成 阶段2/3A/3B及修复后的3C离线通过;Unity CLI Loop编译通过;未允许生产文件hash不变

测试至少使用闸门控制并发时序,不能以短Sleep冒充“旧任务仍在运行”。每条矩阵映射到测试名和运行证据,缺项不能用总通过数代替。

5. 4B/4C设计约束(后续详审)

  • 单个登录会话固定后端;切换存档后端必须退出并销毁当前上下文。KV成功后不镜像到PF;KV失败不自动回落PF。
  • 初始建议保留PF登录用于仍依赖它的资料/外围,拆掉PF UserData初始化;游戏Done要求s101认证、KV Ready、数据校验和初始化都成功。这不等于完整移除PF依赖。
  • 上条只定义4B过渡方案;既有G4“涂游ID直接作为UserId”的目标继续保留,完整G4关闭还需要身份专项及非AGG/编辑器路径通过。不得用4A/4B通过宣称G4完成。
  • 公私数据契约不同:旧PF写为Public,新KV为本人私有;他人等级/资料读取继续单独迁移,不能改成读本人KV。
  • KV为空不等于“新玩家”,newCreate也不单独授权覆盖。默认策略:现有账号空KV阻塞进入游戏,不自动导入旧PF/PlayerPrefs、不生成新档覆盖。新玩家首次建档须另定显式流程与可重复结果,不能把初始化失败后的重试当新账号。
  • LoadPlayerData的所有初始化副作用需盘点;加载、失败重试、换号必须清除旧业务内存,且不向未Ready后端写默认值。主档缺失/字段解析异常分别处理,不捕获成空档。
  • 立即保存只在对应票据Confirmed时视为成功;Superseded不是该消费确认,Unknown不重新执行扣减动作。冻结消费并保留证据;没有服务端事务/幂等协议,不能声称解决跨设备交易一致性。
  • 退出/暂停只可触发有明确所有者的Flush;OS退出不保证网络完成。本地pending持久化与网络确认分别报告。

6. 审查与开发前置条件

其他AI已审查本文4A接口、生命周期、边界和矩阵,结论通过,无新增P1/P2;主会话采纳B-S1–B-S3。3C补验收及外部复核已闭环。开工构建依赖差异已补审PASS并裁决,4A已获准实施;现已完成本轮技术验证,等待外部实施复核。

本方案4A已完成设计审查、实施和本轮技术验证,待外部实施复核。4B中的新档策略、完整游戏回调隔离与消费流程需要后续具体方案;这些不影响4A作为独立桥接层的接口测试,但阻塞4B启用。