using game.PlayerDataStorage; using System.Globalization; static partial class Tests { static async Task RunCampEventSchemaTests() { await Run("M07-camp-strict-fields-and-copy", () => { var raw=new Dictionary { ["IsFix"]="True",["IsOpenSkyscraper"]="1",["CampDataDic"]="{\"CampId\":205}" }; var state=PlayerKVSaveSchema.DecodeCamp(raw,new()); Check(state.IsFix && state.Skyscraper=="1" && state.Values["CampId"]==205,"all raw fields"); var missing=PlayerKVSaveSchema.DecodeCamp(new Dictionary(),new()); Check(!missing.IsFix && missing.Skyscraper=="0" && missing.Values.Count==0,"missing defaults"); foreach(var key in raw.Keys) { foreach(var bad in new[] { "null",null,"[]","1.0" }) Check(PlayerKVSaveSchema.DecodeCamp(new Dictionary(raw) { [key]=bad },new())==null,"bad optional fails"); } Check(PlayerKVSaveSchema.DecodeCamp(new Dictionary { ["IsOpenSkyscraper"]="true" },new())==null,"exact sky flag"); var source=new Dictionary { ["CampId"]=205 }; var copied=new PlayerKVCampState(true,"1",source); source.Clear(); Check(copied.Values.Count==1,"independent map"); return Task.CompletedTask; }); await Run("M20-event-pack-active-dormant-config", () => { var events=new Dictionary { [1]=new(100,"2026-09-24T00:00:00.0000000Z"),[2]=new(999,"2026-09-22T00:00:00.0000000Z"), [3]=new(200,"2026-09-24T00:00:00.0000000Z"),[4]=new(100,null) }; var snapshot=new PlayerKVSchemaSnapshot("M20",Array.Empty(),Array.Empty(),new Dictionary(), CultureInfo.InvariantCulture,new DateTime(2026,9,23,0,0,0,DateTimeKind.Utc),0, new Dictionary { [100]=1,[200]=3,[4054001]=1 },events); events.Clear(); Dictionary Raw(int eventId,int redirect=0) => new() { ["EventPackData"]=$"{{\"currentEventID\":{eventId},\"purchaseCount\":2,\"VIPLvlWhenEventActivate\":3,\"redirectID\":{redirect}}}" }; var state=PlayerKVSaveSchema.DecodeEventPack(Raw(1),snapshot,new()); Check(state.EventId==1 && state.PurchaseCount==2 && state.VipLevel==3 && state.RedirectId==4054001,"active old default normalized after copied config"); foreach(var eventId in new[] { 0,2,999 }) { state=PlayerKVSaveSchema.DecodeEventPack(Raw(eventId),snapshot,new()); Check(state.EventId==eventId && state.RedirectId==0 && state.PurchaseCount==2,"dormant values retained"); } foreach(var invalid in new[] { Raw(3),Raw(4),Raw(1,999) }) { var e=new PlayerKVSchemaDiagnostics(); Check(PlayerKVSaveSchema.DecodeEventPack(invalid,snapshot,e)==null && e.Count(20,PlayerKVSchemaError.UnsupportedSchema)==1,"config dereference failure explicit"); } foreach(var bad in new[] { "null","{}","{\"currentEventID\":1,\"purchaseCount\":\"2\",\"VIPLvlWhenEventActivate\":3,\"redirectID\":0}" }) Check(PlayerKVSaveSchema.DecodeEventPack(new Dictionary { ["EventPackData"]=bad },snapshot,new())==null,"strict object"); state=PlayerKVSaveSchema.DecodeEventPack(new Dictionary(),snapshot,new()); Check(state.EventId==0 && state.PurchaseCount==0 && state.VipLevel==0 && state.RedirectId==0,"missing all zero without normalization"); return Task.CompletedTask; }); } }