feat(upserver): 接入认证与KV存档并完成限定钓鱼闭环
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using game.Account;
|
||||
using asap.core;
|
||||
using PlayFab;
|
||||
using asap.playfab.async;
|
||||
@@ -46,12 +47,13 @@ namespace game
|
||||
public uint internalId { get; set; }
|
||||
}
|
||||
|
||||
public class EvtAPISvrUnauthorized { }
|
||||
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; }
|
||||
@@ -64,6 +66,7 @@ namespace game
|
||||
EntityKey entityKey { get; }
|
||||
Task<string> UpdateAvatarUrl(string url);
|
||||
string UserId { get; }
|
||||
string ApiPlayerId { get; }
|
||||
string CustomId { get; }
|
||||
uint InternalId { get; }
|
||||
string ContinentCode { get; }
|
||||
@@ -96,7 +99,7 @@ namespace game
|
||||
void UpdateProfileLv();
|
||||
}
|
||||
|
||||
public class PlayFabUserService : IUserService
|
||||
public partial class PlayFabUserService : IUserService
|
||||
{
|
||||
[Inject]
|
||||
public IConfig config { get; set; }
|
||||
@@ -104,9 +107,12 @@ namespace game
|
||||
[Inject]
|
||||
public ICustomServerMgr customServerMgr { get; set; }
|
||||
|
||||
public bool IsLogin => PlayFabClientAPI.IsClientLoggedIn();
|
||||
public bool IsLogin => !kvControlledInstance && PlayFabClientAPI.IsClientLoggedIn() && IsAuthorized;
|
||||
|
||||
public bool IsAuthorized => InternalId != 0;
|
||||
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; }
|
||||
|
||||
@@ -140,13 +146,51 @@ namespace game
|
||||
return $"Angler#{id[0..5]}";
|
||||
}
|
||||
|
||||
public PlayFabUserService()
|
||||
public PlayFabUserService() : this(null) { }
|
||||
|
||||
internal PlayFabUserService(PlayerDataStorage.PlayerKVControlledLoadCapability capability)
|
||||
{
|
||||
GContext.OnEvent<EvtAPISvrUnauthorized>().Subscribe(_ => LoginApiServer());
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
public virtual async Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true)
|
||||
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);
|
||||
@@ -155,16 +199,19 @@ namespace game
|
||||
|
||||
if (!PlayFabClientAPI.IsClientLoggedIn())
|
||||
{
|
||||
if (!await LoginPlayFab())
|
||||
if (!await LoginPlayFab(generation))
|
||||
return ELoginStep.PlayFab;
|
||||
}
|
||||
|
||||
if (generation != logoutGeneration) return ELoginStep.Api;
|
||||
|
||||
SetMMTestValue();
|
||||
|
||||
GContext.container.Resolve<IChatService>().WaitingForLogin();
|
||||
|
||||
OnYinDunLogin();
|
||||
LoginApiServer();
|
||||
if (!await LoginApiServer() || generation != logoutGeneration)
|
||||
return ELoginStep.Api;
|
||||
|
||||
InitTapJoy();
|
||||
progress?.Invoke(2);
|
||||
@@ -286,7 +333,7 @@ namespace game
|
||||
GEvent.SetUserProp("MMTestValue", ab, true);
|
||||
Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(MMTestValue, 2)} MMTestValueString:{ab}");
|
||||
}
|
||||
private async Task<bool> LoginPlayFab()
|
||||
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();
|
||||
@@ -303,7 +350,11 @@ namespace game
|
||||
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()
|
||||
{
|
||||
@@ -371,6 +422,11 @@ namespace game
|
||||
#endif
|
||||
}
|
||||
sw.Stop();
|
||||
if (generation != logoutGeneration)
|
||||
{
|
||||
PlayFabClientAPI.ForgetAllCredentials();
|
||||
return false;
|
||||
}
|
||||
if (loginResult == null) return false;
|
||||
|
||||
Debug.Log($"LoginPlayFab elapsed={sw.Elapsed.TotalSeconds}");
|
||||
@@ -401,6 +457,7 @@ namespace game
|
||||
}
|
||||
};
|
||||
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";
|
||||
@@ -416,7 +473,9 @@ namespace game
|
||||
if (string.IsNullOrEmpty(DisplayName))
|
||||
{
|
||||
sw.Start();
|
||||
DisplayName = await ChangeDisplayName(NewPlayerName);
|
||||
var newDisplayName = await ChangeDisplayName(NewPlayerName);
|
||||
if (generation != logoutGeneration) return false;
|
||||
DisplayName = newDisplayName;
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab ChangeDisplayName elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
@@ -427,6 +486,7 @@ namespace game
|
||||
sw.Start();
|
||||
/*AvatarUrl = */
|
||||
await UpdateAvatarUrl(NewPlayerAvatar);
|
||||
if (generation != logoutGeneration) return false;
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab update avatar elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
@@ -434,6 +494,7 @@ namespace game
|
||||
//BuglyAgent.SetUserId(UserId);
|
||||
var setting = GContext.container.Resolve<ISettingService>();
|
||||
await UpdateLanguage(setting.Lang);
|
||||
if (generation != logoutGeneration) return false;
|
||||
|
||||
var playfabUserData = loginResult.InfoResultPayload?.UserData;
|
||||
|
||||
@@ -456,69 +517,71 @@ namespace game
|
||||
|
||||
//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 bool tryLoginApiSvr = false;
|
||||
private S101AccountSession apiSession;
|
||||
private Task<bool> apiLoginTask;
|
||||
private int apiLoginGeneration;
|
||||
|
||||
private async void LoginApiServer()
|
||||
private Task<bool> LoginApiServer()
|
||||
{
|
||||
if (tryLoginApiSvr)
|
||||
return;
|
||||
tryLoginApiSvr = true;
|
||||
|
||||
var sw = new System.Diagnostics.Stopwatch();
|
||||
|
||||
PlayerProfile playerProfile = new PlayerProfile()
|
||||
{
|
||||
Avatar = AvatarUrl,
|
||||
DisplayName = DisplayName,
|
||||
Level = GContext.container.Resolve<PlayerData>().lv
|
||||
};
|
||||
|
||||
LoginRequest loginRequest = new LoginRequest()
|
||||
{
|
||||
playerId = UserId,
|
||||
playerProfile = playerProfile
|
||||
};
|
||||
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
|
||||
{
|
||||
var apiLoginResponse = await customServerMgr.AccountRequest("Login", loginRequest);
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginApiServer elapsed={sw.Elapsed.TotalSeconds}");
|
||||
|
||||
if (string.IsNullOrEmpty(apiLoginResponse))
|
||||
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)
|
||||
{
|
||||
tryLoginApiSvr = false;
|
||||
return;
|
||||
Debug.LogWarning($"API login failed: {result.Error}, HTTP {result.StatusCode}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = JObject.Parse(apiLoginResponse);
|
||||
if ((byte)result["state"] == 0)
|
||||
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
|
||||
{
|
||||
InternalId = (uint)result["internalId"];
|
||||
GContext.Publish(new EvtAPISvrLoginSuccess()
|
||||
{
|
||||
playerId = UserId,
|
||||
internalId = InternalId
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
InternalId = 0;
|
||||
}
|
||||
playerId = session.PlayerId,
|
||||
internalId = InternalId
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
InternalId = 0;
|
||||
if (generation == apiLoginGeneration)
|
||||
{
|
||||
InternalId = 0;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
customServerMgr.ClearAccountSession();
|
||||
}
|
||||
Debug.LogError($"API login failed: {e.GetType().Name}");
|
||||
return false;
|
||||
}
|
||||
|
||||
tryLoginApiSvr = false;
|
||||
}
|
||||
|
||||
public async virtual Task<(int code, int userid)> LinkAccount(EAccoutType accoutType)
|
||||
@@ -608,22 +671,44 @@ namespace game
|
||||
|
||||
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 (result == null)
|
||||
if (generation != logoutGeneration || result == null)
|
||||
return originName;
|
||||
DisplayName = result.DisplayName;
|
||||
GContext.Publish(new ChangeAvatarEvent());
|
||||
@@ -633,6 +718,7 @@ namespace game
|
||||
|
||||
public virtual async Task<string> UpdateAvatarUrl(string url)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var originUrl = AvatarUrl;
|
||||
|
||||
Debug.Log("UpdateAvatarUrlAsync originUrl:" + originUrl + " url " + url);
|
||||
@@ -647,7 +733,7 @@ namespace game
|
||||
ImageUrl = url
|
||||
});
|
||||
Debug.Log("UpdateAvatarUrlAsync :" + result);
|
||||
if (result == null) return originUrl;
|
||||
if (generation != logoutGeneration || result == null) return originUrl;
|
||||
AvatarUrl = url;
|
||||
GContext.Publish(new ChangeAvatarEvent());
|
||||
UpdateProfileAvatar();
|
||||
@@ -656,6 +742,7 @@ namespace game
|
||||
|
||||
public virtual async Task<string> UpdateLanguage(string code)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
if (Language == code)
|
||||
{
|
||||
return Language;
|
||||
@@ -666,7 +753,7 @@ namespace game
|
||||
};
|
||||
|
||||
var result = await PlayFabProfilesAsyncAPI.SetProfileLanguageAsync(setProfileLanguageRequest);
|
||||
if (result != null)
|
||||
if (generation == logoutGeneration && result != null)
|
||||
Language = code;
|
||||
return Language;
|
||||
}
|
||||
@@ -683,32 +770,36 @@ namespace game
|
||||
}
|
||||
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 = UserId, playerProfile = playerProfile };
|
||||
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 = UserId, playerProfile = playerProfile };
|
||||
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 = UserId, playerProfile = playerProfile };
|
||||
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 = UserId, playerProfile = playerProfile };
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
#region 其他玩家头像昵称缓存
|
||||
|
||||
Reference in New Issue
Block a user