保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
232 lines
29 KiB
Markdown
232 lines
29 KiB
Markdown
# M2 C3c 剩余 SDK 接线独立差异审查
|
||
|
||
日期:2026-09-30。审查人:原 ZCode(独立侧),按[纯平移统一审查入口](M2-C3c纯平移统一审查入口-2026-09-30.md)执行。只读审查:未改生产/测试/服务器代码,未运行正式账号操作,未 Play/打包。执行用户 14:45 铁律:**凡会改变正常线上逻辑的修改一律不通过**。
|
||
|
||
## 0. 基线核验
|
||
|
||
- [M2-C3c纯平移统一审查基线.json](M2-C3c纯平移统一审查基线.json)(28 项)逐项重算:**28/28 长度与 SHA256 一致,零漂移**;清单 SHA256 `7ACC6338C044B8A6E503DEEC5256AE81589549C4E8872220EC63876B91E3A6EA`。其内含的原 C3c 受审 hash(31 项)经该清单间接核验一致(`previousC3cVerified: 31` 且本审查 28/28 复算覆盖其交集文件)。
|
||
- 本报告为**静态审查**:全部结论来自源码字符级核对与已归档证据(互加好友实测、服务器只读核查),未运行任何测试;凡断言"线上能力"处均以实测/只读证据为限,未证实写作未证实。
|
||
|
||
## R1 原业务基线核对:**通过,全部属实,另补两处入口未列的等价陷阱**
|
||
|
||
入口 §3 表逐行对源码核验,**13/13 属实**(首次上报 `FishingStage.cs:249` 不 await;`SetLv` 链 :381-396 顺序事件→OnLvChange→SavePlayerData→AwaitSetLvData;建筑 :664-668 deltaLvl>0 触发;`AwaitSetLvData` :211-218 isRefreshing 守卫+300 秒(`RefreshSeconds=300` :144);分数生成 :189-206 双 clamp;`SetLeadboardData` :158-169 一次请求双统计名;`UpdatePlayerStatisticsAsync` 错误 `PlayFabClientAsyncAPI.cs:2428-2431` LogError→SetResult(null);`GetLeadboardData` :171-186 StartPosition=0/50/三项资料约束;`SocialLeadboardItem.SetData` :91-99 Position/rank=Position+1/StatValue;`GetFriendData` :133-216 过滤→LevelB→SetPlayInfo→降序→position/rank→发布事件;`SocialFriends.cs:106-107` 除模解码;`EventPartnerInvitationPanel.cs:360-362` `LastLogin.Value` **无 HasValue 检查**直接读并按时间排序)。
|
||
|
||
**补充陷阱 A(重要,入口未列)**:`FriendService.cs:150,153`——`Profile==null` 或 `Statistics==null` 的好友**整条 continue 跳过**。若 KV 好友响应不含统计等价字段,好友列表会**静默变空**(连带伙伴邀请、好友 UI、AGG 计数)。任何好友映射必须为每项提供 LevelB 等价值或明确该跳过语义保持。
|
||
|
||
**补充陷阱 B**:实测 friends 每项为**序列化 JSON 字符串**(仅 PlayFabId/DisplayName/AvatarUrl/Level 四字段)。原消费链要求 `FriendPlayFabId`+`Profile.Statistics[LevelB].Value`+`Profile.LastLogin` 形状;新 DTO 映射属 `FriendModel.cs` 候选范围,但**字段缺失即不等价**(见 R3)。
|
||
|
||
另核:`KVUserService.cs:278` 的 `GetDefaultName` 已按裁决实现 `Math.Min(5, id.Length)`(短 ID 修复在位);`GetPlayerLv` 未命中返回 **-1**、`GetPlayLv` 抛 `InvalidOperationException`——与原 PF 版(`UserService.cs:836-839` 未命中默认 **1**;`GetPlayLv` 未命中走网络取 Lv,失败时 `PlayLvs[id]` 抛 `KeyNotFoundException`)**返回值与异常类型均有差异**,列入 D3 实施时对齐项。
|
||
|
||
## R2 KV 统计更新(LevelB 写路径):**③ 缺真实接口证据(不得先行编码)**
|
||
|
||
- 客户端侧无可疑义:触发点、计算、双统计名、async void、错误 log 后结束——全部可原样保留,`LeadboardData.cs` 在候选白名单内。
|
||
- **缺的是承接目标**:现客户端证据内不存在任何"按玩家更新统计值"的服务入口——`PlayerKVClient` 仅个人 playerdata 三操作;`account/UpdateProfile` 只带普通 Level 四字段(且其调用点是 `CampPanel.cs:1362` 的 UpdateProfileLv,与 SetLvData 触发链不同);被撤回的"本人 KV 暂存 LevelB 键"方案已因语义拆分被用户否决。服务器只读参考中的 `PlayerDataService.cs:48-57`(读 Statistics[LevelB])**整份注释**,仅历史旁证。
|
||
- 裁决:**禁止**在无服务端统计入口证据时把 `SetLeadboardData` 改写为任何客户端自选目标(个人 KV 键、UpdateProfile、暂存副本)——那正是已被撤回的方案变体。服务端最小问题见 §末尾问题 1。存储容器(统计键/派生)是实现细节,但**更新时点语义**(仅原触发点更新 vs 查询时派生)是行为差异,须随问题 1 一并由用户/服务端定。
|
||
|
||
## R3 榜单与好友读取:**② 有具体合同差异需裁决 + ③(聚合读取缺证据)**
|
||
|
||
- **等级榜读取**(全球/地区前 50):③。个人 KV 无法产生跨玩家榜;`event/GetLeaderboards` 是活动榜(不同功能);无聚合入口证据。对同分顺序、版本策略等后端行为**不凭空添加规则**(入口 §6.3 已列,维持)。
|
||
- **好友列表**:②。路由已实测存在(101/102 互加后 HTTP200、四字段非空项),但相对原消费链缺 **LevelB** 与 **LastLogin**:
|
||
- D1(等级):普通 Level=1 直接填 value → 好友 UI 解码为等级 0(`/100000`);只乘 100000 → 丢建筑等级与排序正确性。**反证成立:不可采用"Level 直接映射",也不可用 `Level*100000` 伪装**。
|
||
- D2(时间):填 null → 伙伴邀请 `friend.LastLogin.Value` NRE;填当前/MinValue → 改变排序与显示。**反证成立:不可用假时间或改消费者规避**。
|
||
- 需裁决的合同差异:服务端在好友单项投影 LevelB 的方式(存统计值 vs 响应时从 KV lv+建筑派生——两者更新时点语义不同)及 LastLogin 来源(问题 2)。**在裁决前 FriendService 不动**。
|
||
- 陷阱 A/B(R1 补充)并入本项验收:映射后必须验证"无 Statistics 跳过"语义下的非空好友列表、伙伴邀请真实消费者(`.Value` 直读)不炸。
|
||
|
||
## R4 LastLogin:**② 未裁决保持——两个候选来源均未获确认,禁止预选**
|
||
|
||
- 候选一:服务器 `DBPlayer.LastLoginTime`(每次认证写 UTC,参考源码 `AccountService.cs:28-65` 实证存在但 `PlayerInfo` 四字段不投影——线上实测吻合)。需服务端确认投影(问题 2b)。
|
||
- 候选二:客户端 KV `LastLoginTimeUtc`(首场景每日初始化后写)。**不等价于 PF Profile.LastLogin**:PF 模式玩家与未写入账号无此键(101/102 现均缺),且写入时点受每日刷新流程约束——不得为好友显示提前改写(C2d 已裁定该时序保持)。
|
||
- 结论:维持未裁决。缺值时沿用原链的"跳过/null"语义(陷阱 A),不填假时间、不改 UI 过滤。
|
||
|
||
## R5 原 C3c 其余项
|
||
|
||
| 项 | 分类 | 结论 |
|
||
|---|---|---|
|
||
| 他人资料 D3(`GetPlayInfo`/`GetPlayLv`) | **①(形状恢复)+②(远端源)** | 可立即实施:在 `KVUserService` 恢复 PF 原语义——同步返回同一占位对象、异步更新该对象并发布 `GetClubPlayInfoEvent`、`GetPlayerLv` 默认 1(对齐原版,含 -1/异常类型差异修正,登记前后对照)。**远端读取源仍是 ②**:`friend/SearchFriend` 为候选(路由/DTO 已有、新 ID 已验),但其线上语义(精确 ID 查询?Unknown 默认资料?)未经正式实测,C3b 响应为合成夹具;在实测确认前不得绑定为准。 |
|
||
| 公告(`TileNewsManager.cs:28` GetTitleNewsAsync) | **③** | 无任何候选入口。公告是全局内容,个人 KV 作用域不符;不猜私有键。服务端问题 3。客户端映射(保留 TitleNewsItem 结果与首项选择、NoticePopupPanel 零修改)待入口后实施。 |
|
||
| 匹配(Duel `EventFishingDuelPanel.cs:608-817` 四调用) | **③** | PlayFab Multiplayer 票据能力无 KV 等价候选。保留 PF 分支;**KV 路径当前 `MachingEnd(null)` 开发性结束不得作为产品行为放行**(面板在活动配置后,非首场景必经,不阻断当前批次)。服务端问题 4。 |
|
||
|
||
## R6 最小开发范围(当前可批准/不可批准清单)
|
||
|
||
**当前无新增编码许可维持。若主会话按下述范围裁决,可连续实施的仅有:**
|
||
|
||
1. `Services/KVUserService.cs`:`GetPlayInfo`/`GetPlayerLv`/`GetPlayLv` 的**原语义形状恢复**(占位对象引用、异步更新、事件发布、默认 1、异常形态对齐原版并逐项登记差异)——纯平移,无线上行为变化;远端源留 TODO 待 SearchFriend 实测。
|
||
2. 对应 ServiceHost 用例(占位/事件/默认值/本人命中路径)。
|
||
|
||
**明确不批准(在对应事实到位前)**:`LeadboardData` 任何写/读目标替换(R2/R3③)、`FriendService.GetFriendData` 映射(R3②)、`TileNewsManager`(R5③)、Duel KV 匹配路径(R5③)、`FriendModel.cs` DTO 新增(字段未裁决)。已撤回暂存方案的"五测试文件"不继承为许可(入口 §6,维持)。UI 消费者、HomePanel、场景、`Core/KVMgr`、底层协议均不在本增量边界。
|
||
|
||
**新增抽象检查**:本报告未批准任何新接口/管理器/Provider;问题 1-4 的服务端入口由服务端开发侧定义,客户端只做调用映射。
|
||
|
||
## 给用户与服务端的最小问题清单(本轮唯一需要外部输入的项)
|
||
|
||
1. **统计更新与聚合读取**:游戏服是否已有/可加"按玩家更新统计(LevelB 与 地区+LevelB 两名同值)"+"全球/地区前 50 读取(返回 StatValue、Position、玩家 ID、昵称、头像)"入口?存储用统计键还是查询时从 KV 派生(更新时点语义差异需用户定)?
|
||
2. **好友单项投影**:`friend/GetFriendsList` 单项能否增加 LevelB(来源同问题 1)与 LastLogin(推荐核 `DBPlayer.LastLoginTime` 投影 vs 客户端 `LastLoginTimeUtc`,由用户定)?缺值时字段省略还是 null?
|
||
3. **公告**:是否存在全局公告读取入口(Title/Body/Timestamp 列表)?
|
||
4. **匹配**:是否存在票据式匹配(创建/3 秒轮询查询/取消/结果含成员 ID 与属性)?若无,Duel 的 KV 匹配迁移需要该能力或产品延期裁决。
|
||
|
||
以上均无现成客户端证据可自答;不要求客户端读服务器源码,不猜测路由。
|
||
|
||
## 结论汇总
|
||
|
||
| 编号 | 裁决 |
|
||
|---|---|
|
||
| R1 | 通过;补陷阱 A(无 Statistics 好友整条跳过)与陷阱 B(JSON 字符串项形状)两处等价验收点 |
|
||
| R2 | ③ 缺统计入口证据,禁止客户端先行改写 |
|
||
| R3 | ②(好友缺 LevelB/LastLogin 合同差异)+ ③(榜聚合读取);反证否决"Level 直接映射"与假时间 |
|
||
| R4 | ② 未裁决保持;两候选来源待问题 2 |
|
||
| R5 | D3 形状恢复 ① 可实施(远端源 ②);公告 ③;匹配 ③ |
|
||
| R6 | 仅 KVUserService 三方法原语义恢复可批;其余文件零编码直至事实到位 |
|
||
|
||
本报告不构成编码许可;主会话核受审 hash 后逐条裁决。凡未列为准许的实现,按用户铁律一律视为不允许。
|
||
|
||
---
|
||
|
||
## 修正与状态补充(2026-09-30 16:20,原ZCode)
|
||
|
||
[主会话裁决](M2-C3c审查主会话裁决与资料缓存实施边界-2026-09-30.md)接受本报告 R1–R6 结论并纠正两处技术表述,[待确定事项](M2-C3c完整审核前待确定事项-2026-09-30.md)转达用户"全部审核通过后才可编码、未确定内容先与用户确定"的最新要求。原ZCode 独立复核确认如下:
|
||
|
||
**修正一(本人表述错误,如实更正)**:本报告 R3-D2 称缺时间会使伙伴邀请触发 "NRE"——**不准确**。`friend.LastLogin` 为 `DateTime?`,`EventPartnerInvitationPanel.cs:361` 的 `friend.LastLogin.Value` 在空值时抛 **`InvalidOperationException`**(Nullable 语义),非 NullReferenceException。该错误属对语言语义的未验证断言,与 P3a 同类,记录在案;不影响"不得伪造时间"的结论。
|
||
|
||
**修正二(歧义澄清)**:本报告 R4"缺值时沿用原链的跳过/null 语义"表述不精确。源码复核:原链跳过条件**仅为** `Profile==null`(:150)或 `Statistics==null`(:153);**缺 LastLogin 的好友仍进入列表**(`friendData.LastLogin=null` 赋值自 `Profile.LastLogin`),其在原链的下游表现就是伙伴邀请的上述 InvalidOperationException 异常路径。因此任何"缺时间就跳过好友"的做法都是**扩大过滤、改变原行为**,明确禁止;R4 结论维持"未裁决保持、不填假时间",且补充:缺时间的原表现是异常而非过滤。
|
||
|
||
**撤回状态复核(干净)**:主会话曾按本报告 R5/R6 批准 KVUserService 三方法局部实施,用户最新要求后已整体撤回该开工许可。原ZCode 独立核证:统一基线 **28/28 仍一致**(生产零改动)、`C3ProfileCacheTests.cs` **不存在**(测试未建)、无任何 C3c 局部编码痕迹。撤回完整、无残留。
|
||
|
||
**对裁决与待确定事项的复核意见**:
|
||
1. 六项逐条裁决与本报告结论一致,两处纠正均已采纳(见上);
|
||
2. 撤回局部开工符合用户"整体审核通过后才编码"要求,执行干净;
|
||
3. 待确定事项表中"好友登录时间来源"给出的方向建议(优先复用服务端每次登录维护的 DB 时间、保持真实登录更新时点)与原 PF `Profile.LastLogin`(最近登录时间)语义吻合,为合理候选——但按流程该方向由用户确定后并入 C3c 材料再审,本复核不代为裁决;
|
||
4. 维持:C3c 整体未通过、五类 SDK 残余零编码、服务端四问为唯一解锁路径。原报告其余结论(含服务端最小四问清单)不变。
|
||
|
||
---
|
||
|
||
## 第二轮修正(2026-09-30 16:25,原ZCode,依[既有裁决与接口证据复核](M2-C3c既有裁决与接口证据复核-2026-09-30.md))
|
||
|
||
**修正三(登录时间方向表述)**:前节补充第 3 点称 DB 时间方向"待用户确定后并入 C3c 再审"——**不准确的待选项化**。复核确认:用户此前已定阶段方向为"先存 KV、后联调"(《先存KV后联调的阶段移植方案》:5——先在 KV 保存对应数据、优先完成移植、联调时再定最终接口;LastLogin 复用 `LastLoginTimeUtc` 不另加同义键),跨玩家投影的 DB-vs-KV 终选属**联调时点决策**(同文件 :41),非当前待用户重选的开放题;主会话已撤回"DB 还是 KV 请重选"的提问。本审查报告 R4 维持"未裁决保持",但准确表述应为:**按既有 KV 阶段方向推进,缺的是跨玩家字段投影的事实证据,不是方向再决策**。
|
||
|
||
**修正四(接口证据分级精确化)**:原报告及服务端四问的"缺证据"表述需按两级证据校正(依复核文件 §二,原ZCode 已对引用文件核实):
|
||
- **正式实测级**(101/102、HTTP200):`account/Login`、`playerdata/Get`、`friend/AddFriend`、`friend/AcceptFriend`、`friend/GetFriendsList`(路由+外层+四字段非空项格式)——这些**不是**缺证据项,服务端四问不再涉及它们是否存在;
|
||
- **离线兼容级**(C3b 双条件 21 组 380 断言、formalRequests=0、内存 HTTP):`SearchFriend`/`DeleteFriend`/`GetHomePage`/`GetRecommendedFriends`/`IsFriendWaitingForAccept`/`GetInvitedList` 等——已验证客户端新 UserID 兼容,**不声称经正式服务器请求**;
|
||
- **真正缺证据的仅剩**:跨玩家好友项的 LevelB/LastLogin 字段投影语义(含源 KV 缺键时的返回差异)、全球/地区统计更新与前 50 聚合读取、公告、匹配四类 KV 替代接口。
|
||
|
||
服务端四问清单相应收窄为上述四类**字段/接口事实**,不再包含任何已实测路由的存在性问题。本修正不改变任何裁决级别(②/③ 分类维持),不构成整批 PASS;"服务器接口未改变"为用户提供的事实,据此沿用既有样例,不推断接口已新增字段。
|
||
|
||
---
|
||
|
||
## 第三轮:KV 读取隔离最终裁定与相关逻辑重设计意见(2026-09-30 16:45,原ZCode,依用户明确指示)
|
||
|
||
**用户最终裁定**:`playerdata/Get` **限制为只能读取本人 KV,不可以读取他人**;与此相关的设计需重新审核并重设计。
|
||
|
||
### 一、事实锁定与撤回
|
||
|
||
1. **隔离事实锁定**(用户确认的服务端合同):`playerdata/Get/Set/Delete` 仅作用于认证账号本人数据。本报告第二轮修正后曾按"服务端可按对方 ID 读取"暂列好友/他人资料两项为"可通过逐个读取好友 KV 接线"——**该解锁作废**,相关接线方向禁止采用。
|
||
2. 源码侧印证(不变):`PlayerKVClient.GetAsync` 请求体仅 `{keys:[...]}` 无目标 ID 字段——客户端实现与服务端限制一致,**不得为跨玩家读取扩展该请求**。
|
||
|
||
### 二、重设计意见(结合用户全部既有裁决)
|
||
|
||
正确且唯一符合"入口不变、返回逻辑不变、值不变、存储迁 KV"的路径是**服务端侧投影**:跨玩家数据由服务端在内部读取 KV 后,经既有路由按原 PF 逻辑形状返回。客户端不新增任何跨玩家读取逻辑。
|
||
|
||
| 能力 | 正确设计方向 | 客户端改动 |
|
||
|---|---|---|
|
||
| 好友 LevelB/LastLogin | 服务端在 `friend/GetFriendsList`(及 SearchFriend 等资料路由)内部从各好友 KV 读取 LevelB/LastLoginTimeUtc 并按原 PF 字段语义(Statistics[LevelB]、Profile.LastLogin 等价形状)返回 | 仅把 `GetFriendsListAsync` 替换为该路由调用+DTO 映射;过滤/排序/发布顺序原样 |
|
||
| 他人公开资料 | 同上——经资料类路由返回(SearchFriend 已实测四字段,缺等级/时间等价字段的服务端投影) | `GetPlayInfo/GetPlayLv` 远端链接到该路由;原占位/异步更新/事件/默认值语义恢复 |
|
||
| 排行榜前 50 | 服务端聚合入口(客户端无法枚举全服 ID,本人 KV 读取不适用) | `GetLeaderboardAsync` 替换+结果映射 |
|
||
| 统计更新(写 LevelB) | 客户端写**本人** KV(本人写不受隔离限制),服务端聚合读取侧从 KV 取值 | `UpdatePlayerStatisticsAsync` → 本人 KV 写,原触发点/计算/时序不变 |
|
||
| 公告、匹配 | 服务端全局内容/协调入口 | 对应替换+原消费逻辑 |
|
||
|
||
### 三、需要重做/明确的设计材料
|
||
|
||
1. C3c 材料中凡涉及"按对方 ID 读 KV""客户端逐好友读取"的表述(含本报告第二轮解锁段)**全部按本意见推翻**;
|
||
2. 开发侧需按上表重写好友/资料接线方案:投影字段形状(服务端返回的 LevelB/LastLogin 等价字段名与缺键行为)与原 PF 消费链逐字段对齐后送完整裁决;
|
||
3. 服务端待确认项维持四类:好友/资料路由的字段投影、前 50 聚合、公告、匹配——均为"服务端内部读 KV 后按原逻辑返回",不要求客户端可见的跨玩家 KV 读取。
|
||
|
||
### 四、禁止项(重申并扩展)
|
||
|
||
- 禁止扩展 `PlayerKVClient` 增加目标 ID 参数或任何跨玩家 KV 读取;
|
||
- 禁止客户端拼榜、重算 LevelB、伪造 LastLogin/默认资料、扩大好友过滤;
|
||
- 禁止以"隔离限制"为由把好友/榜单功能降级或停用——缺口按第三节走服务端投影与聚合。
|
||
|
||
本节为用户指示下的正式审核意见,并入 C3c 待裁材料;整体"审核通过后才编码"门槛不变。
|
||
|
||
---
|
||
|
||
## 第四轮:对《隔离边界修订与复审确认》的审核确认(2026-09-30 16:50,原ZCode)
|
||
|
||
按修订文件第五节要求,在原报告明确三项结论如下。
|
||
|
||
### 一、§1–§3 准确性:**确认准确**
|
||
|
||
- §1 与源码一致(`PlayerKVClient.GetAsync` 仅 keys/token,本审查第一轮已核);[跨账号实测 JSON](M2-C3c-KV跨账号读取实测-2026-09-30.json) 结构与结论吻合——带目标参数的 8 次 Get 均 HTTP200 且**只返回本人数据**(服务端忽略未契约的目标参数),为"仅限本人"裁定提供了网络实证;探测参数仅存在于用户授权的只读脚本、未入生产,属实。
|
||
- §2 六项确认与第三轮意见一一对应,无偏差。其澄清正确并**予以采纳为审查口径**:"不新增跨玩家读取逻辑"指不新增**直接跨账号 KV 读取**;`friend/SearchFriend` 等既有业务路由的原目标 ID 参数是业务合同组成部分,**必须保留,不得误删**。
|
||
- §3 逐字段映射表与本报告 R1 核证的原消费链一致(跳过语义、LastLogin 缺失的原异常表现、排序/名次/事件顺序、每日初始化时序),无新增或遗漏。
|
||
|
||
### 二、§4 四项的最终合同与文件边界
|
||
|
||
1. **本人统计写**:客户端侧形状可预定——`DataCenter.LeadboardData.cs` 单文件:原两统计名映射为本人 KV 两键(同值)、原触发点/计算/async void/错误 log 后复位语义经既有 `IPlayerSaveStore` 保持,不引入额外等待/重试/无关刷新。**但最终批准与第 3 项耦合**:键名/序列化必须与服务端聚合读取侧一致,须与服务端一次对齐后共同定稿——未对齐前不编码。
|
||
2. **好友与公开资料字段投影**:合同=既有路由按原 PF 字段语义返回(§3 表)。**审核端无法单方面定稿**——字段名/缺键/失败语义是服务端事实,缺此无法写 DTO。允许文件维持候选清单(FriendService/FriendModel/KVUserService 相关方法),零编码直至事实到位。
|
||
3. **等级榜读取**:全球/地区前 50 聚合入口及结果映射纯为服务端事实,待指认;与第 1 项键对齐后一并成文。
|
||
4. **公告/匹配**:同上,待入口指认;原消费者/轮询时序在客户端侧已锁定不变。
|
||
|
||
### 三、整体是否可编码:**未达到**
|
||
|
||
方向、隔离边界、既有路由事实均已闭合;剩余为**四个具体服务端事实**,无一可由客户端或审核端猜测:
|
||
①好友/资料路由的 LevelB、LastLogin、真实普通等级字段名与缺键/失败语义;②等级榜前 50 读取入口+与本人统计写的键对齐;③公告入口;④匹配入口。
|
||
|
||
四项事实到位 → 并入 C3c 材料 → 本审查给完整裁决与文件/方法白名单 → 统一编码。此前零编码、双闸保持。修订文件所列旧叙述("SearchFriend 仅离线""DB/KV 时间重选""客户端加目标 ID 读 KV")确认不再作为执行依据。
|
||
|
||
---
|
||
|
||
## 第五轮:四项服务端事实挂起后的可编码范围裁决(2026-09-30 16:58,原ZCode,依用户指示)
|
||
|
||
用户裁决:四项服务端事实(好友/资料字段投影、前50聚合+键对齐、公告、匹配)**放后面等服务器实现**;审核其余内容是否可编码。逐项核对后裁决如下。
|
||
|
||
### 可编码范围(批准,唯一生产文件 `Services/KVUserService.cs`)
|
||
|
||
| 方法 | 批准内容 | 依据 |
|
||
|---|---|---|
|
||
| `GetPlayerLv` | 对齐原 PF 缓存合同:命中返回原值(含 0/负数,不归一化),未命中返回 **1**(当前 -1 移除);`SetPlayerLv` 不改 | 纯平移;原版 `UserService.cs:836-839` 语义 |
|
||
| `GetPlayInfo` | 恢复原三段语义:空 ID 返回 null;缓存命中返回**同一对象引用**;未命中创建默认昵称占位并缓存,本人分支用已认证资料填同一对象并**同步发布一次** `GetClubPlayInfoEvent`(后续命中不重复发布);他人远端异步更新**绑定 `friend/SearchFriend`**(已正式实测:存在 ID 四字段、目标 ID 原样返回)——`code>0 且 info!=null` 时更新占位对象并发布事件,否则静默保留占位(= 原版 PF 失败路径"无事件") | 形状=原版逐字符对齐;远端源字段(昵称/头像)已实测;失败语义按用户裁决归服务端域,客户端按既有 `Code>0` 契约消费 |
|
||
| `SetPlayInfo(string,string,string)` | 原版替换缓存对象语义,不发布事件、不改已有引用 | 原版 :902-912 |
|
||
| `ClearPlayInfos` | 原版清空 | 原版 |
|
||
| `GetPlayLv` | **维持现状不动**(未命中显式 `InvalidOperationException`)——不预改 `KeyNotFoundException` 假装做过网络查询;真实远端等级读取随事实①迁移(SearchFriend level=1 与 KV 62/60 的数据源差异未定论) | 沿用第四轮限定 |
|
||
|
||
**测试**(`CodexOutput/upserver/KVGameLoadTests/ServiceHost/`):`M2MergedGameplayTests` 接入已认证 101/102 合成场景+新增 `C3ProfileCacheTests`(真实 KV/PF 服务对照);红测先行证明当前默认值/对象引用/事件差异;覆盖:默认 1 不污染异步查询、缓存含 0/负数、null/空 ID、本人引用与事件恰好一次、SetPlayInfo 替换规则、ClearPlayInfos、SearchFriend 成功/失败两态占位行为、KV 路径 SDK 计数 0、PF 对照不变。ServiceHost 双条件+CLI Loop 编译后记检查点。
|
||
|
||
### 明确不批准(挂起等四项事实)
|
||
|
||
`LeadboardData`(统计写/榜读)、`FriendService.GetFriendData`/`FriendModel` DTO、`TileNewsManager`、Duel 匹配路径、任何 LevelB/LastLogin 相关代码、`PlayerKVClient` 任何改动。首场景双闸维持(PF 调用计数≠0 的来源正是挂起项)。四项事实到位后并入 C3c 补充裁决,不在本批顺带处理。
|
||
|
||
### 边界重申
|
||
|
||
不新增跨账号 KV 读取;业务路由目标 ID 参数保留;`runtimeVerified` 在真实运行前保持 false;红测首败留痕;PF/AGG 路线零改动。本裁决为**范围性编码许可**:仅上表内容,批内常规编译/测试失败自修不送审,文件/行为越界即停。
|
||
|
||
|
||
|
||
|
||
---
|
||
|
||
## 第六轮:C3d 交付确认与 R2-S 本人统计写独立实施裁决(2026-09-30 19:10,原ZCode)
|
||
|
||
### 一、C3d 交付确认:通过
|
||
|
||
[C3d 交付清单](M2-C3d交付与验证hash.json) 13/13 独立复算一致(另 2118 项基线未动);代码现状与第五轮批准逐点吻合(`GetPlayerLv` 缺省 1、`GetPlayInfo` 占位/同引用/一次事件+SearchFriend 绑定、`GetDefaultName` min(5,长度)、`GetPlayLv` 未接远端保持显式失败)。双条件 24 组 491 断言、编译 0 错误、各首败留痕——按批内规则关闭,不需重审。
|
||
|
||
### 二、R2-S 裁决:附条件允许本人统计写独立实施(替代第四/五轮对该写入侧的挂起;读取/聚合侧挂起不变)
|
||
|
||
依据:用户已裁"LevelB 值不变、存储迁 KV";写入本人 KV 不受隔离限制;服务端聚合尚未有任何既有设计可与客户端键冲突——客户端先行固定键合同是降低而非增加总对齐成本。**本裁决不复活已撤回的"暂存快照+通用 Flush"方案**:该候选在原触发点经正常 store 写统计值本身,无旁路快照、无新刷新管线。
|
||
|
||
| 条件 | 内容 |
|
||
|---|---|
|
||
| C1 键/序列化合同 | 原统计名 `LevelB` 与 `ContinentCode+LevelB` 直接作为本人 KV 键;值=同一复合整数的 `InvariantCulture` 十进制字符串。此合同冻结为客户端事实并写入集中问题单,服务端聚合侧照此对齐;日后若服务端另定键名,属一次性对齐修改而非行为变更 |
|
||
| C2 地区缺失 | `ContinentCode` 缺失/未知时**仅写全球键**——这是原 PF 请求中地区名退化为全球名(`null+"LevelB"=="LevelB"`,双同名统计净效果=仅全球统计)的忠实翻译;不新增地区默认值、不两键同写。KV 档普遍缺大陆码与 PF 档案普遍有值属已登记的数据覆盖差异 |
|
||
| C3 失败映射 | store 拒写/非 Confirmed/传输异常一律走**原错误日志路径后任务正常结束**——原 `UpdatePlayerStatisticsAsync` 从不抛出(错误→log→SetResult(null)),KV 分支同样不得让异常逃出 async void(= Unity 崩溃,行为变更)。仅捕获 store 文档化的失败形态映射为日志,不加宽泛 catch-all、不加重试/回退 |
|
||
| C4 等待范围 | ≥1 键被接受时 await 一次现有 `FlushAsync`;全部拒写则跳过并按 C3 记日志。`FlushAsync` 刷新全部 pending(含无关键)是既有唯一原语、不新建选择性刷新——登记为已知差异(无关键可能提前随行同步、完成时点≠原单请求),不构成行为变更 |
|
||
| C5 原子性 | 两键可部分接受/确认、同键重写 Superseded——逐键结果记日志,不声称同时成功,不加回滚/补偿/锁。原 PF 单请求的后端原子性本就不可由客户端源码证明,不推定 |
|
||
| C6 不变项 | `SetLvData`/`AwaitSetLvData` 的公式、clamp、lv 回写、300 秒延迟、async void、isRefreshing 复位、原触发点全部不动;`SetLeadboardData` 仅在收到的 names/values 列表上做 KV 分支(含 C2 去重),`SetLvData` 零改动;PF/AGG 分支不变 |
|
||
| C7 完成声明 | 统计写完成≠等级榜功能完成;聚合读取、榜读取、双闸挂起不变。PF 后端统计聚合模式(Last/Max/Sum)系历史配置事实,客户端无法证明——作为一行服务端确认项登记(推荐按 Last 语义核),非本批阻塞 |
|
||
|
||
**文件边界**:生产仅 `DataCenter/DataCenter.LeadboardData.cs`(`SetLeadboardData` KV 分支+必要 using)。测试按候选:ServiceHost 链接真实 LeadboardData、`C3IdentityCompatibilityTests` 移除业务替身、Stubs 最小签名+PF 统计请求记录、新增 `C3StatisticsWriteTests.cs`;既有 M2Merged/C3ProfileCache 断言不因本批改动。红测先行;覆盖清单沿申请方 §5.3(原公式/上限/坏值/负值、双统计名、拒写/部分接受/各确认态、延迟确认中换号注销、无关键失败随行、真实 store 重读、PF/AGG 对照、formalRequests=0)。
|
||
|
||
### 三、核查文档其余事项的立场
|
||
|
||
§2 剩余调用清单与 §3 临时冻结边界(F1/F1补充/F5/F6/F7/F8/F9)与本审查各轮记录一致,无异议。特别确认三点:**F1补充**(PFLoginAct 销毁时 Logout 须与双闸同批重审正常转场/异常退出区别——盲删两处 return 会在正常转场撤销会话,同意挂起);**F7**(EmptyMailService/BaseEventData 删除须先证迁移因果与全引用,不凭名称或"无调用者"简表删除,同意);移动端易盾/TapJoy、PlayerKV 改名维持既有登记。§5.4 回写要求按本节履行完毕。
|