This commit is contained in:
2026-05-27 23:24:40 +05:30
parent f3e01462c1
commit 0a1d3c7262
8 changed files with 6966 additions and 100 deletions
+224 -7
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Text;
#if UNITY_EDITOR
@@ -27,12 +28,31 @@ public class LoginManager : MonoBehaviour
[SerializeField] private Button btnLogin;
[Header("Register")]
[SerializeField] private TMP_InputField usernameInputRegister;
[SerializeField] private TMP_InputField emailInputRegister;
[SerializeField] private TMP_InputField passwordInputRegister;
[SerializeField] private Button btnRegister;
[Header("UI")]
[SerializeField] private TMP_Text error_txt;
[Header("Server messages (GET /table_settings, no auth)")]
[Header("Update")]
[SerializeField] private GameObject serverUpdatePanel;
[SerializeField] private Button btnServerUpdate;
[Tooltip("Opened when the user taps Update on the forced-update panel (e.g. Telegram channel).")]
public string telegramUpdateChannelUrl = "";
[Header("Error")]
[SerializeField] private GameObject errorPanel;
[SerializeField] private TMP_Text serverErrorMessageTxt;
[SerializeField] private Button btnErrorClose;
[Header("Warning")]
[SerializeField] private GameObject warningPanel;
[SerializeField] private TMP_Text serverWarningMessageTxt;
[SerializeField] private Button btnWarningClose;
bool _authInProgress;
public static string AuthToken { get; private set; }
@@ -85,6 +105,7 @@ public class LoginManager : MonoBehaviour
void Awake()
{
error_txt.text = "";
HideServerMessagePanels();
AuthToken = PlayerPrefs.GetString(PlayerPrefsAuthKey, "");
if(instance !=null){
@@ -102,6 +123,26 @@ public class LoginManager : MonoBehaviour
{
btnLogin.onClick.AddListener(OnLogin);
btnRegister.onClick.AddListener(OnRegister);
if (btnServerUpdate != null)
btnServerUpdate.onClick.AddListener(OnServerUpdateClick);
if (btnErrorClose != null)
btnErrorClose.onClick.AddListener(OnServerErrorCloseClick);
SetBusy(true);
StartCoroutine(StartupSequenceCoroutine());
}
IEnumerator StartupSequenceCoroutine()
{
bool mayContinue = false;
yield return StartCoroutine(CoApplyTableSettingsGate(r => mayContinue = r));
if (!mayContinue)
{
// Blocked by update or error panel — keep login UI disabled.
SetBusy(false);
yield break;
}
#if UNITY_EDITOR
// Dev: log in with clone-aware credentials instead of resuming a saved session.
@@ -117,12 +158,12 @@ public class LoginManager : MonoBehaviour
string loginJson = "{\"username\":\"" + EscapeJsonString(devUser) + "\",\"password\":\"" + EscapeJsonString(devPass) + "\"}";
// StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/login", loginJson, "Signed in."));
SetBusy(false);
#else
if (!string.IsNullOrEmpty(AuthToken))
{
SetBusy(true);
StartCoroutine(ResumeSessionCoroutine());
}
yield return StartCoroutine(ResumeSessionCoroutine());
else
SetBusy(false);
#endif
}
@@ -231,13 +272,21 @@ public class LoginManager : MonoBehaviour
if (_authInProgress) return;
string username = usernameInputRegister.text;
string email = emailInputRegister != null ? emailInputRegister.text : "";
string password = passwordInputRegister.text;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
{
error_txt.text = "Please fill in all fields";
return;
}
email = email.Trim().ToLowerInvariant();
if (!IsValidEmailFormat(email))
{
error_txt.text = "Please enter a valid email address";
return;
}
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
{
error_txt.text = "Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters";
@@ -250,11 +299,36 @@ public class LoginManager : MonoBehaviour
return;
}
// Server register endpoint accepts username + password only (email field is optional UI-only).
string json = "{\"username\":\"" + EscapeJsonString(username) + "\",\"password\":\"" + EscapeJsonString(password) + "\"}";
string json = "{\"username\":\"" + EscapeJsonString(username) + "\",\"email\":\"" + EscapeJsonString(email) + "\",\"password\":\"" + EscapeJsonString(password) + "\"}";
StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/register", json, "Account created."));
}
static bool IsValidEmailFormat(string email)
{
if (string.IsNullOrEmpty(email))
return false;
int at = email.IndexOf('@');
if (at <= 0)
return false;
if (email.IndexOf('@', at + 1) >= 0)
return false;
string domain = email.Substring(at + 1);
int dot = domain.LastIndexOf('.');
if (dot <= 0 || dot >= domain.Length - 1)
return false;
for (int i = 0; i < email.Length; i++)
{
if (char.IsWhiteSpace(email[i]))
return false;
}
return true;
}
static string EscapeJsonString(string s)
{
if (string.IsNullOrEmpty(s)) return s;
@@ -325,6 +399,149 @@ public class LoginManager : MonoBehaviour
SetBusy(false);
}
void HideServerMessagePanels()
{
if (serverUpdatePanel != null) serverUpdatePanel.SetActive(false);
if (errorPanel != null) errorPanel.SetActive(false);
if (warningPanel != null) warningPanel.SetActive(false);
}
void OnServerUpdateClick()
{
if (!string.IsNullOrWhiteSpace(telegramUpdateChannelUrl))
Application.OpenURL(telegramUpdateChannelUrl.Trim());
}
void OnServerErrorCloseClick()
{
#if UNITY_EDITOR
EditorApplication.ExitPlaymode();
#else
Application.Quit();
#endif
}
/// <summary>
/// GET <c>/table_settings</c> (public) and applies version / error / warning gates.
/// Callback receives <c>true</c> when login may continue to the main menu.
/// </summary>
IEnumerator CoApplyTableSettingsGate(Action<bool> onComplete)
{
CupidTableSettings table = null;
yield return StartCoroutine(FetchTableSettingsCoroutine(t => table = t));
if (table == null)
{
onComplete?.Invoke(true);
yield break;
}
if (IsClientBelowRequiredVersion(table.version))
{
if (serverUpdatePanel != null)
serverUpdatePanel.SetActive(true);
onComplete?.Invoke(false);
yield break;
}
if (!string.IsNullOrWhiteSpace(table.error_msg))
{
if (serverErrorMessageTxt != null)
serverErrorMessageTxt.text = table.error_msg.Trim();
if (errorPanel != null)
errorPanel.SetActive(true);
onComplete?.Invoke(false);
yield break;
}
if (!string.IsNullOrWhiteSpace(table.warning_msg))
yield return CoWaitForWarningDismiss(table.warning_msg.Trim());
onComplete?.Invoke(true);
}
IEnumerator FetchTableSettingsCoroutine(Action<CupidTableSettings> onResult)
{
string url = AuthBaseUrl + "/table_settings";
using (var request = UnityWebRequest.Get(url))
{
request.downloadHandler = new DownloadHandlerBuffer();
yield return request.SendWebRequest();
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
if (request.result != UnityWebRequest.Result.Success &&
request.result != UnityWebRequest.Result.ProtocolError)
{
onResult?.Invoke(null);
yield break;
}
if (request.responseCode < 200 || request.responseCode >= 300 || string.IsNullOrWhiteSpace(text))
{
onResult?.Invoke(null);
yield break;
}
try
{
CupidTableSettings parsed = JsonUtility.FromJson<CupidTableSettings>(text);
onResult?.Invoke(parsed);
}
catch (Exception e)
{
Logger.Log("table_settings JSON error: " + e.Message + " body=" + text);
onResult?.Invoke(null);
}
}
}
static bool IsClientBelowRequiredVersion(string requiredVersion)
{
if (string.IsNullOrWhiteSpace(requiredVersion))
return false;
if (!TryParseVersionFloat(requiredVersion, out float required))
return false;
if (!TryParseVersionFloat(Application.version, out float client))
return false;
return client < required;
}
static bool TryParseVersionFloat(string version, out float value)
{
value = 0f;
if (string.IsNullOrWhiteSpace(version))
return false;
return float.TryParse(version.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value);
}
IEnumerator CoWaitForWarningDismiss(string message)
{
if (serverWarningMessageTxt != null)
serverWarningMessageTxt.text = message;
if (warningPanel != null)
warningPanel.SetActive(true);
bool dismissed = false;
void OnDismiss()
{
dismissed = true;
if (warningPanel != null)
warningPanel.SetActive(false);
}
if (btnWarningClose != null)
{
btnWarningClose.onClick.RemoveListener(OnDismiss);
btnWarningClose.onClick.AddListener(OnDismiss);
}
yield return new WaitUntil(() => dismissed);
if (btnWarningClose != null)
btnWarningClose.onClick.RemoveListener(OnDismiss);
}
static void ApplyUserData(UserData u)
{
_currentUser = u;