feat(upserver): 保存M2客户端KV迁移至本人统计写检查点
保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
This commit is contained in:
@@ -0,0 +1,914 @@
|
||||
using System.Threading.Tasks;
|
||||
using game.Account;
|
||||
using asap.core;
|
||||
using PlayFab;
|
||||
using asap.playfab.async;
|
||||
using PlayFab.ClientModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
#if UNITY_ANDROID
|
||||
using TapjoyUnity.Internal;
|
||||
#endif
|
||||
|
||||
using tysdk;
|
||||
using UniRx;
|
||||
using YiDun.Protector;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public enum ELoginStep
|
||||
{
|
||||
PlayFab,
|
||||
Sdk,
|
||||
Api,
|
||||
Done
|
||||
}
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string playerId { get; set; }
|
||||
public PlayerProfile playerProfile { get; set; }
|
||||
}
|
||||
|
||||
public class PlayerProfile
|
||||
{
|
||||
public int Level { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
}
|
||||
|
||||
public class EvtAPISvrLoginSuccess
|
||||
{
|
||||
public string playerId { get; set; }
|
||||
public uint internalId { get; set; }
|
||||
}
|
||||
|
||||
public class EvtAPISvrUnauthorized { public int SessionVersion { get; set; } }
|
||||
|
||||
public interface IUserService
|
||||
{
|
||||
Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true);
|
||||
void Logout();
|
||||
System.Threading.CancellationToken LoginLifetime { get; }
|
||||
bool IsLogin { get; }
|
||||
bool IsAuthorized { get; }
|
||||
DateTime LastLoginTime { get; }
|
||||
DateTime LoginTime { get; }
|
||||
DateTime CreateTime { get; }
|
||||
Task<string> ChangeDisplayName(string newName);
|
||||
string DisplayName { get; }
|
||||
string AvatarUrl { get; }
|
||||
Dictionary<string, string> EAccoutUrl { get; }
|
||||
Task<string> UpdateAvatarUrl(string url);
|
||||
string UserId { get; }
|
||||
string ApiPlayerId { get; }
|
||||
string CustomId { get; set; }
|
||||
uint InternalId { get; }
|
||||
string ContinentCode { get; }
|
||||
//string CountryCode { get; }
|
||||
double CreateTotalSeconds();
|
||||
string Language { get; }
|
||||
Task<string> UpdateLanguage(string code);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Link sns account
|
||||
/// success return ( code 0, current userid)
|
||||
/// exists return ( code -1, exists account user id)
|
||||
/// fail return ( code 1, 0 )
|
||||
/// </summary>
|
||||
/// <param name="accoutType"> sns type [hwGoogle, hwFacebook, hwGuest, Apple]</param>
|
||||
/// <returns>code and userid</returns>
|
||||
Task<(int code, int userid)> LinkAccount(EAccoutType accoutType);
|
||||
Task<bool> GetLinkState(EAccoutType accoutType);
|
||||
void SetPlayerLv(string playFabId, int lv);
|
||||
Task<(string, int)> GetPlayLv(string playFabId);
|
||||
int GetPlayerLv(string playFabId);
|
||||
|
||||
PlayInfo GetPlayInfo(string id);
|
||||
void ClearPlayInfos();
|
||||
void SetPlayInfo(string playFabId, string name, string avatarUrl);
|
||||
string GetDefaultName(string id);
|
||||
void UpdateProfile();
|
||||
void UpdateProfileLv();
|
||||
}
|
||||
|
||||
public partial class PlayFabUserService : IUserService, PlayerDataStorage.IKVUserLoadReceiver, PlayerDataStorage.IKVUserLifecycle
|
||||
{
|
||||
[Inject]
|
||||
public IConfig config { get; set; }
|
||||
|
||||
[Inject]
|
||||
public ICustomServerMgr customServerMgr { get; set; }
|
||||
|
||||
public bool IsLogin => !kvControlledInstance && PlayFabClientAPI.IsClientLoggedIn() && IsAuthorized;
|
||||
|
||||
public bool IsAuthorized => InternalId != 0 && apiSession != null && apiSession.IsAuthenticated;
|
||||
|
||||
// UserId remains the legacy storage identity until the KV migration.
|
||||
public string ApiPlayerId => IsAuthorized ? apiSession.PlayerId : null;
|
||||
|
||||
public DateTime LastLoginTime { get; protected set; }
|
||||
|
||||
public DateTime LoginTime { get; protected set; }
|
||||
|
||||
public DateTime CreateTime { get; protected set; }
|
||||
|
||||
public string DisplayName { get; protected set; }
|
||||
|
||||
public string AvatarUrl { get; protected set; }
|
||||
public Dictionary<string, string> EAccoutUrl { get; protected set; }
|
||||
public string Language { get; protected set; }
|
||||
|
||||
public string UserId { get; protected set; }
|
||||
public EntityKey entityKey { get; protected set; }
|
||||
public string CustomId { get; set; }
|
||||
public uint InternalId { get; protected set; }
|
||||
public string ContinentCode { get; protected set; }
|
||||
public string CountryCode { get; protected set; }
|
||||
protected virtual string NewPlayerName => $"Angler#{CustomId}";
|
||||
|
||||
protected Action<IDictionary<string, string>> OnInitData;
|
||||
|
||||
protected virtual string NewPlayerAvatar => string.Empty;
|
||||
private bool IsApiServerLogin => InternalId != 0;
|
||||
int MMTestValue;//0=>A 原来的 对照组 1=>B 新的实验组
|
||||
const string MMTestKey = "MMTest";
|
||||
const string MMTestSubKey = "MMFailParam";
|
||||
public string GetDefaultName(string id)
|
||||
{
|
||||
return "Angler#" + (string.IsNullOrEmpty(id) ? string.Empty : id.Substring(0, Math.Min(5, id.Length)));
|
||||
}
|
||||
|
||||
public PlayFabUserService() : this(null) { }
|
||||
|
||||
internal PlayFabUserService(PlayerDataStorage.PlayerKVControlledLoadCapability capability)
|
||||
{
|
||||
kvControlledInstance = capability != null;
|
||||
if (kvControlledInstance) return;
|
||||
GContext.OnEvent<EvtAPISvrUnauthorized>().Subscribe(evt =>
|
||||
{
|
||||
if (evt.SessionVersion != customServerMgr.SessionVersion) return;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) { game.PlayerDataStorage.PlayerKVGameplayLoop.Fault("API unauthorized; restart required."); return; }
|
||||
#endif
|
||||
InternalId = 0;
|
||||
if (PlayFabClientAPI.IsClientLoggedIn()) _ = LoginApiServer();
|
||||
});
|
||||
}
|
||||
|
||||
protected int logoutGeneration;
|
||||
private System.Threading.CancellationTokenSource loginLifetime = new System.Threading.CancellationTokenSource();
|
||||
public System.Threading.CancellationToken LoginLifetime => loginLifetime.Token;
|
||||
private readonly System.Threading.SemaphoreSlim loginGate = new System.Threading.SemaphoreSlim(1, 1);
|
||||
public virtual Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true) => LoginWithBackend(progress,CaptureSaveBackend());
|
||||
|
||||
protected async Task<ELoginStep> LoginWithBackend(Action<int> progress,string backend)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var requestedId = CustomId;
|
||||
if(RejectSaveBackend(backend)) return ELoginStep.Api;
|
||||
await loginGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (generation != logoutGeneration || requestedId != CustomId) return ELoginStep.Api;
|
||||
if (!long.TryParse(requestedId, System.Globalization.NumberStyles.None,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out var numericId) || numericId <= 0)
|
||||
{
|
||||
return ELoginStep.Api;
|
||||
}
|
||||
return await LoginCore(progress, generation);
|
||||
}
|
||||
finally { loginGate.Release(); }
|
||||
}
|
||||
|
||||
private async Task<ELoginStep> LoginCore(Action<int> progress, int generation)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) return await LoginKVGameplayLoopAsync(progress,generation);
|
||||
#endif
|
||||
if (IsLogin)
|
||||
{
|
||||
progress?.Invoke(2);
|
||||
return ELoginStep.Done;
|
||||
}
|
||||
|
||||
if (!PlayFabClientAPI.IsClientLoggedIn())
|
||||
{
|
||||
if (!await LoginPlayFab(generation))
|
||||
return ELoginStep.PlayFab;
|
||||
}
|
||||
|
||||
if (generation != logoutGeneration) return ELoginStep.Api;
|
||||
|
||||
SetMMTestValue();
|
||||
|
||||
GContext.container.Resolve<IChatService>().WaitingForLogin();
|
||||
|
||||
OnYinDunLogin();
|
||||
if (!await LoginApiServer() || generation != logoutGeneration)
|
||||
return ELoginStep.Api;
|
||||
|
||||
InitTapJoy();
|
||||
progress?.Invoke(2);
|
||||
return ELoginStep.Done;
|
||||
}
|
||||
|
||||
private static void InitTapJoy()
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
var lastBalanceStr = PlayFabMgr.Instance.GetLocalData("TapJoy_Diamond") ?? "";
|
||||
Debug.Log($"InitTapJoy -> {lastBalanceStr}");
|
||||
var lastBalance = int.TryParse(lastBalanceStr, out var temp) ? temp : 0;
|
||||
|
||||
var tapjoyObject = GameObject.FindObjectOfType<TapjoyComponent>();
|
||||
if (tapjoyObject && tapjoyObject.gameObject && !tapjoyObject.GetComponent<TapjoyHelper>())
|
||||
{
|
||||
var tapjoyHelper = tapjoyObject.gameObject.AddComponent<TapjoyHelper>();
|
||||
GameObject.DontDestroyOnLoad(tapjoyHelper);
|
||||
tapjoyHelper.Init(lastBalance, OnEarnCurrencyName, OnBalance);
|
||||
}
|
||||
return;
|
||||
void OnEarnCurrencyName(string currencyName, int amount)
|
||||
{
|
||||
Debug.Log($"OnEarnCurrencyName -> {currencyName} {amount}");
|
||||
// var amount = (balance - lastBalance);
|
||||
var diamonds = new ItemData { id = 1005, count = amount };
|
||||
diamonds.curCount = (ulong)GContext.container.Resolve<PlayerItemData>().GetItemCount(diamonds.id);
|
||||
GContext.container.Resolve<PlayerItemData>().AddItem(diamonds, null);
|
||||
GContext.Publish(new ShowData(new List<ItemData> { diamonds }, RewardType.NormalOne));
|
||||
GContext.Publish(new ShowData());
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("tapjoy"))
|
||||
{
|
||||
e.AddContent("item_id", diamonds.id)
|
||||
.AddContent("item_count", diamonds.count);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
void OnBalance(string currencyName, int balance)
|
||||
{
|
||||
Debug.Log($"OnBalance -> {currencyName} {balance}");
|
||||
// const string tapjoyKey = "TapJoy_Diamond";
|
||||
// var lastBalanceStr = PlayFabMgr.Instance.GetLocalData(tapjoyKey) ?? "";
|
||||
// var lastBalance = int.TryParse(lastBalanceStr, out var temp) ? temp : 0;
|
||||
{
|
||||
// PlayFabMgr.Instance.UpdateUserDataValue(tapjoyKey, balance.ToString());
|
||||
// if (balance > lastBalance)
|
||||
// {
|
||||
// var amount = (balance - lastBalance);
|
||||
// var diamonds = new ItemData { id = 1005, count = amount };
|
||||
// diamonds.curCount = (ulong)GContext.container.Resolve<PlayerItemData>().GetItemCount(diamonds.id);
|
||||
// GContext.container.Resolve<PlayerItemData>().AddItem(diamonds);
|
||||
// GContext.Publish(new ShowData(new List<ItemData> { diamonds }, RewardType.NormalOne));
|
||||
// // GContext.Publish(new ShowData(diamonds));
|
||||
// GContext.Publish(new ShowData());
|
||||
// }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
protected async void OnYinDunLogin()
|
||||
{
|
||||
// 开发模式下,先不启用了
|
||||
if (config != null && (config.Get("DEBUG", "0") == "1" || config.Get("GM", "0") == "1"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userService = GContext.container.Resolve<IUserService>();
|
||||
var yiDunManager = GContext.container.Resolve<YiDunManager>();
|
||||
var roleName = userService.DisplayName;
|
||||
var roleId = userService.UserId;
|
||||
var roleAccount = userService.CustomId;
|
||||
var versionData = new
|
||||
{
|
||||
GameVersion = Application.version,
|
||||
AssetVersion = config.Get(GConstant.K_AssetVersion, string.Empty),
|
||||
#if UNITY_ANDROID
|
||||
Channel = GConstant.Yidun_GooglePlayChannel
|
||||
#elif UNITY_IOS
|
||||
Channel = GConstant.Yidun_IOSChannel
|
||||
#endif
|
||||
};
|
||||
Debug.Log($"JsonUtility.ToJson(versionData) => {JsonConvert.SerializeObject(versionData)}");
|
||||
|
||||
var businessId = GConstant.Yidun_AndroidBusinessId;
|
||||
#if UNITY_IOS
|
||||
businessId = GConstant.Yidun_IOSBusinessId;
|
||||
#endif
|
||||
|
||||
#if (UNITY_ANDROID || UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR
|
||||
yiDunManager.SetRoleInfo(businessId, roleId, roleName, roleAccount, null, -1, JsonConvert.SerializeObject(versionData));
|
||||
yiDunManager.PostRiskCheck();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetMMTestValue()
|
||||
{
|
||||
if (MMTestValue == -1)
|
||||
{
|
||||
//int id = (int)(InternalId / 2) * 2 + 1;
|
||||
//int AB = (int)(id % 8);
|
||||
// convert UserId string to ulong
|
||||
var ulongUserId = ulong.Parse(UserId, System.Globalization.NumberStyles.HexNumber);
|
||||
int AB = (int)(ulongUserId % 16);
|
||||
MMTestValue = AB;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
string ab = GContext.container.Resolve<MMTService>().Init(MMTestValue);
|
||||
GEvent.SetUserProp("MMTestValue", ab, true);
|
||||
Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(MMTestValue, 2)} MMTestValueString:{ab}");
|
||||
}
|
||||
public void GMMMT(int value)
|
||||
{
|
||||
MMTestValue = value;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
string ab = GContext.container.Resolve<MMTService>().Init(MMTestValue);
|
||||
GEvent.SetUserProp("MMTestValue", ab, true);
|
||||
Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(MMTestValue, 2)} MMTestValueString:{ab}");
|
||||
}
|
||||
private async Task<bool> LoginPlayFab(int generation)
|
||||
{
|
||||
PlayFabSettings.TitleId = config.Get<string>(GConstant.K_PlayFab_Title, GConstant.V_Debug_PF_Title);
|
||||
PlayFab.Internal.PlayFabWebRequest.SkipCertificateValidation();
|
||||
|
||||
var timeout = config.Get<int>(GConstant.K_PlayFab_TimeOut, 0);
|
||||
if (timeout != 0)
|
||||
{
|
||||
PlayFabSettings.RequestTimeout = timeout;
|
||||
}
|
||||
|
||||
var sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var info_request_parameters = new GetPlayerCombinedInfoRequestParams
|
||||
{
|
||||
GetUserAccountInfo = true,
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
GetUserData = !game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled,
|
||||
#else
|
||||
GetUserData = true,
|
||||
#endif
|
||||
GetPlayerProfile = true,
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
ShowAvatarUrl = true,
|
||||
ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
}
|
||||
};
|
||||
|
||||
LoginResult loginResult = null;
|
||||
|
||||
|
||||
if (CustomId != string.Empty)
|
||||
{
|
||||
var request = new LoginWithCustomIDRequest
|
||||
{
|
||||
CustomId = CustomId,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
Debug.Log($"[UserService::LoginPlayFab] LoginWithCustomIDAsync customId:{CustomId}");
|
||||
loginResult = await PlayFabClientAsyncAPI.ExecuteWithRetryAsync(
|
||||
() => PlayFabClientAsyncAPI.LoginWithCustomIDAsync(request));
|
||||
Debug.Log($"[UserService::LoginPlayFab] LoginWithCustomIDAsync result:{loginResult != null}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if UNITY_IOS
|
||||
CustomId = UnityEngine.iOS.Device.advertisingIdentifier;
|
||||
var request = new LoginWithIOSDeviceIDRequest
|
||||
{
|
||||
DeviceId = UnityEngine.iOS.Device.advertisingIdentifier,
|
||||
OS = "iOS",
|
||||
DeviceModel = UnityEngine.iOS.Device.generation.ToString(),
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
|
||||
loginResult = await PlayFabClientAsyncAPI.LoginWithIOSDeviceIDAsync(request);
|
||||
|
||||
#elif UNITY_ANDROID
|
||||
|
||||
CustomId = SystemInfo.deviceUniqueIdentifier;
|
||||
var request = new LoginWithAndroidDeviceIDRequest
|
||||
{
|
||||
AndroidDeviceId = SystemInfo.deviceUniqueIdentifier,
|
||||
AndroidDevice = SystemInfo.deviceModel,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
|
||||
loginResult = await PlayFabClientAsyncAPI.LoginWithAndroidDeviceIDAsync(request);
|
||||
|
||||
#else
|
||||
CustomId = Application.identifier;
|
||||
var request = new LoginWithCustomIDRequest
|
||||
{
|
||||
AndroidDeviceId = Application.identifier,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
loginResult = await PlayFabClientAsyncAPI.ExecuteWithRetryAsync(
|
||||
() => PlayFabClientAsyncAPI.LoginWithCustomIDAsync(request));
|
||||
#endif
|
||||
}
|
||||
sw.Stop();
|
||||
if (generation != logoutGeneration)
|
||||
{
|
||||
PlayFabClientAPI.ForgetAllCredentials();
|
||||
return false;
|
||||
}
|
||||
if (loginResult == null) return false;
|
||||
|
||||
Debug.Log($"LoginPlayFab elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
|
||||
|
||||
entityKey = loginResult.EntityToken.Entity;
|
||||
if (!loginResult.NewlyCreated)
|
||||
{
|
||||
DisplayName = loginResult.InfoResultPayload.PlayerProfile?.DisplayName ?? string.Empty;
|
||||
AvatarUrl = loginResult.InfoResultPayload.PlayerProfile?.AvatarUrl ?? string.Empty;
|
||||
ContinentCode = loginResult.InfoResultPayload.PlayerProfile?.Locations[0]?.ContinentCode?.ToString() ?? "UNKNOW";
|
||||
CountryCode = loginResult.InfoResultPayload.PlayerProfile?.Locations[0]?.CountryCode?.ToString();
|
||||
|
||||
config.Set("NewlyCreated", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
sw.Start();
|
||||
|
||||
GetPlayerProfileRequest getPlayerProfileRequest = new GetPlayerProfileRequest()
|
||||
{
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
//ShowAvatarUrl = true,
|
||||
//ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
}
|
||||
};
|
||||
var data = await PlayFabClientAsyncAPI.GetPlayerProfileAsync(getPlayerProfileRequest);
|
||||
if (generation != logoutGeneration) return false;
|
||||
//DisplayName = data.PlayerProfile.DisplayName;
|
||||
//AvatarUrl = data.PlayerProfile.AvatarUrl;
|
||||
ContinentCode = data?.PlayerProfile.Locations[0]?.ContinentCode?.ToString() ?? "UNKNOW";
|
||||
CountryCode = data?.PlayerProfile.Locations[0]?.CountryCode?.ToString();
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab GetPlayerProfile elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
|
||||
config.Set("NewlyCreated", true);
|
||||
}
|
||||
|
||||
UserId = loginResult.PlayFabId;
|
||||
if (string.IsNullOrEmpty(DisplayName))
|
||||
{
|
||||
sw.Start();
|
||||
var newDisplayName = await ChangeDisplayName(NewPlayerName);
|
||||
if (generation != logoutGeneration) return false;
|
||||
DisplayName = newDisplayName;
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab ChangeDisplayName elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(AvatarUrl))
|
||||
{
|
||||
sw.Start();
|
||||
/*AvatarUrl = */
|
||||
await UpdateAvatarUrl(NewPlayerAvatar);
|
||||
if (generation != logoutGeneration) return false;
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab update avatar elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
}
|
||||
//BuglyAgent.SetUserId(UserId);
|
||||
var setting = GContext.container.Resolve<ISettingService>();
|
||||
await UpdateLanguage(setting.Lang);
|
||||
if (generation != logoutGeneration) return false;
|
||||
|
||||
var playfabUserData = loginResult.InfoResultPayload?.UserData;
|
||||
|
||||
CreateTime = loginResult.InfoResultPayload.AccountInfo.Created.ToUniversalTime();
|
||||
var tokenExpiration = loginResult.EntityToken.TokenExpiration?.ToUniversalTime();
|
||||
if (tokenExpiration.HasValue)
|
||||
{
|
||||
LoginTime = tokenExpiration.Value.ToUniversalTime() - TimeSpan.FromDays(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoginTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if (loginResult.LastLoginTime.HasValue)
|
||||
LastLoginTime = loginResult.LastLoginTime.Value.ToUniversalTime();
|
||||
|
||||
ZZTimeHelper.AdjustTime(LoginTime);
|
||||
ZZTimeHelper.WathForcusChange();
|
||||
|
||||
//InitData should invoke after local time adjusted.
|
||||
//save login time to local storage.
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(!game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled)
|
||||
#endif
|
||||
InitData(playfabUserData, UserId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private S101AccountSession apiSession;
|
||||
private Task<bool> apiLoginTask;
|
||||
private int apiLoginGeneration;
|
||||
|
||||
private Task<bool> LoginApiServer()
|
||||
{
|
||||
if (apiLoginTask != null && !apiLoginTask.IsCompleted)
|
||||
return apiLoginTask;
|
||||
apiLoginTask = LoginApiServerCore();
|
||||
return apiLoginTask;
|
||||
}
|
||||
|
||||
private async Task<bool> LoginApiServerCore()
|
||||
{
|
||||
var generation = ++apiLoginGeneration;
|
||||
var requestedId = CustomId;
|
||||
InternalId = 0;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
try
|
||||
{
|
||||
customServerMgr.ClearAccountSession();
|
||||
var session = new S101AccountSession(customServerMgr.ApiBaseUri);
|
||||
apiSession = session;
|
||||
var result = await session.LoginAsync(requestedId);
|
||||
if (generation != apiLoginGeneration || requestedId != CustomId)
|
||||
return false;
|
||||
if (!result.Success)
|
||||
{
|
||||
Debug.LogWarning($"API login failed: {result.Error}, HTTP {result.StatusCode}");
|
||||
return false;
|
||||
}
|
||||
customServerMgr.UseAccountSession(session);
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) return true; // Identity is published only after all KV modules load.
|
||||
#endif
|
||||
InternalId = session.InternalId;
|
||||
GContext.Publish(new EvtAPISvrLoginSuccess
|
||||
{
|
||||
playerId = session.PlayerId,
|
||||
internalId = InternalId
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (generation == apiLoginGeneration)
|
||||
{
|
||||
InternalId = 0;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
customServerMgr.ClearAccountSession();
|
||||
}
|
||||
Debug.LogError($"API login failed: {e.GetType().Name}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async virtual Task<(int code, int userid)> LinkAccount(EAccoutType accoutType)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
//测试
|
||||
//#if UNITY_EDITOR
|
||||
// var _accountInfo = new TYSdkFacade.AccountInfo()
|
||||
// {
|
||||
// channel = accoutType,
|
||||
// avatar = "https://tse2-mm.cn.bing.net/th/id/OIP-C.s4x0YU7qC-oat3mnyubG5gAAAA?rs=1&pid=ImgDetMain",
|
||||
// };
|
||||
// AvatarToPF(_accountInfo);
|
||||
|
||||
// if (_accountInfo.channel == EAccoutType.hwFacebook)
|
||||
// {
|
||||
// _ = UpdateAvatarUrl(_accountInfo.avatar);
|
||||
// }
|
||||
//#endif
|
||||
return (1,0);
|
||||
}
|
||||
|
||||
protected void AvatarToPF(TYSdkFacade.AccountInfo accountInfo)
|
||||
{
|
||||
if (accountInfo != null
|
||||
&& (accountInfo.channel == EAccoutType.hwFacebook
|
||||
|| accountInfo.channel == EAccoutType.hwGoogle)
|
||||
&& !string.IsNullOrEmpty(accountInfo.avatar))
|
||||
{
|
||||
if (EAccoutUrl == null)
|
||||
{
|
||||
EAccoutUrl = new Dictionary<string, string>();
|
||||
}
|
||||
EAccoutUrl[accountInfo.channel.ToString()] = accountInfo.avatar;
|
||||
string EAccoutUrlStr = Newtonsoft.Json.JsonConvert.SerializeObject(EAccoutUrl);
|
||||
PlayFabMgr.Instance.UpdateUserDataValue("EAccoutUrl", EAccoutUrlStr);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitData(Dictionary<string, UserDataRecord> datas, string userId)
|
||||
{
|
||||
var comparedData = PlayFabMgr.Instance.CompareDatas(datas, userId);
|
||||
if (comparedData != null)
|
||||
{
|
||||
MMTestValue = 0;
|
||||
if (!comparedData.ContainsKey(MMTestKey))
|
||||
{
|
||||
if (config.Get<bool>("NewlyCreated", false))
|
||||
{
|
||||
//新用户设置MMTest
|
||||
MMTestValue = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//老用户设置MMTest
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string mmTestValue = comparedData[MMTestKey];
|
||||
string[] mmTestValueData = mmTestValue.Split('#');
|
||||
//if (mmTestValueData.Length > 1 && mmTestValueData[0] == MMTestSubOldKey)
|
||||
//{
|
||||
// int.TryParse(mmTestValueData[1], out int value);
|
||||
// MMTestValue = value & 0b101;
|
||||
// //升级老用户数据
|
||||
// PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
//}
|
||||
//else
|
||||
if (mmTestValueData.Length > 1 && mmTestValueData[0] == MMTestSubKey)
|
||||
{
|
||||
|
||||
int.TryParse(mmTestValueData[1], out int value);
|
||||
MMTestValue = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
MMTestValue = 0;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
}
|
||||
GContext.container.Resolve<PlayerData>().LoadPlayerData(comparedData);
|
||||
OnInitData?.Invoke(comparedData);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Logout()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) { _=StopKVGameplayLoopAsync(); return; }
|
||||
#endif
|
||||
if(kvControlledInstance)
|
||||
{
|
||||
logoutGeneration++; InternalId=0; UserId=null; DisplayName=null; AvatarUrl=null;
|
||||
_=kvCoordinator?.Revoke();
|
||||
var previous=loginLifetime; loginLifetime=new System.Threading.CancellationTokenSource();
|
||||
try { previous.Cancel(); } finally { previous.Dispose(); }
|
||||
return;
|
||||
}
|
||||
|
||||
logoutGeneration++;
|
||||
apiLoginGeneration++;
|
||||
apiLoginTask = null;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
UserId = null;
|
||||
InternalId = 0;
|
||||
DisplayName = null;
|
||||
AvatarUrl = null;
|
||||
PlayFabClientAPI.ForgetAllCredentials();
|
||||
customServerMgr.ClearAccountSession();
|
||||
var previousLifetime = loginLifetime;
|
||||
loginLifetime = new System.Threading.CancellationTokenSource();
|
||||
try { previousLifetime.Cancel(); }
|
||||
finally { previousLifetime.Dispose(); }
|
||||
}
|
||||
|
||||
public virtual async Task<string> ChangeDisplayName(string newName)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var originName = string.IsNullOrEmpty(DisplayName) ? NewPlayerName : DisplayName;
|
||||
var result = await PlayFabClientAsyncAPI.UpdateUserTitleDisplayNameAsync(new UpdateUserTitleDisplayNameRequest
|
||||
{
|
||||
DisplayName = newName
|
||||
});
|
||||
if (generation != logoutGeneration || result == null)
|
||||
return originName;
|
||||
DisplayName = result.DisplayName;
|
||||
GContext.Publish(new ChangeAvatarEvent());
|
||||
UpdateProfileName();
|
||||
return DisplayName;
|
||||
}
|
||||
|
||||
public virtual async Task<string> UpdateAvatarUrl(string url)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var originUrl = AvatarUrl;
|
||||
|
||||
Debug.Log("UpdateAvatarUrlAsync originUrl:" + originUrl + " url " + url);
|
||||
|
||||
if (string.IsNullOrEmpty(url)) return originUrl;
|
||||
if (url == originUrl)
|
||||
{
|
||||
return originUrl;
|
||||
}
|
||||
var result = await PlayFabClientAsyncAPI.UpdateAvatarUrlAsync(new UpdateAvatarUrlRequest
|
||||
{
|
||||
ImageUrl = url
|
||||
});
|
||||
Debug.Log("UpdateAvatarUrlAsync :" + result);
|
||||
if (generation != logoutGeneration || result == null) return originUrl;
|
||||
AvatarUrl = url;
|
||||
GContext.Publish(new ChangeAvatarEvent());
|
||||
UpdateProfileAvatar();
|
||||
return url;
|
||||
}
|
||||
|
||||
public virtual async Task<string> UpdateLanguage(string code)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
if (Language == code)
|
||||
{
|
||||
return Language;
|
||||
}
|
||||
PlayFab.ProfilesModels.SetProfileLanguageRequest setProfileLanguageRequest = new PlayFab.ProfilesModels.SetProfileLanguageRequest()
|
||||
{
|
||||
Language = code.Replace('_', '-')
|
||||
};
|
||||
|
||||
var result = await PlayFabProfilesAsyncAPI.SetProfileLanguageAsync(setProfileLanguageRequest);
|
||||
if (generation == logoutGeneration && result != null)
|
||||
Language = code;
|
||||
return Language;
|
||||
}
|
||||
|
||||
public double CreateTotalSeconds()
|
||||
{
|
||||
return (ZZTimeHelper.UtcNow() - CreateTime).TotalSeconds;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> GetLinkState(EAccoutType accoutType)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
return false;
|
||||
}
|
||||
public void UpdateProfile()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Avatar = AvatarUrl;
|
||||
playerProfile.DisplayName = DisplayName;
|
||||
playerProfile.Level = GContext.container.Resolve<PlayerData>().lv;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
public void UpdateProfileLv()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Level = GContext.container.Resolve<PlayerData>().lv;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
void UpdateProfileName()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.DisplayName = DisplayName;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
void UpdateProfileAvatar()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Avatar = AvatarUrl;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
#region 其他玩家头像昵称缓存
|
||||
|
||||
Dictionary<string, PlayInfo> PlayInfos = new Dictionary<string, PlayInfo>();
|
||||
Dictionary<string, int> PlayLvs = new Dictionary<string, int>();
|
||||
public async Task<(string, int)> GetPlayLv(string playFabId)
|
||||
{
|
||||
if (!PlayLvs.ContainsKey(playFabId))
|
||||
{
|
||||
var resultUserData = await PlayFabClientAsyncAPI.GetUserDataAsync(new GetUserDataRequest()
|
||||
{
|
||||
PlayFabId = playFabId,
|
||||
Keys = new List<string>() { "Lv" }
|
||||
});
|
||||
if (resultUserData != null)
|
||||
{
|
||||
string level = resultUserData.Data.TryGetValue("Lv", out var value) ? value.Value : "1";
|
||||
PlayLvs[playFabId] = int.Parse(level);
|
||||
}
|
||||
}
|
||||
int lv = PlayLvs[playFabId];
|
||||
|
||||
return (playFabId, lv);
|
||||
}
|
||||
public int GetPlayerLv(string playFabId)
|
||||
{
|
||||
return PlayLvs.ContainsKey(playFabId) ? PlayLvs[playFabId] : 1;
|
||||
}
|
||||
|
||||
public void SetPlayerLv(string playFabId, int lv)
|
||||
{
|
||||
if (playFabId == UserId)
|
||||
{
|
||||
lv = GContext.container.Resolve<PlayerData>().lv;
|
||||
}
|
||||
PlayLvs[playFabId] = lv;
|
||||
}
|
||||
|
||||
public void ClearPlayInfos()
|
||||
{
|
||||
PlayInfos.Clear();
|
||||
PlayLvs.Clear();
|
||||
}
|
||||
|
||||
public PlayInfo GetPlayInfo(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!PlayInfos.ContainsKey(id))
|
||||
{
|
||||
PlayInfo playInfo = new PlayInfo();
|
||||
playInfo.name = GetDefaultName(id);
|
||||
PlayInfos.Add(id, playInfo);
|
||||
SetPlayInfo(id, playInfo);
|
||||
}
|
||||
return PlayInfos[id];
|
||||
}
|
||||
|
||||
async void SetPlayInfo(string playFabId, PlayInfo playInfo)
|
||||
{
|
||||
if (playFabId == UserId)
|
||||
{
|
||||
playInfo.name = DisplayName;
|
||||
playInfo.avatarUrl = AvatarUrl;
|
||||
GContext.Publish(new GetClubPlayInfoEvent() { id = playFabId, name = playInfo.name, avatarUrl = playInfo.avatarUrl });
|
||||
return;
|
||||
}
|
||||
var resultUserTitle = await PlayFabClientAsyncAPI.GetPlayerProfileAsync(new GetPlayerProfileRequest()
|
||||
{
|
||||
PlayFabId = playFabId,
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
ShowAvatarUrl = true,
|
||||
}
|
||||
});
|
||||
if (resultUserTitle?.PlayerProfile != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resultUserTitle?.PlayerProfile.DisplayName))
|
||||
{
|
||||
playInfo.name = resultUserTitle?.PlayerProfile.DisplayName;
|
||||
}
|
||||
playInfo.avatarUrl = resultUserTitle?.PlayerProfile.AvatarUrl;
|
||||
GContext.Publish(new GetClubPlayInfoEvent() { id = playFabId, name = playInfo.name, avatarUrl = playInfo.avatarUrl });
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPlayInfo(string playFabId, string name, string avatarUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = GetDefaultName(playFabId);
|
||||
}
|
||||
PlayInfo playInfo = new PlayInfo();
|
||||
playInfo.name = name;
|
||||
playInfo.avatarUrl = avatarUrl;
|
||||
PlayInfos[playFabId] = playInfo;
|
||||
}
|
||||
#endregion 其他玩家头像昵称缓存
|
||||
}
|
||||
|
||||
public class PlayInfo
|
||||
{
|
||||
public string name;
|
||||
public string avatarUrl;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user