test(upserver): 归档存档迁移与玩法闭环回归工具

This commit is contained in:
Liubing\LB
2026-09-28 18:16:14 +08:00
parent c4ae02def9
commit a6f5e107f8
232 changed files with 12903 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>

View File

@@ -0,0 +1,12 @@
using System.Reflection;
using game.PlayerDataStorage;
int checkedTypes=0;
foreach(var type in new[]{typeof(PlayerKVGameLoadCapability),typeof(PlayerKVControlledLoadCapability)})
{
if(type.GetMethod("CreateForValidation",BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic)!=null || type.IsPublic ||
type.GetConstructors(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance).Any(c=>!c.IsPrivate))
{Console.WriteLine("FAIL player build capability construction surface");return 1;}
checkedTypes++;
}
Console.WriteLine($"PASS non-editor production capability factories absent types={checkedTypes}; compile probe only, no Unity player/package produced");
return 0;

View File

@@ -0,0 +1,163 @@
using System.Collections.Concurrent;
using System.Globalization;
using System.Net;
using System.Reflection;
using game;
using game.Account;
using game.PlayerDataStorage;
using Newtonsoft.Json;
using PlayFab.ClientModels;
using tysdk;
static class Probe
{
static int assertions,groups;
static void Check(bool condition,string label){assertions++;if(!condition)throw new Exception(label);}
sealed class Loop:SynchronizationContext
{ internal BlockingCollection<(SendOrPostCallback,object)> Queue=new();public override void Post(SendOrPostCallback c,object s)=>Queue.Add((c,s)); }
static int Main() { try { RunHost(); return 0; } catch(Exception ex) { Console.WriteLine(ex); return 1; } }
static void RunHost()
{
var loop=new Loop();SynchronizationContext.SetSynchronizationContext(loop);
var task=Run();_=task.ContinueWith(_=>loop.Post(_=>{},null),TaskScheduler.Default);
while(!task.IsCompleted){var(c,s)=loop.Queue.Take();c(s);}task.GetAwaiter().GetResult();
#if UNITY_ANDROID
const string platform="ANDROID+AGG";
#else
const string platform="NON_ANDROID";
#endif
Console.WriteLine($"RESULT service-host {platform} groups={groups} assertions={assertions} formalRequests=0");
}
sealed class Handler:HttpMessageHandler
{
readonly Func<HttpRequestMessage,Task<HttpResponseMessage>> send;
internal Handler(Func<HttpRequestMessage,Task<HttpResponseMessage>> send)=>this.send=send;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage r,CancellationToken t)=>send(r);
}
static Task<HttpResponseMessage> Json(object value,HttpStatusCode status=HttpStatusCode.OK)=>Task.FromResult(new HttpResponseMessage(status){Content=new StringContent(JsonConvert.SerializeObject(value))});
static readonly Uri Api=new("http://synthetic.invalid/");
static Config Configuration(){var c=new Config();c.Set("EventApiURL",Api.AbsoluteUri);c.Set("DEBUG","1");return c;}
static readonly string[] Core={"3","4","4","5","6","7","101","2026-09-23T00:00:00Z","-1","8","9","9","1","0","10","11","12","13","14"};
static Dictionary<string,string> Raw()=>new(){["NewPlayerDataDic"]=JsonConvert.SerializeObject(new{newData=string.Join(",",Core)}),["TapJoy_Diamond"]="9"};
static PlayerKVSchemaSnapshot Snapshot()=>new("stub-v1",new[]{101},new[]{1,2},new Dictionary<string,int>{{"None",0}},CultureInfo.InvariantCulture,new DateTime(2026,9,23,0,0,0,DateTimeKind.Utc),17);
static PlayerKVIdentitySnapshot Identity()=>new("FF",false,PlayerKVIdentityChannel.hwGuest,Array.Empty<PlayerKVIdentityChannel>());
static object Private(object target,string method,params object[] args)=>target.GetType().GetMethod(method,BindingFlags.Instance|BindingFlags.NonPublic,null,args.Select(a=>a.GetType()).ToArray(),null).Invoke(target,args);
static void Pass(string name){groups++;Console.WriteLine("PASS "+name);}
static async Task Run()
{
foreach(bool agg in new[]{false,true})
{
var config=Configuration();PlayFabUserService user=agg?new AggUserService():new PlayFabUserService();user.config=config;user.CustomId="101";
foreach(var backend in new[]{"kv","KV","unknown","",null})
{
config.Set("SaveBackend",backend);Effects.Clear();
Check(await user.Login(null)==ELoginStep.Api,"public backend gate");
Check(user.KVLastError==(backend=="kv"?PlayerKVGameLoadError.BackendNotEnabled:PlayerKVGameLoadError.BackendUnsupported),"typed rejection");
Check(Effects.Total==0,"before all SDK/event access");
}
}
Pass("public-backend-rejection-before-AGG-shortcut");
var pf=new PlayFabMgr();Private(pf,"Awake");
var data=new Dictionary<string,UserDataRecord>{{"MMTest",new(){Value="MMFailParam#4",LastUpdated=DateTime.UtcNow}},{"TapJoy_Diamond",new(){Value="7",LastUpdated=DateTime.UtcNow}}};
pf.CompareDatas(data,"synthetic");pf.UpdateUserDataValue("local","value",false);
Check(pf.GetLocalData("local")=="value","default PF local write unchanged");
Check(await pf.SyncDataImmidiate("remote","value"),"default PF immediate result unchanged");
Pass("default-PF-storage-passthrough");
foreach(bool agg in new[]{false,true})
{
var config=Configuration();PlayFabUserService user=agg?new AggUserService():new PlayFabUserService();user.config=config;user.CustomId="101";
var server=new CustomServerMgr(config,_=>new Handler(r=>Json(new{})));user.customServerMgr=server;
using var session=new S101AccountSession(Api,jar=>new Handler(r=>{jar.Add(Api,new Cookie("sid","synthetic"));return Json(new{state=0,internalId=17,newCreate=false,createTime="2026-01-01T00:00:00Z",svrTime="2026-01-01T00:00:00Z"});}));
Check((await session.LoginAsync("101")).Success,"PF synthetic account");
typeof(PlayFabUserService).GetField("apiSession",BindingFlags.NonPublic|BindingFlags.Instance).SetValue(user,session);
typeof(PlayFabUserService).GetProperty("InternalId").SetValue(user,(uint)17);PlayFab.PlayFabClientAPI.Logged=true;
int progress=0;Check(await user.Login(v=>progress=v)==ELoginStep.Done && progress==(agg?3:2),"default PF authorized shortcut");
config.Set("SaveBackend","kv");Effects.Clear();Check(await user.Login(null)==ELoginStep.Api && Effects.Total==0,"authorized cannot bypass backend gate");
typeof(PlayFabUserService).GetField("apiSession",BindingFlags.NonPublic|BindingFlags.Instance).SetValue(user,null);
}
Pass("default-PF-and-AGG-shortcuts");
GContext.container.Values[typeof(ISettingService)]=new Setting();GContext.container.Values[typeof(IChatService)]=new Chat();
GContext.container.Values[typeof(PlayerData)]=new PlayerData();GContext.container.Values[typeof(MMTService)]=new MMTService();
asap.playfab.async.PlayFabClientAsyncAPI.Result=new LoginResult{PlayFabId="FF",NewlyCreated=false,
EntityToken=new EntityTokenResponse{Entity=new EntityKey()},InfoResultPayload=new GetPlayerCombinedInfoResultPayload{UserData=data,AccountInfo=new UserAccountInfo{Created=DateTime.UtcNow},PlayerProfile=new PlayerProfileModel{DisplayName="Synthetic",AvatarUrl="synthetic",Locations=new(){new LocationModel()}}}};
// Invalid scheme ensures legacy API stage fails before HttpClient; no real endpoint is contacted.
var normalConfig=Configuration();normalConfig.Set("EventApiURL","ftp://synthetic.invalid/");
var normal=new PlayFabUserService{config=normalConfig,CustomId="101",customServerMgr=new CustomServerMgr(normalConfig,_=>new Handler(r=>throw new Exception("no HTTP")))};
PlayFab.PlayFabClientAPI.Logged=false;Check(await normal.Login(null)==ELoginStep.Api,"normal PF reaches API boundary");
Check(((PlayerData)GContext.container.Values[typeof(PlayerData)]).Loads==1 && normal.MMTestForKVValidation==4,"real PF init after adjusted time");
var hotConfig=Configuration();hotConfig.Set("EventApiURL","ftp://synthetic.invalid/");
var hot=new AggUserService{config=hotConfig,customServerMgr=new CustomServerMgr(hotConfig,_=>new Handler(r=>throw new Exception("no HTTP")))};
var sdk=new TaskCompletionSource<LoginInfo>();TYSdkFacade.IsLoggedIn=false;TYSdkFacade.LoginOverride=()=>sdk.Task;PlayFab.PlayFabClientAPI.Logged=false;
var pending=hot.Login(null);hotConfig.Set("SaveBackend","kv");sdk.SetResult(new(){isSuccess=true,userId=101});
Check(await pending==ELoginStep.Api && hot.KVLastError==PlayerKVGameLoadError.None,"captured PF branch continues to API failure");
Check(((PlayerData)GContext.container.Values[typeof(PlayerData)]).Loads==2,"AGG real PF init window");TYSdkFacade.LoginOverride=null;
Pass("PF-original-init-order-and-backend-capture");
UnityEngine.GameObject.Found=new TapjoyUnity.Internal.TapjoyComponent();Effects.Clear();
typeof(PlayFabUserService).GetMethod("InitTapJoy",BindingFlags.Static|BindingFlags.NonPublic).Invoke(null,null);
#if UNITY_ANDROID
Check(Effects.Helpers==2,"PF Android creates and initializes helper");
#else
Check(Effects.Helpers==0,"PF non-Android no helper");
#endif
Pass("PF-original-TapJoy-platform-branch");
UniRx.Stream<EvtAPISvrUnauthorized>.Handlers=null;
var unauthorizedUser=new PlayFabUserService{config=normalConfig,CustomId="101",customServerMgr=new CustomServerMgr(normalConfig,_=>new Handler(r=>throw new Exception("no HTTP")))};
typeof(PlayFabUserService).GetProperty("InternalId").SetValue(unauthorizedUser,(uint)17);PlayFab.PlayFabClientAPI.Logged=true;
int version=unauthorizedUser.customServerMgr.SessionVersion;
GContext.Publish(new EvtAPISvrUnauthorized{SessionVersion=version-1});Check(unauthorizedUser.InternalId==17,"PF stale 401 ignored");
GContext.Publish(new EvtAPISvrUnauthorized{SessionVersion=version});
Check(unauthorizedUser.InternalId==0 && unauthorizedUser.customServerMgr.SessionVersion>version,"PF current 401 retains old reauthentication branch, invalid API prevents HTTP");
UniRx.Stream<EvtAPISvrUnauthorized>.Handlers=null;
Pass("PF-current-and-stale-401-branches");
Private(pf,"OnDestroy"); typeof(PlayFabMgr).GetField("_instance",BindingFlags.Static|BindingFlags.NonPublic).SetValue(null,null);
var fresh=new PlayFabMgr();Check(fresh.IsUnstartedForKVLoad,"fresh PF component");
Check(PlayerKVGameLoadGuard.TryEnter(out var guard),"guard entered");
using(guard)
{
Effects.Clear();
foreach(Action attempt in new Action[]{()=>fresh.CompareDatas(data,"x"),()=>fresh.GetLocalData("x"),()=>fresh.UpdateUserDataValue("x","y"),()=>fresh.UpdateUserDataValue("x","y",false)})
{try{attempt();throw new Exception("guard missed");}catch(PlayerKVLoadSideEffectException){assertions++;}}
Check(!await fresh.SyncDataImmidiate("x","y"),"immediate rejected");
Private(fresh,"SyncData");Private(fresh,"Awake");Private(fresh,"OnApplicationPause",true);Private(fresh,"OnApplicationPause",false);Private(fresh,"OnApplicationQuit");Private(fresh,"OnDestroy");Private(fresh,"UpdateUserDataValue",CancellationToken.None);
Check(Effects.Total==0 && fresh.IsUnstartedForKVLoad && guard.Violations==12,"all PF entrances reject before buffer/Prefs/SDK");
}
Pass("all-real-PF-guard-entrances");await Controlled();
}
sealed class Chat:IChatService {public void WaitingForLogin(){} }
sealed class Setting:ISettingService {public string Lang=>"en";}
static async Task Controlled()
{
int auth=0,get=0,borrow=0,unauthorizedEvents=0;Dictionary<string,string> raw=Raw();
Task<HttpResponseMessage> delayedBorrow=null;
var config=Configuration();var user=new AggUserService(PlayerKVControlledLoadCapability.CreateForValidation(_=>new Handler(r=>throw new Exception()))){config=config,CustomId="101"};
var server=new CustomServerMgr(config,_=>new Handler(r=>throw new Exception("PF transport forbidden")));
server.KVUnauthorized+=_=>unauthorizedEvents++;
var modules=Enumerable.Range(1,31).Select(id=>(IPlayerKVLoadModule)new PlayerKVLoadModule(id,()=>{if(id==31)user.ResetForKVLoad();},decoded=>{if(id==31)user.ReplaceFromKVDecoded(decoded.Module<PlayerKVLoginState>(31),decoded.Epoch,decoded.Raw);})).ToArray();
user.ConfigureKVForValidation(new PlayerKVGameLoadTransaction(modules,Environment.CurrentManagedThreadId,()=>user.KVIntents?.Discard()),new PlayFabMgr(),server,()=>true);
PlayerKVGameLoadCapability Capability()=>PlayerKVGameLoadCapability.CreateForValidation(Identity(),Snapshot(),Api,Path.Combine(Path.GetTempPath(),"ft-kv-service-probe",Guid.NewGuid().ToString("N")),
jar=>new Handler(r=>{auth++;jar.Add(Api,new Cookie("sid","synthetic"));return Json(new{state=0,internalId=17,newCreate=false,createTime="2026-01-01T00:00:00Z",svrTime="2026-01-01T00:00:00Z"});}),
jar=>new Handler(r=>{get++;return Json(new{data=raw});}),jar=>new Handler(r=>{borrow++;return delayedBorrow ?? Json(new{},HttpStatusCode.Unauthorized);}));
Effects.Clear();
foreach(var invalid in new[]{"0","999","15001","bad","1.5"})
{config.Set("KVLoginCloseObserveMs",invalid);Check((await user.LoadKVForValidationAsync(Capability())).Error==PlayerKVGameLoadError.Input,"invalid option rejected");Check(auth==0 && user.KVReceiverCountForValidation==0,"invalid no send/subscription leak");}
config.Values.Remove("KVLoginCloseObserveMs");
var result=await user.LoadKVForValidationAsync(Capability());
Check(result.State==PlayerKVGameLoadState.LoadedForValidation && user.KVLoadedForValidation,"real service controlled success: "+result.Error);
Check(auth==1 && get==1 && user.KVNotificationCount==1 && user.KVReceiverCountForValidation==0,"one auth/Get/windowed receiver");
Check(!user.IsLogin && !user.IsAuthorized && user.InternalId==0 && user.KVTapJoyBalance==9 && Effects.Total==0,"zero SDK/Prefs/reward/UI/helpers/events in KV");
var intents=user.KVIntents;await server.SendKVForValidationAsync("__kv_validation/unauthorized");await user.CloseKVForValidationAsync();
Check(!user.KVLoadedForValidation && user.KVIntents==null && intents.Discarded && borrow==1 && auth==1,"401 revokes without reauth");
raw.Clear();var failed=await user.LoadKVForValidationAsync(Capability());
Check(failed.Error==PlayerKVGameLoadError.Schema && user.KVReceiverCountForValidation==0 && user.KVNotificationCount==1,"failure removes receiver without notification");
Check(Effects.Total==0,"all failure paths zero effects");
raw=Raw();Check((await user.LoadKVForValidationAsync(Capability())).State==PlayerKVGameLoadState.LoadedForValidation,"new epoch after successful cleanup");
var late=new TaskCompletionSource<HttpResponseMessage>(TaskCreationOptions.RunContinuationsAsynchronously);delayedBorrow=late.Task;
var oldRequest=server.SendKVForValidationAsync("__kv_validation/unauthorized");user.Logout();Check(!user.KVLoadedForValidation,"logout synchronously revoked");
late.SetResult(new HttpResponseMessage(HttpStatusCode.Unauthorized){Content=new StringContent("{}")});
Check(!(await oldRequest).IsCurrent,"late 401 body discarded");await user.CloseKVForValidationAsync();
Check(unauthorizedEvents==1 && auth==3 && Effects.Total==0 && user.KVIntents==null,"late 401 never reauthenticates or publishes another unauthorized event");
Pass("real-Agg-User-CustomServer-coordinator-KV-chain");
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType><TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings><Nullable>disable</Nullable>
<DefineConstants>$(DefineConstants);UNITY_EDITOR;SERVICE_HOST</DefineConstants>
<DefineConstants Condition="'$(AndroidProbe)'=='true'">$(DefineConstants);UNITY_ANDROID;AGG</DefineConstants>
<DefineConstants Condition="'$(PlayerProbe)'=='true'">SERVICE_HOST</DefineConstants>
<NoWarn>$(NoWarn);CS0436;CS1998</NoWarn>
<RestoreConfigFile>$(MSBuildThisFileDirectory)../../MinimalLoginTests/NuGet.Config</RestoreConfigFile>
</PropertyGroup>
<ItemGroup>
<Compile Remove="FactoryProbe.cs" Condition="'$(PlayerProbe)'!='true'" />
<Compile Remove="Program.cs" Condition="'$(PlayerProbe)'=='true'" />
<Compile Include="../../../../Client/Assets/Scripts/Services/PlayerData/PlayerKV*.cs" Exclude="../../../../Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs" />
<Compile Include="../../../../Client/Assets/Scripts/Services/Account/S101AccountSession.cs" />
<Compile Include="../../../../Client/Assets/Scripts/Services/UserService.cs" />
<Compile Include="../../../../Client/Assets/Scripts/Services/UserService.KVLoad.cs" />
<Compile Include="../../../../Client/Assets/Scripts/Services/AggUserService.cs" />
<Compile Include="../../../../Client/Assets/Scripts/Core/PlayFabMgr.cs" />
<Compile Include="../../../../Client/Assets/Scripts/Core/CustomServerMgr.cs" />
<Reference Include="PlayFab"><HintPath>../../../../Client/Library/ScriptAssemblies/PlayFab.dll</HintPath></Reference>
<Reference Include="Newtonsoft.Json"><HintPath>../../../../Client/Library/PackageCache/com.unity.nuget.newtonsoft-json@3.2.1/Runtime/Newtonsoft.Json.dll</HintPath></Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,139 @@
// Only SDK/engine/business dependencies are replaced. Four service sources are linked unchanged.
using PlayFab.ClientModels;
using Newtonsoft.Json;
// Legacy desktop fallback names AndroidDeviceId on CustomID request (unreachable with validated CustomId).
// Keep this compatibility DTO local; do not silently change the pre-existing PF branch in production.
namespace PlayFab.ClientModels { public class LoginWithCustomIDRequest { public string CustomId,AndroidDeviceId;public bool CreateAccount;public GetPlayerCombinedInfoRequestParams InfoRequestParameters; } }
static class Effects
{
internal static int SDK,Prefs,Events,Subscriptions,Helpers,Rewards,UI,Timers,Account;
internal static void Clear() { SDK=Prefs=Events=Subscriptions=Helpers=Rewards=UI=Timers=Account=0; }
internal static int Total => SDK+Prefs+Events+Subscriptions+Helpers+Rewards+UI+Timers+Account;
}
namespace UnityEngine
{
public class Object { public static implicit operator bool(Object value)=>value!=null; public static void DontDestroyOnLoad(Object o){} }
public class GameObject:Object
{
public static object Found;
public static T FindObjectOfType<T>() where T:class =>Found as T;
public T AddComponent<T>() where T:new(){Effects.Helpers++;return new T();}
}
public class MonoBehaviour:Object { public GameObject gameObject=new(); public T GetComponent<T>() where T:class=>null; }
public static class Debug { public static void Log(object o){} public static void LogWarning(object o){} public static void LogError(object o){} }
public static class Application { public static string version="synthetic",identifier="101",persistentDataPath=Path.GetTempPath(); }
public static class SystemInfo { public static string deviceUniqueIdentifier="101",deviceModel="stub"; }
public static class PlayerPrefs
{
public static readonly Dictionary<string,string> Data=new();
public static string GetString(string key,string fallback=""){Effects.Prefs++;return Data.GetValueOrDefault(key,fallback);}
public static void SetString(string key,string value){Effects.Prefs++;Data[key]=value;}
public static void Save(){Effects.Prefs++;}
}
}
namespace asap.core
{
public class InjectAttribute:Attribute{}
public interface IConfig { T Get<T>(string key,T fallback); void Set(string key,object value); }
public static class Awaiters { public static Task NextFrame=>Task.CompletedTask; }
}
sealed class Config:asap.core.IConfig
{
internal readonly Dictionary<string,object> Values=new();
public T Get<T>(string key,T fallback)=>Values.TryGetValue(key,out var v)?(T)v:fallback;
public void Set(string key,object value)=>Values[key]=value;
}
namespace UniRx
{
public sealed class Stream<T>
{ internal static Action<T> Handlers; public IDisposable Subscribe(Action<T> action){Effects.Subscriptions++;Handlers+=action;return new Subscription(()=>Handlers-=action);} }
public sealed class Subscription:IDisposable { readonly Action action; public Subscription(Action action)=>this.action=action;public void Dispose()=>action(); }
}
public static class GContext
{
public static readonly Container container=new();
public static UniRx.Stream<T> OnEvent<T>()=>new();
public static void Publish<T>(T value){Effects.Events++;UniRx.Stream<T>.Handlers?.Invoke(value);}
}
public sealed class Container
{ public readonly Dictionary<Type,object> Values=new();public T Resolve<T>()=> (T)Values[typeof(T)]; }
public static class GameDebug { public static void LogError(string s,params object[] a){} }
public static class ZZTimeHelper { public static DateTime UtcNow()=>new(2026,9,23,0,0,0,DateTimeKind.Utc); public static bool Adjusted; public static void AdjustTime(DateTime t)=>Adjusted=true;public static void WathForcusChange(){} }
public static class GConstant { public const string K_Event_API_URL="EventApiURL",K_AssetVersion="AssetVersion",Yidun_GooglePlayChannel="stub",Yidun_AndroidBusinessId="stub",K_PlayFab_Title="PFTitle",V_Debug_PF_Title="stub",K_PlayFab_TimeOut="PFTimeout"; }
namespace LitJson { public static class JsonMapper { public static string ToJson(object o)=>JsonConvert.SerializeObject(o);public static T ToObject<T>(string s)=>JsonConvert.DeserializeObject<T>(s); } }
namespace GameCore { }
namespace YiDun.Protector { public class YiDunManager { } }
namespace PlayFab
{
public static class PlayFabClientAPI
{
public static bool Logged;
public static bool IsClientLoggedIn(){Effects.SDK++;return Logged;}
public static void ForgetAllCredentials(){Effects.SDK++;Logged=false;}
public static void UpdateUserData(UpdateUserDataRequest r,Action<UpdateUserDataResult> ok,Action<PlayFabError> fail){Effects.SDK++;ok?.Invoke(new());}
}
public static class PlayFabSettings { public static string TitleId;public static int RequestTimeout; }
}
namespace PlayFab.Internal { public static class PlayFabWebRequest { public static void SkipCertificateValidation(){Effects.SDK++;} } }
namespace asap.playfab.async
{
public static class PlayFabClientAsyncAPI
{
internal static LoginResult Result;
internal static Func<Task<LoginResult>> LoginOverride;
public static Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> call)=>call();
public static Task<LoginResult> LoginWithCustomIDAsync(LoginWithCustomIDRequest r){Effects.SDK++;PlayFab.PlayFabClientAPI.Logged=Result!=null;return LoginOverride?.Invoke() ?? Task.FromResult(Result);}
public static Task<LoginResult> LoginWithAndroidDeviceIDAsync(LoginWithAndroidDeviceIDRequest r)=>LoginWithCustomIDAsync(new());
public static Task<GetPlayerProfileResult> GetPlayerProfileAsync(GetPlayerProfileRequest r){Effects.SDK++;return Task.FromResult(new GetPlayerProfileResult{PlayerProfile=new PlayerProfileModel{Locations=new(){new LocationModel()}}});}
public static Task<UpdateUserTitleDisplayNameResult> UpdateUserTitleDisplayNameAsync(UpdateUserTitleDisplayNameRequest r){Effects.SDK++;return Task.FromResult(new UpdateUserTitleDisplayNameResult{DisplayName=r.DisplayName});}
public static Task<EmptyResponse> UpdateAvatarUrlAsync(UpdateAvatarUrlRequest r){Effects.SDK++;return Task.FromResult(new EmptyResponse());}
public static Task<GetUserDataResult> GetUserDataAsync(GetUserDataRequest r){Effects.SDK++;return Task.FromResult(new GetUserDataResult{Data=new()});}
}
public static class PlayFabProfilesAsyncAPI
{ public static Task<PlayFab.ProfilesModels.SetProfileLanguageResponse> SetProfileLanguageAsync(PlayFab.ProfilesModels.SetProfileLanguageRequest r){Effects.SDK++;return Task.FromResult(new PlayFab.ProfilesModels.SetProfileLanguageResponse());} }
}
namespace tysdk
{
public enum EAccoutType {hwGoogle,hwFacebook,hwGuest,hwVkid,Apple,none}
public class LoginInfo { public bool isSuccess; public int userId;public string StrUserId=>userId.ToString(); }
public sealed class TYSdkFacade
{
public sealed class AccountInfo {public EAccoutType channel;public string userName,avatar;public int userId;public List<EAccoutType> linkedAccout=new();}
public static AccountInfo TYAccountInfo=new(){userId=101,channel=EAccoutType.hwGuest};
public static bool IsLoggedIn;
public static TYSdkFacade Instance=new();
internal static Func<Task<LoginInfo>> LoginOverride;
public Task<LoginInfo> LoginBySns(){Effects.SDK++;return LoginOverride?.Invoke() ?? Task.FromResult(new LoginInfo{isSuccess=true,userId=101});}
public Task<LoginInfo> LoginByToken()=>LoginBySns();public Task<LoginInfo> Login(EAccoutType type)=>LoginBySns();
public void Logout(){Effects.SDK++;IsLoggedIn=false;}
public Task<bool> LinkCheck(EAccoutType t){Effects.SDK++;return Task.FromResult(false);}
public Task<(int code,int userId)> LinkAccount(EAccoutType t){Effects.SDK++;return Task.FromResult((1,0));}
}
}
public static class ChannelManager { public static bool IsRustoreStore=>false; }
public class AFHelper { public static AFHelper Instance=new();public bool GetDLReferer(out string pid,out string uid){pid=uid=null;return false;} }
public static class GEvent
{
public sealed class Event:IDisposable { public Event AddContent(string k,object v)=>this;public void Dispose(){} }
public static Event GameEvent(string name){Effects.Events++;return new();}
public static Event LoginEvent()=>GameEvent("login");public static Event CompleteRegistrationEvent()=>GameEvent("registration");
public static void SetProp(string k,object v){Effects.Events++;}public static void SetUserProp(string k,object v,bool b){Effects.Events++;}
public static void SetProps(Dictionary<string,object> p){Effects.Events++;}public static void SetUserProps(Dictionary<string,object> p){Effects.Events++;}
public static void OnLogin(string a,string b){Effects.Events++;}
}
namespace game
{
public interface IChatService { void WaitingForLogin(); }
public interface ISettingService { string Lang {get;} }
public class MMTService { public string Init(int value){Effects.Events++;return value.ToString();} }
public class PlayerData { public int lv=1;public int Loads;public void LoadPlayerData(IDictionary<string,string> data){if(!ZZTimeHelper.Adjusted)throw new Exception("PF time not adjusted");Loads++;} }
public class ChangeAvatarEvent{}
public class GetClubPlayInfoEvent { public string id,name,avatarUrl; }
public class ItemData {public int id,count;public ulong curCount;}
public enum RewardType {NormalOne}
public class ShowData {public ShowData(){}public ShowData(List<ItemData> items,RewardType t){Effects.UI++;}}
public class PlayerItemData {public int GetItemCount(int id)=>0;public void AddItem(ItemData i,object o){Effects.Rewards++;}}
public class TapjoyHelper:UnityEngine.MonoBehaviour {public void Init(int balance,Action<string,int> earn,Action<string,int> changed){Effects.Helpers++;}}
}
namespace TapjoyUnity.Internal { public class TapjoyComponent:UnityEngine.MonoBehaviour{} }