using System.Threading.Tasks; 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.Linq; using tysdk; using UniRx; 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 interface IUserService { Task Login(Action progress); void Logout(); bool IsLogin { get; } bool IsAuthorized { get; } DateTime LoginTime { get; } DateTime CreateTime { get; } Task ChangeDisplayName(string newName); string DisplayName { get; } string AvatarUrl { get; } Dictionary EAccoutUrl { get; } EntityKey entityKey { get; } Task UpdateAvatarUrl(string url); string UserId { get; } string CustomId { get; } uint InternalId { get; } string ContinentCode { get; } string CountryCode { get; } double CreateTotalSeconds(); string Language { get; } Task UpdateLanguage(string code); Task LinkAccount(EAccoutType accoutType); Task 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 class PlayFabUserService : IUserService { [Inject] public IConfig config { get; set; } [Inject] public ICustomServerMgr customServerMgr { get; set; } public bool IsLogin => PlayFabClientAPI.IsClientLoggedIn(); public bool IsAuthorized => InternalId != 0; 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 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> 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 = "MMPack"; const string MMTestSubKey2 = "MMB"; public string GetDefaultName(string id) { return $"Angler#{id[0..5]}"; } public PlayFabUserService() { GContext.OnEvent().Subscribe(_ => LoginApiServer()); } public virtual async Task Login(Action progress) { if (IsLogin) { progress?.Invoke(2); return ELoginStep.Done; } if (!PlayFabClientAPI.IsClientLoggedIn()) { if (!await LoginPlayFab()) return ELoginStep.PlayFab; } SetMMTestValue(); GContext.container.Resolve().WaitingForLogin(); LoginApiServer(); progress?.Invoke(2); return ELoginStep.Done; } 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 % 8); MMTestValue = AB; PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey2}#{MMTestValue}"); } string ab = GContext.container.Resolve().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, $"{MMTestSubKey2}#{MMTestValue}"); string ab = GContext.container.Resolve().Init(MMTestValue); GEvent.SetUserProp("MMTestValue", ab, true); Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(MMTestValue, 2)} MMTestValueString:{ab}"); } private async Task LoginPlayFab() { PlayFabSettings.TitleId = config.Get(GConstant.K_PlayFab_Title, GConstant.V_Debug_PF_Title); PlayFab.Internal.PlayFabWebRequest.SkipCertificateValidation(); var timeout = config.Get(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, GetUserData = true, 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.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 { CustomId = Application.identifier, CreateAccount = true, InfoRequestParameters = info_request_parameters, }; loginResult = await PlayFabClientAsyncAPI.LoginWithCustomIDAsync(request); #endif } sw.Stop(); 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); //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(); DisplayName = await ChangeDisplayName(NewPlayerName); sw.Stop(); Debug.Log($"LoginPlayFab ChangeDisplayName elapsed={sw.Elapsed.TotalSeconds}"); sw.Reset(); } if (string.IsNullOrEmpty(AvatarUrl)) { sw.Start(); /*AvatarUrl = */ await UpdateAvatarUrl(NewPlayerAvatar); sw.Stop(); Debug.Log($"LoginPlayFab update avatar elapsed={sw.Elapsed.TotalSeconds}"); sw.Reset(); } //BuglyAgent.SetUserId(UserId); var setting = GContext.container.Resolve(); await UpdateLanguage(setting.Lang); 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; } ZZTimeHelper.AdjustTime(LoginTime); ZZTimeHelper.WathForcusChange(); //InitData should invoke after local time adjusted. //save login time to local storage. InitData(playfabUserData, UserId); return true; } private bool tryLoginApiSvr = false; private async void LoginApiServer() { if (tryLoginApiSvr) return; tryLoginApiSvr = true; var sw = new System.Diagnostics.Stopwatch(); PlayerProfile playerProfile = new PlayerProfile() { Avatar = AvatarUrl, DisplayName = DisplayName, Level = GContext.container.Resolve().lv }; LoginRequest loginRequest = new LoginRequest() { playerId = UserId, playerProfile = playerProfile }; try { var apiLoginResponse = await customServerMgr.AccountRequest("Login", loginRequest); sw.Stop(); Debug.Log($"LoginApiServer elapsed={sw.Elapsed.TotalSeconds}"); if (string.IsNullOrEmpty(apiLoginResponse)) return; var result = JObject.Parse(apiLoginResponse); if ((byte)result["state"] == 0) { InternalId = (uint)result["internalId"]; GContext.Publish(new EvtAPISvrLoginSuccess() { playerId = UserId, internalId = InternalId }); } else { InternalId = 0; } } catch (Exception e) { Debug.LogError(e); InternalId = 0; } tryLoginApiSvr = false; } public async virtual Task 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 false; } 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(); } EAccoutUrl[accountInfo.channel.ToString()] = accountInfo.avatar; string EAccoutUrlStr = Newtonsoft.Json.JsonConvert.SerializeObject(EAccoutUrl); PlayFabMgr.Instance.UpdateUserDataValue("EAccoutUrl", EAccoutUrlStr); } } private void InitData(Dictionary datas, string userId) { var comparedData = PlayFabMgr.Instance.CompareDatas(datas, userId); if (comparedData != null) { MMTestValue = 0; if (!comparedData.ContainsKey(MMTestKey)) { if (config.Get("NewlyCreated", false)) { //新用户设置MMTest MMTestValue = -1; } else { //老用户设置MMTest PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey2}#{MMTestValue}"); } } else { string mmTestValue = comparedData[MMTestKey]; string[] mmTestValueData = mmTestValue.Split('#'); if (mmTestValueData.Length > 1 && mmTestValueData[0] == MMTestSubKey) { int.TryParse(mmTestValueData[1], out int value); MMTestValue = value & 0b110; PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey2}#{MMTestValue}"); } else if (mmTestValueData.Length > 1 && mmTestValueData[0] == MMTestSubKey2) { int.TryParse(mmTestValueData[1], out int value); MMTestValue = value; } else { MMTestValue = 0; PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey2}#{MMTestValue}"); } } GContext.container.Resolve().LoadPlayerData(comparedData); OnInitData?.Invoke(comparedData); } } public virtual void Logout() { UserId = null; InternalId = 0; DisplayName = null; AvatarUrl = null; PlayFabClientAPI.ForgetAllCredentials(); } public virtual async Task ChangeDisplayName(string newName) { var originName = string.IsNullOrEmpty(DisplayName) ? NewPlayerName : DisplayName; var result = await PlayFabClientAsyncAPI.UpdateUserTitleDisplayNameAsync(new UpdateUserTitleDisplayNameRequest { DisplayName = newName }); if (result == null) return originName; DisplayName = result.DisplayName; GContext.Publish(new ChangeAvatarEvent()); UpdateProfileName(); return DisplayName; } public virtual async Task UpdateAvatarUrl(string url) { 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 (result == null) return originUrl; AvatarUrl = url; GContext.Publish(new ChangeAvatarEvent()); UpdateProfileAvatar(); return url; } public virtual async Task UpdateLanguage(string code) { if (Language == code) { return Language; } PlayFab.ProfilesModels.SetProfileLanguageRequest setProfileLanguageRequest = new PlayFab.ProfilesModels.SetProfileLanguageRequest() { Language = code.Replace('_', '-') }; var result = await PlayFabProfilesAsyncAPI.SetProfileLanguageAsync(setProfileLanguageRequest); if (result != null) Language = code; return Language; } public double CreateTotalSeconds() { return (ZZTimeHelper.UtcNow() - CreateTime).TotalSeconds; } public virtual async Task GetLinkState(EAccoutType accoutType) { await Awaiters.NextFrame; return false; } public void UpdateProfile() { PlayerProfile playerProfile = new PlayerProfile(); playerProfile.Avatar = AvatarUrl; playerProfile.DisplayName = DisplayName; playerProfile.Level = GContext.container.Resolve().lv; LoginRequest loginRequest = new LoginRequest() { playerId = UserId, playerProfile = playerProfile }; customServerMgr.AccountRequest("UpdateProfile", loginRequest); } public void UpdateProfileLv() { PlayerProfile playerProfile = new PlayerProfile(); playerProfile.Level = GContext.container.Resolve().lv; LoginRequest loginRequest = new LoginRequest() { playerId = UserId, playerProfile = playerProfile }; customServerMgr.AccountRequest("UpdateProfile", loginRequest); } void UpdateProfileName() { PlayerProfile playerProfile = new PlayerProfile(); playerProfile.DisplayName = DisplayName; LoginRequest loginRequest = new LoginRequest() { playerId = UserId, playerProfile = playerProfile }; customServerMgr.AccountRequest("UpdateProfile", loginRequest); } void UpdateProfileAvatar() { PlayerProfile playerProfile = new PlayerProfile(); playerProfile.Avatar = AvatarUrl; LoginRequest loginRequest = new LoginRequest() { playerId = UserId, playerProfile = playerProfile }; customServerMgr.AccountRequest("UpdateProfile", loginRequest); } #region 其他玩家头像昵称缓存 Dictionary PlayInfos = new Dictionary(); Dictionary PlayLvs = new Dictionary(); public async Task<(string, int)> GetPlayLv(string playFabId) { if (!PlayLvs.ContainsKey(playFabId)) { var resultUserData = await PlayFabClientAsyncAPI.GetUserDataAsync(new GetUserDataRequest() { PlayFabId = playFabId, Keys = new List() { "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().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; } }