feedback report 3 done
This commit is contained in:
+407
-106
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
@@ -16,12 +17,14 @@ public class LoginManager : MonoBehaviour
|
||||
{
|
||||
public const string AuthBaseUrl = "https://kkapi.playpoolstudios.com";
|
||||
const string PlayerPrefsAuthKey = "WalkInvest.AuthToken";
|
||||
const string PlayerPrefsDeviceIdKey = "WalkInvest.DeviceId";
|
||||
const string AlreadyLoggedInCode = "already_logged_in";
|
||||
|
||||
const int MIN_USERNAME_LENGTH = 3;
|
||||
const int MAX_USERNAME_LENGTH = 16;
|
||||
const int MIN_PASSWORD_LENGTH = 8;
|
||||
const int MAX_PASSWORD_LENGTH = 20;
|
||||
const int MIN_USERNAME_LENGTH = 2;
|
||||
const int MIN_REGISTER_PASSWORD_LENGTH = 6;
|
||||
const int ShortAuthMessageMaxLength = 36;
|
||||
const string PlayerConnectionError = "Please check your internet connection and try again.";
|
||||
const string PlayerGenericError = "Something went wrong. Please try again.";
|
||||
|
||||
[Header("Login")]
|
||||
[SerializeField] private TMP_InputField usernameInputLogin;
|
||||
@@ -55,6 +58,8 @@ public class LoginManager : MonoBehaviour
|
||||
|
||||
bool _authInProgress;
|
||||
Coroutine _keepaliveRoutine;
|
||||
bool _remoteLogoutInFlight;
|
||||
bool _retireAfterRemoteLogout;
|
||||
bool _appPaused;
|
||||
bool _appFocused = true;
|
||||
bool _handlingKeepaliveUnauthorized;
|
||||
@@ -115,7 +120,10 @@ public class LoginManager : MonoBehaviour
|
||||
{
|
||||
instance.StopKeepalive();
|
||||
SceneManager.sceneLoaded -= instance.OnSceneLoaded;
|
||||
Destroy(instance.gameObject);
|
||||
if (instance._remoteLogoutInFlight)
|
||||
instance._retireAfterRemoteLogout = true;
|
||||
else
|
||||
Destroy(instance.gameObject);
|
||||
}
|
||||
|
||||
instance = this;
|
||||
@@ -136,6 +144,10 @@ public class LoginManager : MonoBehaviour
|
||||
{
|
||||
btnLogin.onClick.AddListener(OnLogin);
|
||||
btnRegister.onClick.AddListener(OnRegister);
|
||||
if (usernameInputLogin != null)
|
||||
usernameInputLogin.characterLimit = 0;
|
||||
if (usernameInputRegister != null)
|
||||
usernameInputRegister.characterLimit = 0;
|
||||
if (btnServerUpdate != null)
|
||||
btnServerUpdate.onClick.AddListener(OnServerUpdateClick);
|
||||
if (btnErrorClose != null)
|
||||
@@ -172,7 +184,7 @@ public class LoginManager : MonoBehaviour
|
||||
if (usernameInputLogin != null) usernameInputLogin.text = devUser;
|
||||
if (passwordInputLogin != null) passwordInputLogin.text = devPass;
|
||||
|
||||
string loginJson = "{\"username\":\"" + EscapeJsonString(devUser) + "\",\"password\":\"" + EscapeJsonString(devPass) + "\"}";
|
||||
string loginJson = BuildLoginBody(devUser, devPass);
|
||||
// StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/login", loginJson, "Signed in."));
|
||||
SetBusy(false);
|
||||
#else
|
||||
@@ -244,21 +256,40 @@ public class LoginManager : MonoBehaviour
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
/// <summary>Clears session state and all PlayerPrefs (full logout).</summary>
|
||||
/// <summary>Clears the server session, then local session state and PlayerPrefs.</summary>
|
||||
public static void Logout()
|
||||
{
|
||||
if (MainMenuManager.instance != null)
|
||||
MainMenuManager.instance.ClearWithdrawalAddress();
|
||||
|
||||
string token = AuthToken;
|
||||
if (instance != null)
|
||||
{
|
||||
instance.StopKeepalive();
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
instance._remoteLogoutInFlight = true;
|
||||
instance.StartCoroutine(instance.RemoteLogoutCoroutine(token));
|
||||
}
|
||||
}
|
||||
|
||||
AuthToken = "";
|
||||
ClearLocalUserProfile();
|
||||
MainMenuManager.ClearCachedSettingsProfile();
|
||||
ClearMatchYourTeam();
|
||||
OnUserDataUpdated = null;
|
||||
Cupid.ClearSession();
|
||||
CupidLobby.ClearUsername();
|
||||
GameManager.ClearMatchSession();
|
||||
if (LevelLoadManager.instance != null)
|
||||
LevelLoadManager.instance.ResetSession();
|
||||
NetPlayer.RedPlayer = null;
|
||||
NetPlayer.BluePlayer = null;
|
||||
|
||||
string deviceId = PlayerPrefs.GetString(PlayerPrefsDeviceIdKey, "");
|
||||
PlayerPrefs.DeleteAll();
|
||||
if (IsUsableDeviceId(deviceId))
|
||||
PlayerPrefs.SetString(PlayerPrefsDeviceIdKey, deviceId);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
@@ -280,8 +311,25 @@ public class LoginManager : MonoBehaviour
|
||||
void SetBusy(bool busy)
|
||||
{
|
||||
_authInProgress = busy;
|
||||
if (btnLogin != null) btnLogin.interactable = !busy;
|
||||
if (btnRegister != null) btnRegister.interactable = !busy;
|
||||
SetButtonInteractable(btnLogin, !busy);
|
||||
SetButtonInteractable(btnRegister, !busy);
|
||||
}
|
||||
|
||||
static void SetButtonInteractable(Button button, bool interactable)
|
||||
{
|
||||
if (button == null)
|
||||
return;
|
||||
|
||||
if (button.interactable == interactable)
|
||||
return;
|
||||
|
||||
button.interactable = interactable;
|
||||
if (!interactable)
|
||||
return;
|
||||
|
||||
// Disabling mid-click swallows pointer-up, and the button then ignores later clicks.
|
||||
button.enabled = false;
|
||||
button.enabled = true;
|
||||
}
|
||||
|
||||
void ShowAuthMessage(string message)
|
||||
@@ -289,6 +337,8 @@ public class LoginManager : MonoBehaviour
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
message = ToPlayerError(message, PlayerGenericError);
|
||||
|
||||
if (MessageBoxDialog.instance == null)
|
||||
{
|
||||
_pendingAuthMessage = message;
|
||||
@@ -321,40 +371,37 @@ public class LoginManager : MonoBehaviour
|
||||
void OnLogin()
|
||||
{
|
||||
if (_authInProgress) return;
|
||||
if (_keepaliveRoutine != null && !string.IsNullOrEmpty(AuthToken)) return;
|
||||
// A failed attempt can leave a token and keepalive without leaving this screen.
|
||||
// Drop that session so this click performs a new connection check.
|
||||
if (_keepaliveRoutine != null && !string.IsNullOrEmpty(AuthToken))
|
||||
InvalidateStoredSession();
|
||||
|
||||
string username = usernameInputLogin.text;
|
||||
string password = passwordInputLogin.text;
|
||||
string username = usernameInputLogin.text != null ? usernameInputLogin.text.Trim() : "";
|
||||
string password = passwordInputLogin.text ?? "";
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
||||
{
|
||||
ShowAuthMessage("Please fill in all fields");
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
|
||||
if (username.Length < MIN_USERNAME_LENGTH)
|
||||
{
|
||||
ShowAuthMessage("Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters");
|
||||
ShowAuthMessage("Username must be at least " + MIN_USERNAME_LENGTH + " characters");
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.Length < MIN_PASSWORD_LENGTH || password.Length > MAX_PASSWORD_LENGTH)
|
||||
{
|
||||
ShowAuthMessage("Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters");
|
||||
return;
|
||||
}
|
||||
|
||||
string json = "{\"username\":\"" + EscapeJsonString(username) + "\",\"password\":\"" + EscapeJsonString(password) + "\"}";
|
||||
StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/login", json, "Signed in."));
|
||||
StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/login", BuildLoginBody(username, password), "Signed in."));
|
||||
}
|
||||
|
||||
void OnRegister()
|
||||
{
|
||||
if (_authInProgress) return;
|
||||
if (_keepaliveRoutine != null && !string.IsNullOrEmpty(AuthToken)) return;
|
||||
if (_keepaliveRoutine != null && !string.IsNullOrEmpty(AuthToken))
|
||||
InvalidateStoredSession();
|
||||
|
||||
string username = usernameInputRegister.text;
|
||||
string username = usernameInputRegister.text != null ? usernameInputRegister.text.Trim() : "";
|
||||
string email = emailInputRegister != null ? emailInputRegister.text : "";
|
||||
string password = passwordInputRegister.text;
|
||||
string password = passwordInputRegister.text ?? "";
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
|
||||
{
|
||||
ShowAuthMessage("Please fill in all fields");
|
||||
@@ -368,20 +415,19 @@ public class LoginManager : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
|
||||
if (username.Length < MIN_USERNAME_LENGTH)
|
||||
{
|
||||
ShowAuthMessage("Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters");
|
||||
ShowAuthMessage("Username must be at least " + MIN_USERNAME_LENGTH + " characters");
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.Length < MIN_PASSWORD_LENGTH || password.Length > MAX_PASSWORD_LENGTH)
|
||||
if (password.Length < MIN_REGISTER_PASSWORD_LENGTH)
|
||||
{
|
||||
ShowAuthMessage("Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters");
|
||||
ShowAuthMessage("Password must be at least " + MIN_REGISTER_PASSWORD_LENGTH + " characters");
|
||||
return;
|
||||
}
|
||||
|
||||
string json = "{\"username\":\"" + EscapeJsonString(username) + "\",\"email\":\"" + EscapeJsonString(email) + "\",\"password\":\"" + EscapeJsonString(password) + "\"}";
|
||||
StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/register", json, "Account created."));
|
||||
StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/register", BuildRegisterBody(username, email, password), "Account created."));
|
||||
}
|
||||
|
||||
static bool IsValidEmailFormat(string email)
|
||||
@@ -416,69 +462,301 @@ public class LoginManager : MonoBehaviour
|
||||
return s.Replace("\\", "\\\\").Replace("\"", "\\\"");
|
||||
}
|
||||
|
||||
static string BuildLoginBody(string username, string password)
|
||||
{
|
||||
return "{\"username\":\"" + EscapeJsonString(username)
|
||||
+ "\",\"password\":\"" + EscapeJsonString(password)
|
||||
+ "\",\"device_id\":\"" + EscapeJsonString(GetDeviceId()) + "\"}";
|
||||
}
|
||||
|
||||
static string BuildRegisterBody(string username, string email, string password)
|
||||
{
|
||||
return "{\"username\":\"" + EscapeJsonString(username)
|
||||
+ "\",\"email\":\"" + EscapeJsonString(email)
|
||||
+ "\",\"password\":\"" + EscapeJsonString(password)
|
||||
+ "\",\"device_id\":\"" + EscapeJsonString(GetDeviceId()) + "\"}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stable id sent on register and login. The server keeps it across logout, so a login
|
||||
/// from this device can still match while logout is in flight.
|
||||
/// </summary>
|
||||
static string GetDeviceId()
|
||||
{
|
||||
string id = SystemInfo.deviceUniqueIdentifier;
|
||||
if (IsUsableDeviceId(id))
|
||||
return id;
|
||||
|
||||
id = PlayerPrefs.GetString(PlayerPrefsDeviceIdKey, "");
|
||||
if (IsUsableDeviceId(id))
|
||||
return id;
|
||||
|
||||
id = Guid.NewGuid().ToString("N");
|
||||
PlayerPrefs.SetString(PlayerPrefsDeviceIdKey, id);
|
||||
PlayerPrefs.Save();
|
||||
return id;
|
||||
}
|
||||
|
||||
static bool IsUsableDeviceId(string id)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(id) && id != SystemInfo.unsupportedIdentifier;
|
||||
}
|
||||
|
||||
/// <summary>JsonUtility cannot read <c>"id":"42"</c> into <see cref="UserData.id"/>.</summary>
|
||||
static string CoerceQuotedNumericIds(string json)
|
||||
{
|
||||
if (string.IsNullOrEmpty(json))
|
||||
return json;
|
||||
return Regex.Replace(json, "\"id\"\\s*:\\s*\"(\\d+)\"", "\"id\":$1");
|
||||
}
|
||||
|
||||
static bool IsTransportFailure(UnityWebRequest request)
|
||||
{
|
||||
return request.result != UnityWebRequest.Result.Success &&
|
||||
request.result != UnityWebRequest.Result.ProtocolError;
|
||||
}
|
||||
|
||||
static string TransportError(UnityWebRequest request)
|
||||
{
|
||||
string technical = request == null || string.IsNullOrEmpty(request.error) ? "Network error" : request.error;
|
||||
string url = request != null ? request.url : "";
|
||||
Logger.Log("Network error: " + technical + " url=" + url);
|
||||
return PlayerConnectionError;
|
||||
}
|
||||
|
||||
static string PlayerFacingHttpError(string body, long responseCode)
|
||||
{
|
||||
if (responseCode == 401)
|
||||
return "Session expired. Please sign in again.";
|
||||
|
||||
string apiError = TryReadApiErrorField(body);
|
||||
string candidate = !string.IsNullOrWhiteSpace(apiError) ? apiError : body;
|
||||
if (string.IsNullOrWhiteSpace(candidate))
|
||||
{
|
||||
Logger.Log("HTTP " + responseCode + " with no error message");
|
||||
return PlayerGenericError;
|
||||
}
|
||||
|
||||
return ToPlayerError(candidate, PlayerGenericError);
|
||||
}
|
||||
|
||||
static string TryReadApiErrorField(string body)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(body))
|
||||
return null;
|
||||
|
||||
string trimmed = body.TrimStart();
|
||||
if (trimmed.Length == 0 || trimmed[0] != '{')
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
AuthApiResponse resp = JsonUtility.FromJson<AuthApiResponse>(body);
|
||||
return resp != null ? resp.error : null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rewrites engine and transport text ("Cannot resolve destination host", HTTP dumps, raw JSON)
|
||||
/// before it reaches the player. Readable server messages are left as-is.
|
||||
/// </summary>
|
||||
static string ToPlayerError(string message, string fallback)
|
||||
{
|
||||
if (IsConnectivityError(message))
|
||||
{
|
||||
Logger.Log("Replaced technical error: " + message);
|
||||
return PlayerConnectionError;
|
||||
}
|
||||
|
||||
if (!IsPlayerReadable(message))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
Logger.Log("Replaced technical error: " + message);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return message.Trim();
|
||||
}
|
||||
|
||||
static bool IsConnectivityError(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return false;
|
||||
|
||||
string m = message.ToLowerInvariant();
|
||||
return m.Contains("resolve host")
|
||||
|| m.Contains("resolve destination")
|
||||
|| m.Contains("could not resolve")
|
||||
|| m.Contains("couldn't resolve")
|
||||
|| m.Contains("cannot resolve")
|
||||
|| m.Contains("can't resolve")
|
||||
|| m.Contains("unable to resolve")
|
||||
|| m.Contains("unknown host")
|
||||
|| m.Contains("name resolution")
|
||||
|| m.Contains("name or service")
|
||||
|| m.Contains("destination host")
|
||||
|| m.Contains("cannot connect")
|
||||
|| m.Contains("can't connect")
|
||||
|| m.Contains("could not connect")
|
||||
|| m.Contains("couldn't connect")
|
||||
|| m.Contains("connection refused")
|
||||
|| m.Contains("connection reset")
|
||||
|| m.Contains("connection aborted")
|
||||
|| m.Contains("connection timed")
|
||||
|| m.Contains("network is unreachable")
|
||||
|| m.Contains("network error")
|
||||
|| m.Contains("no internet")
|
||||
|| m.Contains("no route to host")
|
||||
|| m.Contains("host unreachable")
|
||||
|| m.Contains("failed to receive")
|
||||
|| m.Contains("request timeout")
|
||||
|| m.Contains("timed out")
|
||||
|| m.Contains("timeout")
|
||||
|| m.Contains("ssl")
|
||||
|| m.Contains("certificate")
|
||||
|| m.Contains("curl error")
|
||||
|| m.Contains("unknown error")
|
||||
|| m.Contains("insecure connection");
|
||||
}
|
||||
|
||||
static bool IsPlayerReadable(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message) || IsConnectivityError(message))
|
||||
return false;
|
||||
|
||||
string trimmed = message.Trim();
|
||||
if (trimmed.Length > 180)
|
||||
return false;
|
||||
|
||||
char c = trimmed[0];
|
||||
if (c == '{' || c == '<' || c == '[')
|
||||
return false;
|
||||
|
||||
string m = trimmed.ToLowerInvariant();
|
||||
return m.IndexOf("http/1") < 0
|
||||
&& m.IndexOf("request failed (") < 0
|
||||
&& m.IndexOf("empty response") < 0
|
||||
&& m.IndexOf("exception") < 0
|
||||
&& m.IndexOf("stack trace") < 0
|
||||
&& m.IndexOf("stacktrace") < 0
|
||||
&& m.IndexOf("nullreference") < 0
|
||||
&& m.IndexOf("data processing error") < 0
|
||||
&& m.IndexOf("malformed url") < 0
|
||||
&& m.IndexOf('\n') < 0
|
||||
&& m.IndexOf('\r') < 0;
|
||||
}
|
||||
|
||||
IEnumerator AuthPostCoroutine(string url, string jsonBody, string successMessage)
|
||||
{
|
||||
SetBusy(true);
|
||||
HideAuthMessage();
|
||||
ClearLocalUserProfile();
|
||||
|
||||
using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
||||
bool enteredMenu = false;
|
||||
try
|
||||
{
|
||||
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonBody);
|
||||
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
|
||||
yield return request.SendWebRequest();
|
||||
|
||||
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||
|
||||
if (request.result != UnityWebRequest.Result.Success &&
|
||||
request.result != UnityWebRequest.Result.ProtocolError)
|
||||
using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
||||
{
|
||||
ShowAuthMessage("Network Error, Please try again.");
|
||||
SetBusy(false);
|
||||
yield break;
|
||||
}
|
||||
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonBody);
|
||||
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
ShowAuthMessage("Empty response from server");
|
||||
SetBusy(false);
|
||||
yield break;
|
||||
}
|
||||
yield return request.SendWebRequest();
|
||||
|
||||
AuthApiResponse resp = JsonUtility.FromJson<AuthApiResponse>(text);
|
||||
|
||||
if (resp.ok && !string.IsNullOrEmpty(resp.token))
|
||||
{
|
||||
AuthToken = resp.token;
|
||||
PlayerPrefs.SetString(PlayerPrefsAuthKey, resp.token);
|
||||
PlayerPrefs.Save();
|
||||
StartKeepalive();
|
||||
|
||||
bool profileOk = false;
|
||||
string profileErr = null;
|
||||
yield return StartCoroutine(FetchUserDataCoroutine((success, err, _) =>
|
||||
if (IsTransportFailure(request))
|
||||
{
|
||||
profileOk = success;
|
||||
profileErr = err;
|
||||
}, true));
|
||||
ShowAuthMessage(TransportError(request));
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (profileOk)
|
||||
ProceedToMainMenu();
|
||||
string text = CoerceQuotedNumericIds(ReadResponseBody(request));
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
ShowAuthMessage(PlayerGenericError);
|
||||
yield break;
|
||||
}
|
||||
|
||||
AuthApiResponse resp = null;
|
||||
try
|
||||
{
|
||||
resp = JsonUtility.FromJson<AuthApiResponse>(text);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log("Auth JSON error: " + e.Message);
|
||||
}
|
||||
|
||||
if (resp != null && resp.ok && !string.IsNullOrEmpty(resp.token))
|
||||
{
|
||||
AuthToken = resp.token;
|
||||
PlayerPrefs.SetString(PlayerPrefsAuthKey, resp.token);
|
||||
PlayerPrefs.Save();
|
||||
|
||||
bool profileOk = resp.user != null && !string.IsNullOrEmpty(resp.user.username);
|
||||
string profileErr = null;
|
||||
if (profileOk)
|
||||
ApplyUserData(resp.user);
|
||||
else
|
||||
{
|
||||
yield return StartCoroutine(FetchUserDataCoroutine((success, err, _) =>
|
||||
{
|
||||
profileOk = success;
|
||||
profileErr = err;
|
||||
}, true));
|
||||
}
|
||||
|
||||
if (profileOk)
|
||||
{
|
||||
enteredMenu = true;
|
||||
StartKeepalive();
|
||||
ProceedToMainMenu();
|
||||
}
|
||||
else
|
||||
{
|
||||
InvalidateStoredSession();
|
||||
ShowAuthMessage(string.IsNullOrEmpty(profileErr)
|
||||
? PlayerConnectionError
|
||||
: profileErr);
|
||||
}
|
||||
}
|
||||
else
|
||||
ShowAuthMessage(string.IsNullOrEmpty(profileErr)
|
||||
? "Signed in but could not load your profile. Check your connection and try again."
|
||||
: profileErr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowAuthMessage("Login failed. Please try again.");
|
||||
{
|
||||
string serverError = resp != null ? resp.error : null;
|
||||
if (string.IsNullOrEmpty(serverError) && resp != null && resp.code == AlreadyLoggedInCode)
|
||||
serverError = "Already logged in on another device";
|
||||
ShowAuthMessage(string.IsNullOrEmpty(serverError)
|
||||
? "Login failed. Please try again."
|
||||
: serverError);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!enteredMenu)
|
||||
SetBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
SetBusy(false);
|
||||
static string ReadResponseBody(UnityWebRequest request)
|
||||
{
|
||||
if (request == null || request.downloadHandler == null)
|
||||
return "";
|
||||
|
||||
try
|
||||
{
|
||||
return request.downloadHandler.text ?? "";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log("Auth response unreadable: " + e.Message);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void HideServerMessagePanels()
|
||||
@@ -673,16 +951,15 @@ public class LoginManager : MonoBehaviour
|
||||
|
||||
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||
|
||||
if (request.result != UnityWebRequest.Result.Success &&
|
||||
request.result != UnityWebRequest.Result.ProtocolError)
|
||||
if (IsTransportFailure(request))
|
||||
{
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(request.error) ? "Network error" : request.error, null);
|
||||
onComplete?.Invoke(false, TransportError(request), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (request.responseCode < 200 || request.responseCode >= 300)
|
||||
{
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(text) ? "Request failed (" + request.responseCode + ")" : text, null);
|
||||
onComplete?.Invoke(false, PlayerFacingHttpError(text, request.responseCode), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -731,22 +1008,21 @@ public class LoginManager : MonoBehaviour
|
||||
|
||||
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||
|
||||
if (request.result != UnityWebRequest.Result.Success &&
|
||||
request.result != UnityWebRequest.Result.ProtocolError)
|
||||
if (IsTransportFailure(request))
|
||||
{
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(request.error) ? "Network error" : request.error);
|
||||
onComplete?.Invoke(false, TransportError(request));
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (request.responseCode < 200 || request.responseCode >= 300)
|
||||
{
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(text) ? "Request failed (" + request.responseCode + ")" : text);
|
||||
onComplete?.Invoke(false, PlayerFacingHttpError(text, request.responseCode));
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
onComplete?.Invoke(false, "Empty response from server");
|
||||
onComplete?.Invoke(false, PlayerGenericError);
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -765,12 +1041,12 @@ public class LoginManager : MonoBehaviour
|
||||
if (refreshOk)
|
||||
onComplete?.Invoke(true, null);
|
||||
else
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(refreshErr) ? "Purchase ok but profile refresh failed" : refreshErr);
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(refreshErr) ? PlayerGenericError : ToPlayerError(refreshErr, PlayerGenericError));
|
||||
}
|
||||
else
|
||||
{
|
||||
string err = resp != null && !string.IsNullOrEmpty(resp.error) ? resp.error : "Purchase failed";
|
||||
onComplete?.Invoke(false, err);
|
||||
string err = resp != null && !string.IsNullOrEmpty(resp.error) ? resp.error : null;
|
||||
onComplete?.Invoke(false, ToPlayerError(err, "Purchase failed. Please try again."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -798,22 +1074,21 @@ public class LoginManager : MonoBehaviour
|
||||
|
||||
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||
|
||||
if (request.result != UnityWebRequest.Result.Success &&
|
||||
request.result != UnityWebRequest.Result.ProtocolError)
|
||||
if (IsTransportFailure(request))
|
||||
{
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(request.error) ? "Network error" : request.error, null);
|
||||
onComplete?.Invoke(false, TransportError(request), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (request.responseCode < 200 || request.responseCode >= 300)
|
||||
{
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(text) ? "Request failed (" + request.responseCode + ")" : text, null);
|
||||
onComplete?.Invoke(false, PlayerFacingHttpError(text, request.responseCode), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
onComplete?.Invoke(false, "Empty response from server", null);
|
||||
onComplete?.Invoke(false, PlayerGenericError, null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -822,8 +1097,8 @@ public class LoginManager : MonoBehaviour
|
||||
onComplete?.Invoke(true, null, resp.report);
|
||||
else
|
||||
{
|
||||
string err = resp != null && !string.IsNullOrEmpty(resp.error) ? resp.error : "Ping report failed";
|
||||
onComplete?.Invoke(false, err, null);
|
||||
string err = resp != null && !string.IsNullOrEmpty(resp.error) ? resp.error : null;
|
||||
onComplete?.Invoke(false, ToPlayerError(err, PlayerGenericError), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -846,26 +1121,25 @@ public class LoginManager : MonoBehaviour
|
||||
|
||||
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||
|
||||
if (request.result != UnityWebRequest.Result.Success &&
|
||||
request.result != UnityWebRequest.Result.ProtocolError)
|
||||
if (IsTransportFailure(request))
|
||||
{
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(request.error) ? "Network error" : request.error, null);
|
||||
onComplete?.Invoke(false, TransportError(request), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (request.responseCode < 200 || request.responseCode >= 300)
|
||||
{
|
||||
onComplete?.Invoke(false, string.IsNullOrEmpty(text) ? "Request failed (" + request.responseCode + ")" : text, null);
|
||||
onComplete?.Invoke(false, PlayerFacingHttpError(text, request.responseCode), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
onComplete?.Invoke(false, "Empty response from server", null);
|
||||
onComplete?.Invoke(false, PlayerGenericError, null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
AuthUserApiResponse resp = JsonUtility.FromJson<AuthUserApiResponse>(text);
|
||||
AuthUserApiResponse resp = JsonUtility.FromJson<AuthUserApiResponse>(CoerceQuotedNumericIds(text));
|
||||
|
||||
if (resp != null && resp.ok && resp.user != null)
|
||||
{
|
||||
@@ -875,8 +1149,8 @@ public class LoginManager : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
string err = resp != null && !string.IsNullOrEmpty(resp.error) ? resp.error : "Request failed";
|
||||
onComplete?.Invoke(false, err, null);
|
||||
string err = resp != null && !string.IsNullOrEmpty(resp.error) ? resp.error : null;
|
||||
onComplete?.Invoke(false, ToPlayerError(err, PlayerGenericError), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -948,6 +1222,33 @@ public class LoginManager : MonoBehaviour
|
||||
_keepaliveRoutine = null;
|
||||
}
|
||||
|
||||
IEnumerator RemoteLogoutCoroutine(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
string url = AuthBaseUrl + "/auth/logout";
|
||||
using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
||||
{
|
||||
request.uploadHandler = new UploadHandlerRaw(Array.Empty<byte>());
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Authorization", "Bearer " + token);
|
||||
|
||||
yield return request.SendWebRequest();
|
||||
|
||||
if (IsTransportFailure(request))
|
||||
Logger.Log("auth/logout network error: " + (string.IsNullOrEmpty(request.error) ? "Network error" : request.error));
|
||||
else if (request.responseCode < 200 || request.responseCode >= 300)
|
||||
Logger.Log("auth/logout failed: HTTP " + request.responseCode);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_remoteLogoutInFlight = false;
|
||||
if (_retireAfterRemoteLogout)
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator KeepalivePingCoroutine()
|
||||
{
|
||||
if (string.IsNullOrEmpty(AuthToken))
|
||||
@@ -971,7 +1272,7 @@ public class LoginManager : MonoBehaviour
|
||||
{
|
||||
AuthApiResponse resp = JsonUtility.FromJson<AuthApiResponse>(text);
|
||||
if (resp != null && !string.IsNullOrEmpty(resp.error))
|
||||
err = resp.error;
|
||||
err = ToPlayerError(resp.error, err);
|
||||
}
|
||||
|
||||
HandleKeepaliveUnauthorized(err);
|
||||
|
||||
Reference in New Issue
Block a user