保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
24 KiB
M2 DP1 独立方案审查(双提供方独立 KV 用户服务抽离)
日期:2026-09-29。审查人:原 ZCode(独立侧)。只读审查,未修改任何生产/测试/服务器代码,未调用正式 101/102,未打包。
0. 受审基线核验
- 18 项受审基线逐项按字节数与 SHA256 重算:18/18 一致,无漂移。
- 清单自身 SHA256 实测
858C43EF26361EB348D4CE36C7F3AFC8AF4DAF85E754A45F3D960A2D078145FA,与提示词记录一致。 - 本报告据无漂移基线作出,可作为主会话裁决输入。基线外只读扩展源码调用点(
PlayerKVEditorValidationHost.cs、EventFishingDuelPanel.cs、FriendService.cs、LeadboardData等业务文件、S101LoginWindow.cs、CodexOutput/upserver/KVGameLoadTests/ServiceHost/*)仅用于证据,相关发现单独标注"基线外"。
总裁决
限定通过(需修订后可进入白名单裁决)。方案 C 的最小纵切方向成立:调用图证据显示正式 KV 路径可以在 DP1 内完全脱离 PlayFabUserService 运行时实例与 PlayFab SDK 调用,同时 PF/AGG/受控验证/kv-loop 四条既有通道不需要行为变化。无 P1 阻断。有 3 项 P2 需修订(R1/R2/R3)与 3 项 P3 登记(见 §8),全部是"把方案自身遗留的未决点落成可执行裁决",不是结构性缺陷。
1. 三方案比较是否成立(重点:方案 C 能否真正移除 PlayFabUserService 与 SDK 依赖)
结论:通过(附 R1/R2 修订条件)。
源码证据链:
- 启动装配:
SubBoostrap.cs:37-42一次捕获PlayerKVRuntimeSelection;SubBoostrap.cs:103if(!selection.IsKV) rootCtxGo.AddComponent<PlayFabMgr>()—— KV 模式下PlayFabMgr组件根本不创建,PlayFabMgr.HasLiveInstanceForKVLoad恒假(PlayFabMgr.cs:31)。 - 登录注册:
LoginStage.cs:24目前无条件Register<IUserService, PlayFabUserService>(),是正式 KV 依赖旧类的唯一注入源;方案 C 只需在此按冻结选择分支注册新 KV 类。 - 登录行为:
UserService.cs:195-226LoginCore中if(IsFormalKV) return await LoginKVFormalAsync(...)(:200)在 PlayFab SDK 全部步骤(LoginPlayFab/OnYinDunLogin/InitTapJoy/LoginApiServer,:207-225)之前返回;PFLoginAct.cs:94-115正式 KV 分支在MailService.Init(); iapService.Init()(:139-140)之前 return。即正式 KV 现有执行路径已零 PlayFab SDK 调用,抽离后把这段逻辑整体搬进新类即可,无需改语义。 - 正式 KV 编排全部位于
UserService.KVLoad.cspartial(LoginKVFormalAsync:82-147、LogoutFormalKV:67-75、BindFormalPromptServices:148-171、CreateFormalKVHandler:64-65、OnFormalKVApplicationQuitting:66),对旧类其余成员的依赖只有:config/customServerMgr注入属性(:104-106)、kvControlledInstance/runtimeSelection字段(:16-17)、logoutGeneration/loginLifetime(UserService.cs:169-172)、模块 31 的ResetForKVLoad/ReplaceFromKVDecoded/KVIntents(:222-251)。这些均可随迁或在窄接口上重建,见 §3。 - 旧类保留通道不受损:
- PF 模式:
LoginCore去掉IsFormalKV分支后即恢复纯 PF 语义;LoginStage非 KV 分支注册不变。 - AGG:
PlayerKVRuntimeSelection.Capture在 agg+kv 时直接抛异常(PlayerKVRuntimeSelection.cs:29-30),AGG 构建永远走AggUserService(LoginStage.cs:20-22),不因 DP1 改变。 - kv-loop:
UserService.KVGameplayLoop.cs:537-586LoginKVGameplayLoopAsync深度依赖 PF 身份(LoginPlayFab:552、LoginApiServer:553、PlayFabMgr.Instance.StartKVGameplayLoopSync():576、ForgetAllCredentials:613),它必须也应当留在PlayFabUserService上;Bind(this)(:570)在 Bind 改窄接口后仍编译(旧类实现该接口)。 - 受控验证:
PlayerKVEditorValidationHost.cs:236,265-267手工new AggUserService(...)并直接调用user.ResetForKVLoad/ReplaceFromKVDecoded/KVIntents—— 签名不变即不受影响(基线外证据)。
- PF 模式:
- 编译级约束(须写入方案):新 KV 用户服务源文件不得引入
PlayFab.*using、不得引用PlayFabMgr/PlayFabUserService具体类型;"双模式证明"不得以能编译冒充,须按 §7 矩阵以注册结果与HasLiveInstanceForKVLoad断言。
方案 A/B 的否决理由与既有一致(A 无法满足双提供方目标,B 需要未核的线上合同),维持。
为什么是"附条件":方案把两个决定性问题(IUserService 实现义务、登录/退出阶段的中立面)留作未决点(方案 §"完整评审需集中裁决的未决点" 1、4),本审查已给出确定性答案(见 §2、§6),方案文本需按此落成可执行裁决,否则白名单无法精确冻结。
2. DI/构造路径追踪:DP1 是否必须让独立 KV 类实现当前宽泛 IUserService
结论:必须实现。追据如下(R1 的核心证据)。
PlayerKVGameplayBindings.Bind在正式登录LoginKVFormalAsync内执行(UserService.KVLoad.cs:100),Require<GameCore.SmallGameData>()(PlayerKVGameplayBindings.cs:28)触发容器构造SmallGameData。DataCenter.SmallGame.cs:26-30:public SmallGameData(cfg.Tables tables, IUserService userService, ICustomServerMgr customServerMgr)—— 构造函数注入IUserService,是 31 个模块中唯一的构造参数级依赖(其余 4 个 DataCenter 文件均为方法内Resolve<>或属性注入:DataCenter.PlayerData.cs:599、DataCenter.FishingEventData.cs:514等、DataCenter.LeadboardData.cs:137)。- 模块 14
ClubService有[Inject] public IUserService userService { get; set; }(ClubService.cs:86),在 Bind 解析时做属性注入,同样要求容器内已注册IUserService。 PFLoginAct.cs:15[Inject] public IUserService是登录页的必经注入。- 结论:若 KV 类不实现
IUserService,KV 模式下SmallGameData无法构造、Bind 失败。替代路线(把SmallGameData构造与ClubService属性改为窄契约)需把DataCenter.SmallGame.cs、ClubService.cs加入白名单,改动两处业务文件,但 DP1 执行路径上没有任何成员被调用(SmallGameData构造函数只存引用不调用成员,DataCenter.SmallGame.cs:26-34;受控验证 fixture 甚至传null,PlayerKVEditorValidationHost.cs:237基线外证据),因此该替代在 DP1 无收益,不推荐本批做,登记为 DP2+ 可选项。
必须同时给出的成员三分表(否则"显式失败"不可审计):
| 类别 | 成员 | KV 类处理 | 依据 |
|---|---|---|---|
| 真实实现 | Login/Logout/LoginLifetime/CustomId |
迁入正式 KV 逻辑 | PFLoginAct.cs:38,88,96,111,168 是登录页仅有的接口调用;LoginLifetime 用于链接取消 |
| 可观察状态(如实反映"未发布") | IsLogin/IsAuthorized/UserId/ApiPlayerId/InternalId/entityKey/DisplayName/AvatarUrl |
恒 false/null/0,与今日正式 KV 现状逐项一致 |
UserService.cs:110-115 + LoginKVFormalAsync 从不写这些字段;LogoutFormalKV 置空(UserService.KVLoad.cs:71);不是伪造,是如实反映"身份未发布" |
| 显式失败(throw InvalidOperationException,消息含"formal KV identity/profile contract pending") | ChangeDisplayName/UpdateAvatarUrl/UpdateLanguage/LinkAccount/GetLinkState/SetPlayerLv/GetPlayLv/GetPlayerLv/GetPlayInfo/SetPlayInfo/ClearPlayInfos/UpdateProfile/UpdateProfileLv |
全部显式失败并登记清账 | 全部调用点位于 FishingStage 闸后(社交/资料/UI)或 PF 专属链;DP1 执行路径零调用(§6 证据) |
| 登记裁决 | CreateTime/LastLoginTime/LoginTime/CreateTotalSeconds/ContinentCode/Language/EAccoutUrl/Dictionary EAccoutUrl |
EAccoutUrl 按模块 31 置换真实 KV 值(UserService.KVLoad.cs:249);时间/大陆/语言字段维持默认值并在清账清单登记(P3-1) |
FishingEventData/PlayerShopData 读 CreateTime(基线外,闸后);默认值行为与今日正式 KV 完全一致,不新增伪造 |
不接受项复核:方案明令禁止静默空实现、假 EntityKey、假 PlayFab ID、KV 失败回退 PF——上表不触碰任何一项;GetDefaultName 不属于上表(见 §4)。
P2 风险 R1:方案 §"未决点 1" 仍以问句留空。按本节证据应改写为确定裁决:"DP1 的独立 KV 类实现 IUserService,成员按上表三分;窄化 SmallGameData 构造依赖延后到 DP2+,登记最终清账"。
3. 第 31 模块的最小中立接收契约
结论:通过。契约成员恰好三个,方案描述准确,可按下述精确化。
事务侧全部消费点(PlayerKVGameplayBindings.cs:55-56):
modules.Add(new PlayerKVLoadModule(31, user.ResetForKVLoad,
d => user.ReplaceFromKVDecoded(d.Module<PlayerKVLoginState>(31), d.Epoch, d.Raw)));
return new PlayerKVGameLoadTransaction(modules, ..., () => user.KVIntents?.Discard());
即窄契约 = ResetForKVLoad() + ReplaceFromKVDecoded(PlayerKVLoginState, long epoch, IReadOnlyDictionary<string,string> raw) + KVIntents(或折叠为 DiscardKVIntents() 委托源)。三个调用通道:正式(UserService.KVLoad.cs:100)、kv-loop(UserService.KVGameplayLoop.cs:570)、受控验证(PlayerKVEditorValidationHost.cs:265 基线外,手工组模块、直调具体方法、不经接口)。接口只需服务 Bind 一处调用者,两实现类(PlayFabUserService 供验证/kv-loop、新 KV 类供正式),符合 AGENTS.md"实际调用者"要求。
字段/成员归属清单(方案要求"哪些留在旧类、哪些必须迁出",此处给出完整盘点,全部出自 UserService.KVLoad.cs 行号):
| 归属 | 成员 | 行号 |
|---|---|---|
| 迁入新 KV 类(正式专属) | kvHandoffEpoch/kvHandoffHandle/kvFormalLoading/kvFormalAttempted/kvFormalPromptsBoundEver/kvFormalClosing |
:22-26 |
| 迁入新 KV 类 | IsFormalKV/KVHandoffReady/TryGetCurrentGameHandle(epoch 对账) |
:27-39 |
| 迁入新 KV 类 | LoginKVFormalAsync/LogoutFormalKV/TrackFormalClose/BindFormalPromptServices/CreateFormalKVHandler/OnFormalKVApplicationQuitting |
:64-171 |
| 迁入新 KV 类 | CaptureSaveBackend/RejectSaveBackend 的 formal 分支(:47-51)——旧类保留 PF/kv-loop/受控分支 |
:40-55 |
| 迁入新 KV 类 | ObserveKVQuitAsync 的 formal 观察语义 |
:194 |
| 留在旧类(受控验证/kv-loop 专属) | ConfigureKVForValidation/LoadKVForValidationAsync/CloseKVForValidationAsync/KVLoadedForValidation/KVReceiverCountForValidation/EnterKVReceiver/ExitKVReceiver/kvReceiver/kvReceiverUsers/MMTestForKVValidation/IsControlledKVInstance |
:56-63,172-203,219-220 |
| 两边各自实现(窄契约接口) | ResetForKVLoad/ReplaceFromKVDecoded/KVIntents/KVLoadingCopy/MMTestValue/KVTapJoyBalance/EAccoutUrl/KVLastError |
:21,216-218,222-251 |
| 留在旧类(旧类自身还需) | kvControlledInstance/runtimeSelection(受控实例判定) |
:16-17 |
拆分时必须保持的安全语义(方案已列,逐条核过源码成立):31 模块定序与主线程 Apply(PlayerKVGameLoadTransaction.cs:37-45)、失败 ResetAll+discard 回滚(:77-83)、admitted 准入在两个实现里各自收紧(旧类删掉 formal 项后剩 kvControlledInstance || KVLoopLoading;新 KV 类为"formal loading/closing 且无 handle 且 InternalId==0 且无 api 会话")、AggUserService 的 internal override ResetForKVLoad/ReplaceKVLinked(AggUserService.cs:20-26)不受影响。
4. IUserService 身份成员的分提供方语义
结论:需修订(并入 R1 的成员表);DP1 不修改共享接口。
entityKey:真实非注释读取全客户端仅 2 处,均在 Duel 事件 UI:EventFishingDuelPanel.cs:634-635(Id/Type)、:748(成员比对)——FishingStage 闸后(基线外证据)。KV 无 Entity 合同,新类entityKey => null(与今日正式 KV 一致,今日也从未赋值)。Duel 两处属 DP2+ 身份/业务合同批次;null 会在该 UI 触发 NRE,闸未开前无影响,但必须登记到最终门控清账清单(P3-2)。UserId:PF 语义为 PlayFabId;正式 KV 今日恒null(LoginKVFormalAsync不写、LogoutFormalKV:71 置空)。社交/缓存键(如DataCenter.SmallGame.cs:390,434,513)全部闸后。KV 侧最终以哪个 ID(101/102规范名还是服务端内部 ID)作社交键,属 DP2+ 身份合同,本批不得提前拍板。ApiPlayerId:IsAuthorized ? apiSession.PlayerId : null(UserService.cs:115),正式 KV 无apiSession恒 null,新类照实返回 null。InternalId:正式 KV 恒 0(不经过LoginApiServerCore)。IsLogin/IsAuthorized:正式 KV 现状恒 false(UserService.cs:110-112),新类照实。不得在KVHandoffReady后翻转这两个值——那是首场景身份发布(CP2B 后续)的裁决,DP1 的"就绪"只通过 KV 专用状态面暴露(见 §6 R3)。GetDefaultName:id[0..5](UserService.cs:146)对"101"/"102"会抛ArgumentOutOfRangeException(长度 3 < 5)。调用点约 10 处全在闸后(FriendService.cs:164、LeadboardData.cs:239、各社交/伙伴 UI,基线外)。方案已如实记录;登记清账(P3-2),DP1 不改。
临时门控与清账条件:DP1 的门 = LoginStage.RequestNextAct 的 KV 早退(LoginStage.cs:114-115)+ 显式失败成员表;清账条件 = 线上身份/资料合同(DP2+)落地后逐成员改为真实语义或迁移到窄接口,届时重审。
5. 启动开关、冻结、AGG、URL、准入与生命周期安全性
结论:通过。方案继承的时序在源码全部成立,可整体迁入新类。
- 唯一开关与冻结时点:
SubBoostrap.RegisterServices在任何用户服务注册/登录之前捕获一次(SubBoostrap.cs:37-42);LoginStage只读SubBoostrap.RuntimeSelection(:35,114)。会话内不可换:LoginKVFormalAsync的kvFormalAttempted单发 +loginGate信号量 + generation/requestedId 对账(UserService.cs:172-193、UserService.KVLoad.cs:85-119)阻止二次登录/换号,符合用户已裁决的重启式重登。 SaveBackend再读取:登录时CaptureSaveBackend重读配置(UserService.cs:173)并经RejectSaveBackend与冻结选择对账(UserService.KVLoad.cs:47-51:formal 下非"kv"即Input错)。这是对同一事实的防御性复读,不是第二开关;拆分后新 KV 类必须保留等价校验(读到的后端串必须等于"kv"),旧类保留 PF/kv-loop/受控分支。- 编辑器直开:
InternalEditorBoost.Init仅允许 playfab,KV 选择直接抛异常(SubBoostrap.cs:378-392)。不因 DP1 改变。 - AGG:
Capture在 agg+kv 时抛(PlayerKVRuntimeSelection.cs:29-30),早于LoginStage的#if AGG注册(LoginStage.cs:20-22),"AGG-KV 启动失败、不静默回退"已由现状保证。 - 正式 URL:
Capture严格比对FormalApiUrl(:31-32);LoginKVFormalAsync再核server.ApiBaseUri与同 URL(UserService.KVLoad.cs:92-95)。 - 准入:
TryAdmitPlayer仅 101/102 且规范化(PlayerKVRuntimeSelection.cs:36-43);"有现存档"由加载三态语义保证(无档≠假空,此前批次已裁定)。 - KV 无 PF 活动实例:
SubBoostrap.cs:103不挂组件 +LoginKVFormalAsync:94核HasLiveInstanceForKVLoad。 - 生命周期可迁性:401/403 经
server.KVUnauthorized事件 → 版本对账 →LogoutFormalKV(UserService.KVLoad.cs:105-109、CustomServerMgr.cs:180-184);Application.quitting订阅(UserService.cs:156)在旧类删除 formal 分支后由新类构造器订阅;Logout/QuitAct先同步失效再观察关闭(LogoutFormalKV先DisableKVBusiness/置空 handle/取消 LoginLifetime,再TrackFormalClose,:67-75);Close 失败保留kvFormalClosing屏障(:76-81);在途旧响应与同账号新 epoch 由TryGetCurrentGameHandle的 epoch/引用对账(:30-39)+CustomServerMgr.SessionVersion(CustomServerMgr.cs:177)拒绝。以上全部位于可整体搬迁的 formal 段,无旧类私有耦合。
6. 跨批依赖与隐藏必经路径
结论:通过(附白名单修订 R2/R3)。
- DP1 执行路径(SubBoostrap → LoginStage → PFLoginAct → Bind/Apply → 交接 → 停在登录页)上的
IUserService实际调用面 =Login/Logout/LoginLifetime/CustomId四个(PFLoginAct.cs:38,88,96,111,168);SmallGameData构造只存引用(DataCenter.SmallGame.cs:26-34);ClubService属性注入后在 Reset/Replace 中不触userService(ClubService.csM14 段无引用,社交请求全部IsAuthorized/ApiPlayerId守卫且闸后,:245-330,384-589)。 - DP1 执行路径上的
PlayFabMgr.Instance直调 = 0:组件不创建(SubBoostrap.cs:103),登录页 KV 分支不触 TapJoy/邮件/IAP(PFLoginAct.cs:114提前 return)。全客户端 225 处PlayFabMgr.Instance(83 文件)与 213 处IUserService文本引用全部位于 FishingStage 闸后或 PF/AGG 模式——本批无须迁移任何一个;打开闸门前(DP2+)必须成组处理,方案已如实声明。 - 本批必须迁移的最小调用者(与方案候选白名单一致):
LoginStage.cs(注册分支)、PFLoginAct.cs(中立检测/状态/输入)、QuitAct.cs(退出观察)、PlayerKVGameplayBindings.cs(Bind 窄接口)、UserService.cs+UserService.KVLoad.cs(formal 段搬出与旧类分支清理)、新增 KV 类。 FishingStage闸保持:LoginStage.RequestNextAct(:114-115)不动。- 不把客户端路由字面量或本机服务器代码当合同:本审查未读服务器目录;
mail/fetch等仅为 CP2B-T 的路径示例,不构成线上合同。
R2(P2)— 测试迁移与不改文件的编译耦合核验义务:CodexOutput/upserver/KVGameLoadTests/ServiceHost/Program.cs 与 Stubs.cs 直接 new PlayFabUserService()/new AggUserService() 驱动 formal 登录、401、quitting、双登等用例(:50,64,73,88,105,109,124,153,基线外证据)。formal 成员搬出后这批用例必须改指向新 KV 类;同时 UserService.KVGameplayLoop.cs、AggUserService.cs、PlayerKVEditorValidationHost.cs 不在白名单也确实无须改动,但白名单文件须显式记录"这三个文件依赖窄契约签名不变"的核验义务,防止实施时顺手扩面。方案文本需补这两点,否则"必要合成测试"不可审计。
R3(P2)— 登录/退出阶段的中立面须落成具体机制:方案只说"提供方中立的登录结果或窄 KV 会话能力"。具体缺陷三处:
PFLoginAct.cs:88playFabUserService.CustomId = configCustomId与 :167-170 的下转——新类注册后playFabUserService为 null 会 NRE;应改userService.CustomId(接口已有成员,UserService.cs:70),彻底去掉该下转。- formal 检测(:94,:316)应以
SubBoostrap.RuntimeSelection?.IsKV==true为准,不得以"注入对象可下转到某具体类"为准。 QuitAct.cs:40,61两个下转在新类下全部失效(validation 退出观察与 formal 退出观察分别落在两个类上)。建议一个 internal 窄接口(例如含KVHandoffReady、KVLastError、ObserveKVQuitAsync三成员),PFLoginAct/QuitAct为其仅有的两个消费者,PF/受控语义由旧类实现、formal 语义由新类实现;或明确采用"新类具体下转 + 旧类下转并存",二选一须在方案定死,避免实施时各写各的。
7. AGENTS.md 简洁性核对
结论:通过(附条件)。
- 新增抽象仅两件,均有实际调用者:31 模块窄契约(消费者=
Bind一处,实现=旧类[验证/kv-loop]+新类[formal]);KV 状态/退出观察窄接口(消费者=PFLoginAct/QuitAct,实现=两类)。无第二套 Coordinator/Guard/存档管理器;方案明令禁止,源码确认无需。 - 开关单一:复用
RuntimeSelection冻结捕获;kv-loopEnabled是编辑器测试通道许可(permit 文件驱动),CustomServerMgr.kvOnly/kvBusinessReady是运输层状态,各有不可省职责,DP1 不新增任何开关。 - 闲置路径清账已在方案 §DP2+ 与约束文件清单中登记;本报告补充:旧类拆分后残留的 formal 分支(ctor :156、
EvtAPISvrUnauthorized:159、IsLogin:110、LoginCore:200、Logout:684 的IsFormalKV项)必须同批清干净,不得留死代码——这正是UserService.cs入白名单的原因,方案候选已含。 - 最小必测矩阵(建议写入验收):
- PF 回归:同构建选 playfab →
Resolve<IUserService>()为PlayFabUserService(AGG 配置为AggUserService),现有 PF/kv-loop/受控验证套件原样通过; - KV 正式:选 kv →
Resolve<IUserService>()为新 KV 类 且PlayFabMgr.HasLiveInstanceForKVLoad==false且全进程零PlayFabUserService构造(以注册/类型断言,不以编译冒充); - 既有 formal 用例迁移后全绿:成功/无档/非 101/102/错误 URL/AGG-KV 拒绝、401/403、双登单发、
Logout/Application.quitting、Close 失败屏障、旧响应/新 epoch、31 模块定序与回滚、KVHandoffReady与EnableKVBusiness单次发布; - 显式失败成员逐个断言抛出(防静默空实现回潮);
- Unity CLI Loop Editor 编译 0 错误;可取得的真实 Player 编译另记。
- PF 回归:同构建选 playfab →
- 不能声称已通过的限制:线上业务路由/响应合同、
IsLogin/ApiPlayerId正式发布、SignalR 真连接、首场景链路、真实 Player 编译(工具受限时)。方案 §DP1 验收已如实声明,维持。
8. 发现清单(严重度汇总)
| 编号 | 严重度 | 内容 | 最小修订 |
|---|---|---|---|
| R1 | P2 | 方案未决点 1(IUserService 实现义务)应按 §2 证据落成确定裁决 + 成员三分表 | 修订方案文本,白名单裁决时引用本报告 §2 表 |
| R2 | P2 | 既有 formal 合成测试(ServiceHost Program/Stubs)迁移义务与三个"不改但受签名耦合"文件的核验义务未写入方案 | 补入 DP1 白名单说明与验收 |
| R3 | P2 | PFLoginAct/QuitAct 中立面机制未定死(CustomId NRE 风险 :88/:167-170;QuitAct 双下转失效 :40/:61) | 按 §6 R3 定死窄接口或双下转方案 |
| P3-1 | P3 | KV 类时间/大陆/语言只读成员维持默认值(与今日一致),CreateTotalSeconds 返回值无意义 |
登记最终清账;DP2+ 身份合同批次裁决 |
| P3-2 | P3 | GetDefaultName 短 ID 抛异常;Duel entityKey 两处读取在身份发布后 NRE |
登记清账清单,指定 DP2+ 批次 |
| P3-3 | P3 | 基线外观察:S101LoginWindow.cs 编辑器最小认证探针与 IUserService 无关,不构成双提供方影响 |
无须动作,记录在案 |
9. 可执行修订建议(供主会话直接裁决)
- 方案按 R1 改写未决点 1 为确定裁决(§2 表原样落入方案)。
- DP1 白名单定格:生产 =
LoginStage.cs、PFLoginAct.cs、QuitAct.cs、PlayerKVGameplayBindings.cs、UserService.cs、UserService.KVLoad.cs、新增Services/PlayerKVUserService.cs(命名可裁);测试 =KVGameLoadTests/ServiceHost/Program.cs、Stubs.cs及既有 formal 用例迁移、必要新增断言。SubBoostrap.cs现状无 DI/冻结缺口,不入白名单(若实施发现缺口须先补审)。 - 明确禁止项写入白名单:新类禁 PlayFab using/具体类型引用;不碰
UserService.KVGameplayLoop.cs、AggUserService.cs、PlayerKVEditorValidationHost.cs、DataCenter.SmallGame.cs、ClubService.cs、场景/Prefab/服务器/付费/社交路由。 - R3 的中立面机制二选一定死后才可编码;建议窄接口(消费者明确、两类实现语义分离)。
PlayerKV*改名、Core/KVMgr物理依赖、公开身份发布、GetDefaultName/时间字段语义——全部留在既有延期清单,本批不动。
再次声明:本报告落盘不等于批准编码。主会话须先重核 18/18 hash(本报告 §0 可复核),再逐项裁决并落精确白名单。