57 lines
5.0 KiB
Plaintext
57 lines
5.0 KiB
Plaintext
using Loop=game.PlayerDataStorage.PlayerKVGameplayLoop;
|
|
int checks=0;
|
|
void Check(bool ok,string text){checks++;if(!ok)throw new Exception(text);}
|
|
ControlSlice Fresh(){Loop.Enabled=true;Loop.Ready=true;Loop.Faults=0;GContext.container.Player=new();Application.internetReachability=NetworkReachability.Reachable;return new();}
|
|
var s=Fresh();var player=GContext.container.Player;var first=s.RunAwaited();
|
|
Check(s.Continues==0 && player.Calls==1,"actual method waits for response");
|
|
s.Run();await s.RunAwaited();Check(player.Calls==1 && s.Continues==0,"duplicate consume and callback cannot repeat");
|
|
player.Response.SetResult();await first;Check(s.Continues==1,"actual success continuation exactly once");
|
|
await s.RunAwaited();Check(s.Continues==1 && player.Calls==1,"post-success duplicate suppressed");
|
|
foreach(var kind in new[]{"free","retry","duel","pack","special","missing-fish","missing-item","offline","activity","bad-mag"}){
|
|
s=Fresh();player=GContext.container.Player;
|
|
switch(kind){
|
|
case "free":s.fishingData.IsFree=true;break;case "retry":s.fishingData.IsRetry=true;break;
|
|
case "duel":s.fishingData.IsDuel=true;break;case "pack":s.fishingData.Pack=true;break;
|
|
case "special":s.fishingData.General=false;break;case "missing-fish":s.fishingData.curFishData=null;break;
|
|
case "missing-item":s.fishingData.fishItem=null;break;case "offline":Application.internetReachability=NetworkReachability.NotReachable;break;
|
|
case "activity":s._tables.TbFishingEvent.DataList.Add(new());break;case "bad-mag":player.magnification=-1;break;}
|
|
await s.RunAwaited();Check(player.Calls==0 && s.Continues==0 && Loop.Faults>0,"pre-consumption gate "+kind);
|
|
}
|
|
foreach(var kind in new[]{"fish","rod","map","mag","free","retry","duel","disabled","cancelled","fault"}){
|
|
s=Fresh();player=GContext.container.Player;var task=s.RunAwaited();
|
|
switch(kind){case "fish":s.fishingData.curFishData.ID++;break;case "rod":s.fishingData.RodId++;break;
|
|
case "map":player.lastMapId++;break;case "mag":player.magnification++;break;case "free":s.fishingData.IsFree=true;break;
|
|
case "retry":s.fishingData.IsRetry=true;break;case "duel":s.fishingData.IsDuel=true;break;
|
|
case "disabled":s.isActiveAndEnabled=false;break;case "cancelled":s.kvFishingAction.Cancelled=true;break;
|
|
case "fault":Loop.Ready=false;break;}
|
|
player.Response.SetResult();await task;Check(player.Calls==1 && s.Continues==0,"no stale continuation "+kind);
|
|
}
|
|
s=Fresh();player=GContext.container.Player;var failed=s.RunAwaited();player.Response.SetException(new InvalidOperationException("Unknown"));await failed;
|
|
Check(s.Continues==0 && player.Calls==1 && !Loop.Ready,"async exception observed, no retry");
|
|
s=Fresh();Loop.Enabled=false;s.Run();Check(s.LegacyCalls==1 && GContext.container.Player.Calls==0,"default paid PF path unchanged");
|
|
s.fishingData.IsFree=true;s.Run();Check(s.Continues==1,"default free PF path unchanged");
|
|
Console.WriteLine($"PASS {checks} assertions; compiled actual source methods; visual continuation and storage boundaries are spies; no network");
|
|
public enum NetworkReachability{NotReachable,Reachable}
|
|
public static class Application{public static NetworkReachability internetReachability;}
|
|
public enum ConditionType{ConsumeEnergy}
|
|
public sealed class Fish{public int ID=1;}
|
|
public sealed class FishingData{public object fishItem=new();public Fish curFishData=new();public int RodId=1;public bool IsFree,IsRetry,IsDuel,Pack;public bool General=true;public bool IsGeneralFish()=>General;public bool IsPackItemFish()=>Pack;}
|
|
public sealed class EnergyDef{public int Mag=3;}
|
|
public sealed class EnergyTable{public List<EnergyDef> DataList=new(){new()};}
|
|
public sealed class ConditionRule{public ConditionType Condition=ConditionType.ConsumeEnergy;}
|
|
public sealed class Activity{public List<ConditionRule> ConditionList=new(){new()};}
|
|
public sealed class ActivityTable{public List<Activity> DataList=new();}
|
|
public sealed class Tables{public EnergyTable TbEnergyDef=new();public ActivityTable TbFishingEvent=new();}
|
|
public sealed class PlayerData{public int Calls,magnification,lastMapId;public TaskCompletionSource Response=new();public async Task ConsumeEnergyForKVLoopAsync(int n,Loop.FishingAction a){Calls++;await Response.Task;a.Confirmed=true;}}
|
|
public sealed class Container{public PlayerData Player=new();public T Resolve<T>()=>(T)(object)Player;}
|
|
public static class GContext{public static Container container=new();}
|
|
namespace game.PlayerDataStorage{public static class PlayerKVGameplayLoop{
|
|
public static bool Enabled,Ready;public static int Faults;
|
|
public sealed class FishingAction{public bool Consuming,Cancelled,Confirmed,Continued;}
|
|
public static bool FishingCurrent(FishingAction a)=>Ready && !a.Cancelled;
|
|
public static bool ClaimFishingConsumption(FishingAction a){if(a.Consuming)return false;a.Consuming=true;return true;}
|
|
public static InvalidOperationException Stop(string m){Fault(m);return new(m);}
|
|
public static void Fault(string m){Ready=false;Faults++;}
|
|
public static void ContinueFishing(FishingAction a,Action next){if(!FishingCurrent(a)||!a.Confirmed)throw Stop("invalid");if(a.Continued)return;a.Continued=true;next();}
|
|
}}
|