27 KiB
阶段4B2字段与模块加载合同(正式版2:合并D1/D2/D3)
日期:2026-09-23。状态:已审设计合同,供检查点2继续实施;不代表已实现或验收。正文合并修订2附件与D1/D2/D3差异修订,依据差异裁决生效。D3依据值类型空态裁决生效。后续实施首读本正式版2;原受审附件及其hash保留供历史核对,不再作为D1/D2的当前规则。其余合同沿用修订2方案及原实施批准。本清单限定于当前PlayerData.LoadPlayerData/LoadPlayerDataNew同步消费链和登录标记,不等于全项目玩法存档盘点。其余按需玩法读取由4B3/4C另审,受控入口不启动那些流程。
1. 统一规则
- 所有适配器为本次epoch产生独立的加载状态;不与原快照、上次账号、静态默认字典或其他模块共享可变集合。
D<K,V>为JSON对象解码到相应字典,L<T>为JSON数组,T为现有保存DTO的序列化字段形状。必须先检查token类型、重复属性、null、数值溢出与有限浮点数;禁止靠Json.NET字符串/浮点到整数的宽松转换判合法。键转为整数/枚举后重复也拒绝。- 原保存DTO有构造副作用时用新文件中的同形状纯数据结构解码,不能在Decode里Resolve服务;应用时仅传已解码字段。不得反射复制真实模块全部字段,配置/服务/订阅不属于存档。
- 已存在的可选键坏值也使整个加载Malformed。缺失才生成下面列明的空态。JSON文字null不是缺失;集合可以为空但不能为null,活动对象缺失由Missing状态表达,不向旧loader传
"null"。 - 未识别顶层键保留原快照、计数为Unconsumed,不送给总入口或参与主档判定。注释掉的MonthCardData、ChallengeData、TargetEventPackData、PokerScoreData、EventPartnerData、EventBreak/EventCan不算当前消费键。
- 配置引用只验证该加载步骤会解引用的表项;0作为尚未激活/未选择的明确哨兵保留,不统一要求所有活动ID都为正。活动已失效但载荷可解析时保持Dormant,不启动活动、不自动清档/写回。
- 数字紧凑串按InvariantCulture且无千位分隔解析;时间兼容按
O(RoundtripKind)优先,之后仅允许本次捕获的CurrentCulture与InvariantCulture的TryParse;若两者成功却得到不同日期,返回UnsupportedSchema,不猜测。newData中日期含分隔逗号导致槽数不是19同样拒绝。既有文化存档的不兼容必须明确报错,未来迁移另审,不改PF解析规则。
2. 主档格式:完整核心字段,不再使用“任一特征键命中”
支持两种已知布局:
- 现代格式:顶层
NewPlayerDataDic为D<string,string>,其newData恰好19个逗号槽;每槽必须可解析。只有NewPlayerDataDic而没有完整newData,不推断为新档。 - 完整旧格式:下表的19个顶层键全部存在且合法。不支持用部分旧键加部分新槽拼出主档。NewPlayerDataDic内的旧散键只作一致性校验,不单独授权完整档;缺核心时返回MissingCore,要求显式迁移,不造默认资产。
两格式同时完整存在时逐字段比较规范值,任意不同返回ConflictingRepresentations。现代格式存在但损坏,不回落旧格式。现存非完整旧格式只要包含与现代格式重叠的字段,也逐字段检查一致;不能让残余冲突值被静默覆盖。
| 槽 | 顶层旧键 | NewPlayerDataDic旧散键 | 类型/主档规则 |
|---|---|---|---|
| 0 | Lv | lv | Int32,>=1 |
| 1 | Gold | gold | UInt64 |
| 2 | vipAllScore | vipS | Int32,>=0 |
| 3 | Energy | energy | Int32,>=0,坏/负值拒绝而非改0 |
| 4 | Diamond | pearl | Int32,>=0;源码PlayerData.Pearl常量实际是Diamond |
| 5 | Magnification | mag | Int32,>=0 |
| 6 | EquipRodID | rodID | Int32,必须在固定TbRodData存在,不以默认杆掩盖坏档 |
| 7 | LastEnergyRecoveryTime | EnergyTime | 时间,按§1解析规则 |
| 8 | RewardNewMap | rewardNewMap | Int32原始位集;保持有符号位表示,不按普通计数限制 |
| 9 | EnergyRM | energyRM | Int32,>=0 |
| 10 | GoldRM | goldRM | UInt64 |
| 11 | DiamondTrue | diamond | Int32,>=0;不能错认成Diamond |
| 12 | CurrentMapId | cMapID | Int32;0为原入口哨兵,非0须存在于TbMapData |
| 13 | LastMapId | lMapID | 同上 |
| 14 | FishingFrequency | FF | Int32,>=0 |
| 15 | AllFishingFrequency | AFF | Int32,>=0 |
| 16 | HeistGameCount | HGC | Int32,>=0 |
| 17 | BombGameCount | BGC | Int32,>=0 |
| 18 | FishHuntGameCount | FHGC | Int32,>=0 |
这是一项比原PF加载更严格的KV准入提案,不声称所有历史PF档都兼容;不使用正式账号猜测规则。本阶段只用合成完整档/残档验证。凡不符合支持布局的真实档将来必须走单独迁移,不自动建档。
3. 全部当前消费键登记及优先级
表中的模块编号也用于产物诊断。主档19键及NewPlayerDataDic由M01/M02消费;其余顶层键如下,均可缺失但不可损坏。
| 模块 | 顶层键/模式 | 格式合同 |
|---|---|---|
| M01 玩家附加 | NewRodList、SeasonId、IsOpenAppraise、InfiniteBuildingData | L、Int32、存在标记(任意非null字符串,仅存在性转true)、原始字符串(加载期不解释建筑载荷) |
| M03 鱼/杆/地图 | RodData、RodLevelData、FishSwimToMap、FishSwimToMapCount、MapProgress、HonorLevelData | D<Int32,Int32>;整型负哨兵按原类型保留,不误把所有键看成表ID |
| M03 鱼/杆/地图 | InitRodAccessoriesData、Map_5_{mapId}、Map_{mapId}、LuckDataDic | RodGSAData形状;D<Int32,string>每项鱼紧凑串;旧D<Int32,FishWeightData>;D<string,float> |
| M04 商店 | ShopPackBuyCount、ShopAllPackBuyCount、ShopGiftBuyCount | D<string,Int32>、D<Int32,Int32>、D<Int32,Int32> |
| M04 商店 | Voucher、ChainPackDataNew、VIPAdPackData、TurntableAdData、BuyData、AdvertAllCount | Int32、DailySignEvent、AdvertData、DailySignEvent、L、D<Int32,Int32> |
| M04 商店 | DiamondShopRefreshCount、DiamondShopRefreshTime、DiamondShopItemRecords、BlackFridayCouponDate | Int32、时间、D<Int32,ItemShopRecord>、优惠券紧凑格式 |
| M05 活动基础 | LimitedTimeEventDic、FixedTimeEventDic、ETDropAfterDropDic、RankTargetCount、WheelLevelCount | D<Int32,LimitedTimeEvent>、D<Int32,FixedTimeEvent>、后三者D<Int32,Int32> |
| M05 活动基础 | TurntableRoll、WheelOperateCount、AllWheelOperateCount、WheelLevelFreeCount、PVPToken | Int32 |
| M06 增益 | BuffDataList、TargetBuffDataList、FishDailyBuff、GlobalBuffDataList | D<Int32,FishBuffTimeData>、L、FishBuffTimeData、D<Int32,FishBuffTimeData> |
| M07 营地 | IsFix、IsOpenSkyscraper、CampDataDic | bool、精确0/1字符串、D<string,Int32>;不能调用SetIsFix默认isSave=true |
| M08 成就 | AchievementData | D<ConditionType,AchievementData> |
| M09 钓鱼箱 | FishingBoxData | 原FishingBoxData的序列化形状,实际加载FishingBoxOpenNum;FishingNewBoxs按§5重置 |
| M10 引导 | GuideSaveData、GuideSaveDataNew | D<string,GuideSaveData>;新格式`group |
| M11 战令 | BattlePassRecordTask、BattlePassRecord、BattleTask | 新组合紧凑格式;旧BattlePassRecord/旧BattleTask JSON形状 |
| M12 签到 | MonthlySignEvent | MonthlySignEvent形状;dailySignEvent不允许null |
| M13 触发礼包 | TriggerPackDataList、GiftVIPDData、FixedTimeShopDic | L、D<string,GiftVIPDData>、D<Int32,FixedTimeEvent> |
| M14 公会加载态 | ClubSignin、LureDonateData、IsOpenClubRed | 时间或空字符串(无签到)、LureDonateData形状、存在标记(非null字符串→false) |
| M15 挑战 | FishingChallengeData | 现有InitChallengeData(string)的DTO形状,仅数据解码,不启动挑战 |
| M16 宝箱链礼包 | OfferChainsChestData | TOfferChainsData |
| M17 Disco礼包 | OfferDiscoPartyManager | TOfferDiscoPartyData |
| M18 伙伴 | EventPartnerGatherData | EventPartnerGatherData,不能调用带Save的ClearData |
| M19 消块 | EventCubeMeltData | EventCubeMeltData |
| M20 活动礼包 | EventPackData | 现有LoadEventPackData的数据形状;配置派生仅在Apply前解码计算 |
| M21 储蓄罐 | PiggyBankPackData | PiggyBankPackData.Data;旧兼容redirectID=0规范为4055001并检查配置存在,不写回 |
| M22 砍价礼包 | GeneralEventPackData | GeneralEventPackData;redirectID须存在且PackType=3才可交BargainPackData,否则UnsupportedSchema,不能静默丢弃 |
| M23 足迹 | FootPrint | UInt64位集,完整值由新增私有ulong受控字段保存;现有Int32枚举仅提供低32位视图,不代表全部64位;不调用UploadData,不拒未声明高位 |
| M24 靶场 | EventShootingRangeData | EventShootingRangeData.RawData |
| M25 药水 | epdk | EventPotionData |
| M26 弹珠 | epbdk | EventPinballData |
| M27 刮刮卡 | estdk | EventScratchTicketData |
| M28 节日礼包 | ThanksGivingPackData、ThanksGivingPackMigrationData | 各自4个Int32逗号槽:EventId,RedirectId,TokenProgress,ChainProgress |
| M29 Pinata | EventPinataPackData | EventPinataPackData;仅SetData对应数据赋值,不GetFreshPinataPackData |
| M30 挖矿加载壳 | 无 | InitServerData当前忽略字典,仅在_dataModel为空时new;KV必须替换成新未启动模型,不能复用旧实例 |
| M31 登录附属 | MMTest、hwGoogle、hwFacebook、Apple、EAccoutUrl、TapJoy_Diamond | MMTest规则见主方案;渠道精确0/1;D<string,string>;Int32余额。额外当前非游客渠道名只允许来自测试身份枚举,不能任意字符串入意图 |
3.1 NewPlayerDataDic的附属键
| 键 | 解析及覆盖语义 |
|---|---|
| vipL | Int32,Int32记录以` |
| ld | 恰好3个有限float,以` |
| ad | `ConditionType:value1 |
| bd | Int32,Int32记录以;连接;替换FishingBoxOpenNum,空串为空字典 |
| ct、rt | Int32:Int32记录以,连接;分别替换ETDropAfterDropDic、RankTargetCount,空串为空字典 |
| LDD | 旧JSON格式同LuckDataDic;与ld共存时先分别严格解码,规范值必须一致,否则冲突 |
| AD | 旧JSON为D<ConditionType,string>,每个字符串恰好Id|Target|day三槽,按Int32/UInt64/Int32、InvariantCulture严格解析;不同于顶层AchievementData的对象值。键须为已定义ConditionType,归一化后重复键拒绝。与ad/顶层共存时全部先独立严格解码,共有字段冲突则ConflictingRepresentations;一致按ad > AD > 顶层AchievementData选择并整字典替换,不合并旧账号条目 |
NewPlayerDataDic中的其余旧散键为§2的19项,全部校验。其它内部键保留为Unconsumed,不能参与初始化。
同义表示同时出现时不依赖Dictionary迭代顺序:核心先作一致性检查;其余 Map_5_x与Map_x、GuideSaveDataNew与GuideSaveData、BattlePassRecordTask与旧战令、ld/LDD/LuckDataDic、ad/AD/AchievementData、bd/FishingBoxData、ct/ETDropAfterDropDic、rt/RankTargetCount全部先分别严格解码,共有字段不同则Conflict;一致时选新格式,缺新则选旧。新引导替换整个字典,不把未出现在新格式的旧组静默合并。
3.2 紧凑格式与动态键边界
- Map动态后缀必须正整数且在固定TbMapData内;同一数值的非规范十进制后缀拒绝。只匹配
Map_5_数字与Map_数字,其它前缀属于Unconsumed。鱼记录支持源码现存5/9槽两种:5槽Level/MaxG/TotalCard/TotalNum/NewFish;9槽采用0/1/5/6/7这五个位置,其余兼容槽必须保留但不作为当前字段。所有被消费槽严格解析。 - BattlePassRecordTask恰好两个
#分段(即一个分隔符);第一段按现存BattlePassRecord 7个|字段解析(ID、Level、Exp、BuyVipToastIndex、IsUnlockVip、两份逗号ID列表),第二段按BattleTask 5个|字段(两份key:value逗号字典、loginDays逗号列表、两个截止ticks)。空集合串允许,记录字段数量不符拒绝;ticks须在DateTime范围内。以受审源码SerializeObject顺序为准,不能改存档格式。 - BlackFridayCouponDate支持7槽旧格式或9槽新格式,6个日期分量必须组成合法DateTime;旧格式RedirectId=0/IsBought=false,新格式末位只允许0/1;8槽或多余槽拒绝。
- ThanksGiving两种键都严格4槽;不能复用原Data.Deserialize的“错误时打印原值并返回零对象”容错。
- 所有分隔集合末尾空记录按当前保存器的Join格式:空整体表示空集合,非空整体中的空记录/重复key为Malformed。不得日志输出被拒绝的原字符串。
4. 无副作用加载路径及写点裁决
修订2不再调用旧LoadPlayerData/LoadPlayerDataNew总入口,以下旧路径仅为提取数据规则的依据。
| 旧同步链/风险点 | KV路径明确处置 |
|---|---|
| CompareDatas的PF×PlayerPrefs合并与写回 | 完全不调用;只有4B1不可变快照和独立解码状态 |
| InitData MMTest缺失/旧格式两处写+SetMMTestValue写 | 内存规范化及本epoch意图;不运行GEvent统计 |
| Agg InitData补渠道、AvatarToPF写EAccoutUrl | 受控仅纯标记合并;不运行真实SDK和Login返回后的头像/邀请链 |
| Camp.SetIsFix(isSave:false) | 原路径本次抽查安全,但KV直接赋私有字段,禁止误调用默认true |
| PlayerData.energy相关setter、体力恢复、SavePlayerData | 直接给energy/_lastEnergyRecoveryTime等赋值,不调用SetEnergy/恢复/保存或事件发布 |
| PlayerFishData InitData中的GEvent、地图派生调用 | 纯decode后整组替换;不统计、不发解锁提示 |
| FishingEventData Init/SetEvent/目标/排名恢复 | 不调用;只替换数据和置空运行派生对象;LimitedTime按冻结时钟做纯过滤 |
| Potion/Pinball/Scratch GetDataFromServer→Init | 不调用;只解析并替换原始数据,运行配置状态未激活 |
| Pinata.LoadData→GetFreshPinataPackData | 不调用刷新;只替换纯数据,奖励、红点不启动 |
| Bargain.LoadData→AddGiftFaceUIForce | 不调用旧函数;只替换_data |
| Partner.ClearData→SaveData、Cube.ClearData、活动默认建档 | 不借这些函数Reset;新增纯空态替换 |
| Shop.Init、BattlePass刷新任务/领奖、Signin刷新 | 不调用;加载不等于功能激活 |
| Digging.InitServerData之后的模型InitServerData异步服务 | 本阶段只new空模型;禁止调用异步业务初始化 |
| 所有旧LoadData带ELog/Debug输出原JSON、TryParse吞错 | 新KV纯入口只用结构化诊断;不能借用其日志和容错路径 |
| PlayFabMgr定时/暂停/销毁/普通/立即写 | 主方案guard前置拦截;违反计数导致受控加载失败 |
因此无需声称未经读取的所有玩法保存调用都被4B2重构。受控路径只允许运行附件的纯适配器;未来新调用/活动激活属于范围变化,先补方案再审。
5. 每模块空态、替换职责和精确文件清单
以下路径相对Client/Assets/Scripts/。每行都要实现/测试 DecodeForKVLoad(纯)、ResetForKVLoad、ReplaceFromKVDecoded;同一类可合并方法但不能遗漏状态。固定配置/注入服务引用保留;旧账号的已加载数据和从它派生的缓存必须清除。无玩法运行时的订阅/计时器要求为零,非零拒绝准入,不在此模块中擅自扩展玩法退出。
| ID | 文件(白名单) | 缺键/撤销时的明确空态与替换集合 |
|---|---|---|
| M01 | DataCenter/DataCenter.PlayerData.cs | NewPlayerDataDic/vipScoreDic/newRodList新空集合;19核心字段置为不可发布空态(数值0、时间MinValue),合法Apply后全部由核心档替换;seasonId=0、IsOpenAppraise=false、InfiniteBuildingData=null;vip/vipLastScore/PriceLv=0、priceLevel=null,地图缓存0。空态仅用于未就绪,不允许作为新档进入玩法 |
| M02 | DataCenter/DataCenter.SmallGame.cs | 三个游戏次数=0;加载派生的对手/预选缓存空、当前对手null;不触发取对手或网络 |
| M03 | DataCenter/DataCenter.PlayerFishData.cs;DataCenter/PlayerFishDataRod.cs | mapFishData/mapProgress/fishData/fishCardData/rodData/rodLevelData/fishSwimToMap/fishSwimToMapCount/LuckDataDic/_honorlevel全新空集合;rodAccessoriesData=null;InitBotLuck/InitFishLuck/BotCount=0;所有地图/杆加载派生开关false、选择0。缺配件不调用默认建档方法 |
| M04 | DataCenter/DataCenter.PlayerShopData.cs;UI/Shop/BlackFridayCouponData.cs | 购买/广告/钻石商店字典和buyData空,Voucher=0,ChainPackData/VIPAdPackData/TurntableAdData=null,刷新计数0/时间MinValue;CouponData原对象中账号字段归零/MinValue/false;SKU及配置引用不替换,不触发IAP查询 |
| M05 | DataCenter/DataCenter.FishingEventData.cs;DataCenter/FishingEvent_PVPData.cs | 限时/固定/目标/排名/WheelLevelCount全新空集合;TurntableCoupon取固定表InitWheelTicket,WheelOperateCount/AllWheelOperateCount/freecount/PVPToken=0;目标队列、rankInit/rankTarget、wheelInit/wheelLevel和活动实例引用null;解锁集合空。缺活动是未激活,不启动Init |
| M06 | DataCenter/BuffDataCenter.cs | map/target/global buffs和showFishBuffIds空,fishDailyBuff=null |
| M07 | Camp/CampDataMM.cs | CampDataDic空,_isFix=false、_isOpenSkyscraper="0";_id=101、PreID/AreaIndex/AreaStep/AreaId/AreaStepCount/TimelineId=0;当前场景/StepInfo/AreaList派生引用清空,不加载建筑/资源 |
| M08 | DataCenter/AchievementDataManager.cs | _achievementDatas整字典替换;空态空字典,不继续合并旧条目 |
| M09 | DataCenter/FishingBoxData.cs | FishingNewBoxs/FishingBoxOpenNum新空字典,不领取、生成箱子或发布奖励 |
| M10 | DataCenter/DataCenter.GuideData.cs | GuideSaveDataDic空、curSaveData/curDefine/curGroupDefine/popupPanelNode=null;配置指引定义保留,不启动引导 |
| M11 | DataCenter/BattlePassData.cs | BattlePassRecord新构造态(ID/Exp/BuyVipToastIndex/IsUnlockVip/InflationRate=0,Level=1,三份奖励列表空),BattleTask新零态且集合显式空;不Refresh/Save/领奖 |
| M12 | DataCenter/DataCenter.SignupData.cs | _monthlySignEvent=null;存在时替换,不读取会刷新日期/保存的dailySignEvent getter |
| M13 | DataCenter/DataCenter.TriggerPackData.cs | triggerPackList/giftVIPDData/fixedTimeShopDic新空;不Init、不注册条件事件 |
| M14 | Services/ClubService.cs | signTime=null、lureDonateData新零态、IsOpenClubRed=true;只处理三项加载态,不登录公会/取网络成员信息 |
| M15 | DataCenter/FishingChallengeManager.cs | 原始挑战存档对象null,挑战运行态未激活;有数据直接替换,不调用无参InitChallengeData |
| M16 | DataCenter/OfferChainsChestManager.cs | OfferChainsData=null;ValidIds空、活动派生引用null,不进场、不改FirstOpen |
| M17 | DataCenter/OfferDiscoPartyManager.cs | OfferDiscoPartyData=null、ValidIds空、活动派生引用null;FreeTime回构造值1 |
| M18 | DataCenter/EventPartnerGatherManager.cs | GatherData=null、_isEventDataChanged=false、当前伙伴运行引用null;PartnerInfoManager/RobotInvitationSystem替换为空的新实例,不调用含Save的ClearData |
| M19 | EventCubeMelt/Data/EventCubeMeltManager.cs | EventData=null、_isInit=false、活动运行引用null;不调用Init/ClearData |
| M20 | DataCenter/EventPackData.cs | _data是单个Data结构,空态四字段currentEventID/purchaseCount/VIPLvlWhenEventActivate/redirectID全0;存在时整体替换,活动有效且redirectID=0时沿用4054001的旧兼容映射并校验表,不TriggerEventPack、不发布红点 |
| M21 | DataCenter/PiggyBankPackData.cs | 原Data struct保持;Reset currentEventID/purchaseCount/VIPLvlWhenEventActivate/savingProgress/redirectID=0,doNeedTrigger/isTriggered=false,inflation=0,_kvLoadPresent=false(internal只读KVLoadPresent)。Missing保持该状态;Present八字段全赋后标记true;4055001映射仅用于存在载荷,不UpdateData |
| M22 | DataCenter/BargainPackData.cs | 原GeneralEventPackData struct保持;Reset十一整数、两bool、inflation清零/false,selections全新空List,_kvLoadPresent=false(internal只读KVLoadPresent)。Present通过PackType=3验证后完整替换、集合深复制、标记true,不经会触发FaceUI的LoadData;GeneralEventPackData.cs不改 |
| M23 | Services/FootPrintService.cs | 新增私有ulong完整KV位集及internal只读验证访问;Reset完整字段和_footPrint同时归零,Replace完整保留UInt64并用显式unchecked低32位投影到_footPrint(注释说明视图位宽)。公开EFootPrint : int、IFootPrintService及旧LoadData/UpdateFootPrint/UploadData不改;受控范围禁止旧保存,完整值的4B3消费另审,不承诺退出受控后可玩 |
| M24 | UI/ShootingRange/ShootingRangeData.cs | 原RawData struct保持;Reset EventId/CycleId/RedirectId/AmmoCount/CurrentStageIdx/ChainProgress=0,RewardFlag/TargetFlag按Int64归零,HasBeenOpened=false,Inflation=0,_kvLoadPresent=false(internal只读KVLoadPresent)。Missing不赋null,Present全字段替换后标记true;未就绪不得读玩法getter |
| M25 | DataCenter/EventPotionDataManager.cs | m_PotionData=null,运行关卡/表缓存回未激活;存在只装载原始数据,不调用Init |
| M26 | DataCenter/EventPinballDataManager.cs | m_PinballData=null,运行关卡/表缓存回未激活;同上 |
| M27 | DataCenter/EventScratchTicketDataManager.cs | m_ScratchTicketData=null,运行关卡/表缓存回未激活;同上 |
| M28 | UI/Shop/ThanksGivingPackData.cs;UI/Shop/ChainPackWithProgressData.cs | 两个实例的_data各自Missing/null,无共享可变对象;不做礼包迁移/领奖 |
| M29 | UI/PinataPack/EventPinataPackDataManager.cs | 同一容器的EventPinataPackData实例直接将EventID/PinataMainID/PurchasedCount三个自动属性全置0,然后由已解码状态全量赋值;不调用AddPurchasedCount或GetFreshPinataPackData,不替换容器注册、不反射。Decode不得new该数据类(其字段初始化会Resolve表),用纯三整数DTO |
| M30 | SandDig/DiggingGameManager.cs | _dataModel替换全新未初始化实例、_broadLogic=null、游戏状态Normal;已有玩法资源/音频/board时拒绝受控准入,不在此释放活跃玩法资源 |
| M31 | Services/UserService.cs及其新增KV partial;Services/AggUserService.cs | 本次副本/意图/绑定集合独立,MMTest=0、EAccoutUrl空、TapJoy余额0;真实SDK集合不作跨账号复用;PF公开身份默认不因受控加载而变成已授权 |
上表的“回未激活/派生引用”限定为相应loader能够写入或由该存档直接缓存的状态,不授权全面重构模块。实现需将各行定义映射到字段并在测试中显式断言,不能只测一个代表字段。若源码出现清单外的同步消费或状态写入,视为设计差异先补审,不能自动扩白名单。
固定Apply顺序:M01基础核心→M02→M03→M04→M05→M06→M07→M08→M09→M10→M11→M12→M13→M14→M15…M30→M31通知。全部Reset先执行,再开始任何Replace;物化DTO之间的引用由本次状态创建,配置引用只能来自同一只读快照。
6. 附件验收与证据口径
- 每行至少:存在合法值、完全缺失、存在坏值;A有数据/B缺数据在同对象上检查整组字段;所有三类用例不能只靠字典已清空来冒充实际模块状态。
- 每个现代/旧表示同时出现的组合验证一致与冲突;动态地图多ID、缺某地图、旧地图不会残留;同一模块多个可选键只缺其中一项也须清空该项。
- 数据类型测试覆盖两种货币键别名、19槽精确长度、时间文化歧义、负位掩码与非负计数区别、null/重复属性/未知表项;无正式账号值。
- 零副作用计数同时覆盖写入、SDK/网络调用、PlayerPrefs、事件、UI及计时器;任何非白名单调用导致失败,不能用最终数据库没变化代替。
- 本附件为已审设计约束与待验清单,没有声称31模块已经实测通过,也没有声称现有业务支持重载;主会话须在代码交付后逐行填入实测证据。
7. D1/D2补审合并的边界与必验项
D1:空对象和空紧凑整体表示空集合。JSON null、对象值冒充旧AD字符串、缺槽/多槽、空记录、溢出、重复属性、枚举归一化后重复和未定义枚举均为Malformed;不尝试另一值类型解码、不回落合法新表示掩盖坏旧表示。分别验证三种成就表示的合法/缺失/坏值、AD字符串正例/对象值反例、Target超UInt64、重复枚举别名、三者共存一致/冲突、坏AD与合法ad共存仍失败。M08在同对象A→B缺失时为空,零保存/事件。
D2:明确投影表达式例如unchecked((EFootPrint)(int)(uint)completeBits),注释视图仅含低32位、完整值保留在ulong字段;不依赖默认溢出检查设置。验证0、低位组合、2^31、2^32、UInt64.MaxValue的完整值与投影视图,拒负值/溢出,A高位→B缺失时两个字段一起归零。必须在Unity真实对象上验证,离线算术不能替代;旧PF原方法及公开面保持,既定PF行为回归仍执行。guard禁止受控范围内UploadData写入,未来4B3完整值消费另审。
P2-α迁移盘点输入新增:旧PF成就加载器曾静默跳过的AD坏槽条目。当前受控KV按严格合同整体Malformed;未来真实档方案必须覆盖识别、备份、修复或人工退出,并经独立评审及用户产品确认。此次不读取或迁移真实档,不授权自动跳过/清档/默认建档。
版本历史:原附件SHA256为9E1A49E389CD6E9B38FA2CB55E5839ED5356E915AECCD4787FFE6F8276340745,保持不变。本正式版仅合并已审D1/D2及三条P3落地,未增加业务文件白名单;4B2实施完成仍需独立实施复核,检查点1不重做,检查点2–4顺序不变。
8. D3合并历史与验收补充
正式版1及原受审附件保留。D3已外部通过并由主会话裁决,三处值类型空态使用显式存在标记+全字段清零;具体字段与测试以《阶段4B2检查点2值类型空态差异补审方案》§2/§4为准。JSON null仍Malformed,缺键不规范成默认礼包,不改PF原字段类型/方法。同一真实对象A全部字段有值→B缺失→Present,验证所有字段及标记;M22集合不共享、M24 Int64高位清零;失败Reset后标记false,Reset失败Poisoned。D1/D2及其他合同不变。