125 lines
13 KiB
Markdown
125 lines
13 KiB
Markdown
# P2-α-D:固定101历史档导入最小方案
|
||
|
||
2026-09-28,版本1,主会话。**方案完成,待独立评审;未编码、未导入。** 用户已确认固定历史备份与101映射;本方案不再请求重复确认该决定。真实写入前的停写窗口尚未建立。
|
||
|
||
## 1. 目标、产品决定和不做的事
|
||
|
||
将用户指定的历史PF101样本作为可丢弃专用账号101的固定测试起点,原样写入空的s101目标,再完整读回核对。这是一次测试导入,不代表所有历史用户、活动业务或身份系统已完成迁移。
|
||
|
||
- PF来源:`KVGameplayLoopTests/pf-source-backups/20260924T041802Z/101.json`,title108EE8/customId101,55键,SHA256=`E6EB3E656B62A3535FDB8F0DC517321A4C3E32F3A8D95385A2B7D5C0AA8222EF`。
|
||
- 用户确认其映射至s101 playerId101,接受固定历史测试起点;不要求重新访问PF,也不把其称为当前PF最新数据。
|
||
- API固定`http://s101.local:18080/`;目标非空、来源或候选校验失败则停止,不覆盖、不合并、不生成空白档。
|
||
- 102及其导入claim保持原状;不登录102、不复用/修改Import102,不运行PF、玩法、批量迁移、热换号、打包或新交易/CAS框架。
|
||
- C的070339Z目标空快照仅为历史参考;其at是本地UTC,本地钟与服务器钟偏差未校验。实际写前必须新Get,不把历史快照当锁或服务时间。
|
||
- 不合格时只停止101本次导入,不改变旧PF入口、路由或全体用户产品策略。26模块外键等保持原值,不声称业务消费者已迁移。
|
||
|
||
## 2. 选型与改动白名单
|
||
|
||
| 方案 | 结论 |
|
||
|---|---|
|
||
| 单独固定101辅助入口,沿用既有客户端/解码器 | 采用,便于约束一次测试,游戏逻辑零改动 |
|
||
| 把Import102改成通用账号迁移器 | 不采用,扩大原已验收路径和参数范围 |
|
||
| 直接HTTP脚本导入,省略候选校验/claim/替身测试 | 不采用,无法验证写前拒绝与Unknown路径 |
|
||
|
||
唯一允许的源码变更(均在`CodexOutput/upserver/KVGameplayLoopTests/Preflight/`):
|
||
|
||
1. **Program.cs**:增加两个严格单参数分支`--verify-import-101`和`--import-approved-101`。保留现有分支内容/默认拒绝逻辑不变,不添加任意账号/地址/路径参数。
|
||
2. **新增Import101.cs**:固定入口及一次导入流程;复用S101AccountSession、PlayerKVClient、PlayerKVSaveSchema.Decode。配置快照构造沿用Import102现有取值方式,复制所需短函数,不为共享函数改Import102。
|
||
3. **新增Import101Tests.cs**:固定离线验证组及替身handler;不新建通用测试/迁移框架。
|
||
|
||
csproj已有本目录C#默认包含规则,不修改项目文件。Client下C#、cfgData、场景、原备份、旧导入器及claims禁止改动。新编译产物只进入本目录D1-bin及原Preflight obj;无Unity编译必要。
|
||
|
||
数据/证据输出仅本目录:已冻结`D0-source-values.json`(55键名/hash/UTF8长度,不含整档值);D1/D2离线证据;D3停写记录;唯一`import-101.claimed`;`runs/<UTC+随机唯一后缀>/`的账号描述、before、候选报告、Set结果、after和核对。不得覆盖旧文件。
|
||
|
||
## 3. 固定输入与本地门
|
||
|
||
固定生产入口不接收外部覆盖参数,检查顺序:
|
||
|
||
1. 确认固定工作目录/源路径/API/101常量;现有claim存在即拒绝,禁止删除旧claim或换目录重试。
|
||
2. 校验原文件完整hash,再用DateParseHandling.None、拒绝重复属性/尾随内容读取。source=PlayFab/titleId108EE8/customId101、非空playFabId必须符合冻结源;data只能是55项唯一键/字符串值。
|
||
3. 逐项与受审D0-source-values的Ordinal键集合、UTF8长度/hash匹配;源原值保持在内存,不重新序列化值内JSON。未知键同样写入,不能按31模块过滤。
|
||
4. 配置固定读取7项:tbglobalconfig、tbroddata、tbmapdata、tbeventpackmanager、tbfishingevent、tbfishdata、tbfishcard;以及既有cfg.ConditionType。固定清单`D0-config-hashes.json`,版本`import101-fixed-20260928-v1`;送审hash不符停止,不自动更新、补配置或转默认值。两个D0数据清单本身的hash须在实现中锁定并随实施复核核验,不允许通过同时修改清单绕过来源/配置门。
|
||
5. 新证据目录必须不存在。目录或本地证据持久化失败时,写前直接停止;无正式请求也要保留可用错误输出。
|
||
|
||
源元数据检查和逐值清单是误操作防护,不是秘密/签名校验。来源及测试映射的产品依据是本轮用户明确答复。
|
||
|
||
## 4. 登录、候选和一次写入顺序
|
||
|
||
### 4.1 登录与写前数据
|
||
|
||
只登录101一次;使用原会话Cookie/禁止重定向/既有超时。成功且NewlyCreated=false,PlayerId101;记录InternalId、CreateTimeUtc、ServerTimeUtc及本地采集时刻,不记录Cookie。PlayerId是客户端请求ID,不把它冒充服务端回显或正式跨系统身份绑定。
|
||
|
||
登录可能产生会话/更新元数据;newCreate后置拒绝不能撤销服务器可能已创建的账号。失败/newCreate停止,不Get或清理账号,不自动重试。
|
||
|
||
随后第一次全量Get,必须成功,将完整before原值持久化后要求0键。非空立即停止,无Set/删除/合并。此Get不提供CAS,安全边界依赖已确认的专用账号停写窗口;不宣称消除了外部并发竞争。
|
||
|
||
构造与Import102同类的候选:本次真实账号描述符、固定epoch=1/专用ownerId、原PF playFabId身份快照、channel none、固定cfg快照及**本次ServerTimeUtc**。只Decode,不Apply/Open真实游戏会话。完整保存verdict/diagnostics,再要求Valid;未通过则保留原档并停止。A/B样本Valid只作依据,不跳过本次候选。
|
||
|
||
### 4.2 claim与Set
|
||
|
||
以上所有写前证据保存成功后,用CreateNew、Flush(true)持久化固定`import-101.claimed`:账号/API、源hash、键数、证据目录、配置版本、开始UTC和已确认停写记录标识。claim冲突/写盘失败则零Set;若已留下部分claim也不得自动删除。claim持久化之后、调用Set之前即认定“本次尝试已消耗”,即使进程此时崩溃也不自动重跑。
|
||
|
||
只调用一次SetAsync,载荷为全部55键原字符串。没有Set重试、PF双写、Delete或补偿。进入调用后本地/网络异常都按“可能已写”处理;不能因没有set-result文件就判定没写。
|
||
|
||
### 4.3 写后观察与本地落盘失败
|
||
|
||
Set调用正常返回或抛异常后,只要进程仍可运行且无并发/身份等新异常,均尝试**一次**同会话全量Get观察;Set结果文件保存失败不得短路此观察。实现用明确的try/catch/finally路径:单独记录网络结果/异常与本地保存异常,再走观察;Get失败不重试,保留claim。
|
||
|
||
观察结果若可用,尽力以CreateNew保存完整after及55项逐值矩阵(新增/缺失/额外键、UTF8长度/hash)。写后磁盘失败只能说明证据不完整,不反推KV没写;将错误送stderr/非零退出,仍不再Set。若进程/系统崩溃,无法保证finally运行,留到人工只读观察,不宣称总能获得after。
|
||
|
||
成功必须同时满足:Set明确Success、after成功且恰55键、全部原值相同、必需证据均持久化、停写边界未被破坏。任何一项不满足即“需人工核查”,即使Unknown后读值相同也不自动标成功。随后不Play、不补发;claim永久保留。
|
||
|
||
正常路径上限:1次登录+2次Get+1次Set,应用层4调用;失败按实际已到达阶段计数,不硬编码总是4。PF/102/Delete=0。观察Get读取原账户状态,不触发业务保存。
|
||
|
||
## 5. 狭窄测试接缝与离线验证
|
||
|
||
测试必须跑同一导入核心,不复制一套流程。允许在Import101内部提供仅测试使用的参数:输入副本/证据临时目录、CookieContainer→HttpMessageHandler工厂、候选校验回调、证据保存回调。固定CLI运行入口全部绑定真实固定实现;CLI不得传入这些参数,测试入口不得创建真实HttpClientHandler或用null回退联网。
|
||
|
||
接缝只用于:拒绝源变更的临时副本、有限mock序列、强制候选无效、模拟before/claim/set-result/after保存失败。候选真实实现仍调用既有完整Decode;成功组至少一次使用真实55键及真实配置的隔离候选解码,其他组可替身返回verdict。mock仅允许101登录/Get/Set固定URL,额外请求、102/PF/Delete或不符合顺序立即失败。测试Cookie由替身构造,不访问网络。
|
||
|
||
| 验证组 | 必须断言 |
|
||
|---|---|
|
||
| 源/配置/清单不符 | 错源hash、条目遗漏/重复或值变更、配置hash不符均不登录/不Set;原样55键可通过本地门 |
|
||
| 登录拒绝 | HTTP/协议失败、newCreate=true;Get/Set均0,不自动清理 |
|
||
| 目标非空/Get失败 | Set=0,非空before保留;无额外登录或重试 |
|
||
| 候选无效 | Set=0、claim未创建、诊断保存;记录这是故障注入,非真实样本不兼容 |
|
||
| before/候选保存失败 | Set=0,不跨过持久化门 |
|
||
| claim既有/写失败 | Set=0,旧claim字节保持;失败留下的claim不删除 |
|
||
| 正常成功 | 登录1/Get2/Set1,完整载荷55项逐值相同(含两种历史null字符串/未知键),结果与证据齐全 |
|
||
| Set返回Unknown/抛异常 | 仍Get一次,Set始终1,结果不标成功;无Delete/补偿/重试 |
|
||
| Set结果落盘失败 | 仍Get一次、非零结束,after可落则保留,不能静默成功 |
|
||
| after失败/缺键/多键/值变/落盘失败 | 非成功、claim保留、Set1;所有故障不触发第二次Set |
|
||
| 再执行 | 已有claim在网络前拒绝,原claim/快照保持,不通过删除claim复测 |
|
||
|
||
边界测试使用临时目录并保留摘要,不改原备份/生产claim。无意义的全量历史回归不重跑;既有Program分支内容和Import102全文件hash须保持(Program仅两处分支新增)。如实现必须超出三文件范围或增设通用服务,先停并修订方案。
|
||
|
||
## 6. 检查点与交接
|
||
|
||
- **D0**:本完整方案、产品决定、55项源指纹、依赖hash及自审送独立方案评审;主会话逐条裁决前不写C#。
|
||
- **D1**:通过后按三文件白名单实现。显式`dotnet build .../Preflight.csproj --no-restore -o CodexOutput/upserver/P2AlphaImport101/D1-bin`;不得隐式restore/运行正式分支。保存输入/产物hash、diff、构建结果。
|
||
- **D2**:只运行锁定DLL的`--verify-import-101`,完成上表替身验证;记录断言/请求计数及失败证据、确认源码范围。**交独立实施复核,主会话裁决前不运行正式导入分支。** 本步骤不要求用户开启停写窗口。
|
||
- **D3**:实施复核通过后,主会话向用户明确实际执行窗口:从确认101所有客户端/设备停用开始,至本次执行结束并报告;不声称已由当前来源确认建立。按Unity CLI Loop核本地空闲/无permit并冻结全部claimed;若编辑器已关闭只读进程与文件,不强启/强退。确认停写后记录D3窗口文件,仅运行锁定DLL的`--import-approved-101`一次。
|
||
- **D4**:前后状态/claimed核对、原源不变、真实账号/配置/候选、before/Set/after/对账和异常口径全部交付。新import-101.claimed为唯一预期新增,既有11 gameplay claimed不变;无Play。交独立执行复核后主会话逐条裁决关闭,只关闭此单账号测试导入。
|
||
|
||
每个检查点更新入口/状态/TODO/开发日志及机器todoItem。若任一阶段受阻,标明卡点和已发生的真实请求,不自动继续写入或修改原方案/证据掩盖差异。
|
||
|
||
## 7. 最终验收及恢复边界
|
||
|
||
| 项 | 成功条件 |
|
||
|---|---|
|
||
| 授权与基线 | 固定源/101映射已确认;方案和实施复核已裁决;执行时停写窗口明确 |
|
||
| 修改范围 | 仅Program两分支+两个新辅助C#;Client/配置/Import102与102 claim无变化 |
|
||
| 写前门 | 来源55项、配置、现有账号、空目标、候选Valid及持久化before/claim全部成功 |
|
||
| 至多一次 | 独立持久claim,最多一次Set;错误/Unknown/写盘失败均不重发 |
|
||
| 实际结果 | before空,after恰55原值且无额外键,Set明确成功,全部证据完整 |
|
||
| 边界 | 101独占窗口、无Play/permit/102/PF/Delete;新导入claim之外旧claims不变 |
|
||
| 交付口径 | 准确阶段请求计数、at本地钟限制、剩余迁移条件、无需回滚时不执行回滚 |
|
||
|
||
失败不是“恢复到空档”的授权:即使原before为空,也不得自动Delete所有键,因为Unknown/外部进度会使清空成为数据丢失。人工恢复另案需当时新鲜观察、差异和无新增进度证据;当前只保留源、claim及结果停止。本方案不承诺多键服务器事务或CAS,不为本专用测试引入新服务器能力。
|
||
|
||
## 8. 作者自审
|
||
|
||
已按真实Import102顺序检查并区别处理“set-result写盘失败会跳过观察”的缺口;明确claim在Set之前持久化、失败可能已写、崩溃不能保证观察、源码固定与测试接缝分离、真实导入前实施复核/停写两门。源的历史性、未知键保留、两种null原字符串、时间偏差及身份映射局限均已声明。未扩大游戏模块或通用迁移范围。
|
||
|
||
当前停在D0方案独立评审;用户来源决定已完成,不能再标为“待用户确认来源”。真实停写确认在D3发生,当前不打断用户进行无必要的提前停机。
|