5.9 KiB
阶段4B2检查点2值类型空态差异补审方案
日期:2026-09-23。状态:D3待差异评审。D1/D2已批准并推进,无需重审。当前补充来自继续实施时对实际字段类型的核查;主会话此前附件将三处值类型误写为null空态,本次集中纠正,未先改相应业务代码。
1. 三处源码事实与合同冲突
| 模块 | 受审附件空态 | 实际源码 | 结论 |
|---|---|---|---|
| M21 PiggyBank | _data=null |
DataCenter/PiggyBankPackData.cs:14声明私有struct Data,:36为非Nullable的Data _data |
不能赋null |
| M22 Bargain | _data=null |
DataCenter/BargainPackData.cs:14为GeneralEventPackData _data,DataCenter/GeneralEventPackData.cs:3声明public struct GeneralEventPackData |
不能赋null |
| M24 ShootingRange | _data显式Missing/null |
UI/ShootingRange/ShootingRangeData.cs:11为RawData _data,:335声明私有struct RawData |
Missing可用显式标记表达;null不能直接赋值 |
路径相对Client/Assets/Scripts/。不是编译器或平台差异,也不是现有文件漂移:三处原始源码与修订2开工基线一致。GeneralEventPackData.cs仅作为只读类型证据,不加入修改白名单。
2. 推荐修订:显式缺失状态+原值类型清零
保留三个现有字段的类型、公开属性、构造器和PF原方法,新增内部KV加载存在标记(每模块一个私有bool字段,默认false,internal只读供验证)。纯解码状态也有独立IsPresent;缺键产生Missing,不调用旧Load/Init/Update或默认建档,JSON文字null仍Malformed。
固定步骤:全部模块Reset阶段先将标记false、原字段按下表清空;Replace收到Missing时保持该空态,收到已严格解码的Present状态时整体赋值,最后把标记true。Present不能从ID非0推断:合法可解析但未激活数据仍按已有Dormant规则处理;“存在的零值记录”和“缺键”可由标记区分。标记只用于内部受控加载,不改变PF公开行为,不授玩法就绪或保存权限。
| 模块 | Reset具体空态 | Present替换 |
|---|---|---|
| M21 | _data=default(Data);currentEventID/purchaseCount/VIPLvlWhenEventActivate/savingProgress/redirectID=0,doNeedTrigger/isTriggered=false,inflation=0;存在标记false |
完整赋入已解码的八字段后置标记true;redirectID=0的既有兼容映射4055001及固定配置验证仅用于存在载荷,缺键不映射 |
| M22 | _data=default(GeneralEventPackData)后将selections设为本次新空List;全部整数/布尔字段及inflation清零/false,存在标记false,详见下方字段枚举 |
保留已有严格DTO及PackType=3配置验证,整体赋值、selections深复制,最后标记true;不调用会发FaceUI的旧LoadData |
| M24 | _data=default(RawData);所有整数字段、Int64位集和浮点字段归零,HasBeenOpened=false,存在标记false,详见下方字段枚举 |
仅已解码RawData整体替换后标记true,不运行活动;未就绪不得读取需要有效配置的玩法getter |
M22整数:currentEventID、redirectID、VIPLvlWhenEventActivate、purchaseCount、savingProgress、discountLvl、visualProgress、visualDiscountLvl、rodID、lastPackIDBought、packID(11项);布尔:doNeedTrigger、isTriggered(2项);float:inflation;集合:selections。本行的全量枚举为精确合同,避免将数量摘要当成字段定义。
M24整数:EventId、CycleId、RedirectId、AmmoCount、CurrentStageIdx、ChainProgress(6项);Int64位集:RewardFlag、TargetFlag(2项);bool:HasBeenOpened;float:Inflation。按声明位宽清零,不能把Int64位集缩窄成Int32。
Reset仅清受控加载态;不清表/服务引用,不调用运行时getter,不发事件、红点、UI、网络或保存。若整个Apply失败,按既定事务合同再次Reset全部模块;Reset异常仍Poisoned销毁容器,不恢复旧账号。
3. 备选路线与范围
- 不推荐把现有字段改为Nullable:会迫使多处PF getter/方法调整、扩大回归范围,违反本轮保持PF原路径的约束。
- 不推荐只清零而无存在标记:无法证明缺键和Present零值记录区别,不符合显式Missing要求。
- 推荐保留原值类型,加仅内部加载存在标记;这是具体空态承载修订,需要本次补审,但不新增业务文件白名单,不改变存档JSON格式或完整主档规则。
本提案不调整其他模块空态。M15/16/17/18/19/25/26/27/28等相关载荷已按声明核查为class;M08/M23已完成的纯入口不受此差异影响。配置计算型getter不是账号持有的缓存,不为了“置null”而改变其PF公开行为。
4. 必验用例
每模块用同一真实对象执行:A所有字段非零/集合非空→B完全缺键→Present合法数据,显式断言上述每个字段和存在标记,不能只断言活动ID。M22同时证明selections为新集合、与A及解码集合不共享;M24验证Int64高位写入→缺键清零。
缺键、JSON null、坏值分别产生Missing/失败/失败;存在零值与缺键由IsPresent区分(仍须满足该模块其他已审配置准入条件);M21缺键不规范出4055001,不生成业务存档。Present阶段失败/撤销后全部Reset,失败不能留下true标记;不执行旧Load/Update/Init/Save,不触发UI/SDK/PlayerPrefs。
Unity真实对象验证需在已审隔离宿主下完成,不以离线DTO或default表达式测试替代;PF原方法文本保持,原行为回归仍执行。M21/22/24目前未修改;本次不预填实现通过。
5. 下一步
外部只审D3三处空态修订→主会话核对hash并逐条裁决→合并至下一正式附件版本→继续剩余检查点2及3/4。此次不会撤回D1/D2通过或重做检查点1。P2-α真实档兼容/迁移/用户确认启用门、固定地址、Unity CLI Loop、不用UnitySkills及暂不打包均保持。