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

140 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 阶段4存档接线分期与4A方案
最新补充(4B1关闭):4B1独立实施复核已完成,主会话核对6项交付及68项范围hash并逐条裁决三条P3,正式关闭4B1,见[关闭裁决](阶段4B1实施复核裁决与关闭-2026-09-23.md)。下一步为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启用。