230 lines
9.1 KiB
C#
230 lines
9.1 KiB
C#
using Cysharp.Threading.Tasks;
|
|
using System;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
using VContainer;
|
|
|
|
namespace BulletHellTemplate
|
|
{
|
|
/// <summary>
|
|
/// Handles player authentication (login / register) and
|
|
/// shows translated status feedback based on server responses.
|
|
/// </summary>
|
|
[DisallowMultipleComponent]
|
|
public sealed class AuthManager : MonoBehaviour
|
|
{
|
|
/*────────── UI References ──────────*/
|
|
[Header("Input Fields")]
|
|
public TMP_InputField loginEmailInputField;
|
|
public TMP_InputField loginPasswordInputField;
|
|
public TMP_InputField createEmailInputField;
|
|
public TMP_InputField createPasswordInputField;
|
|
public TMP_InputField createPasswordConfirmInputField;
|
|
|
|
[Header("Screens")]
|
|
public GameObject UILogin;
|
|
public GameObject UICreateAccount;
|
|
|
|
[Header("Loading UI")]
|
|
public GameObject UILoading;
|
|
public TextMeshProUGUI loadingText;
|
|
|
|
[Header("Settings")]
|
|
[Tooltip("Scene loaded after successful authentication.")]
|
|
public string HomeSceneName = "Home";
|
|
|
|
[Header("Status Text")]
|
|
[Tooltip("UI text element used to show status or error messages.")]
|
|
public TextMeshProUGUI statusText;
|
|
|
|
/*────────── Translated Errors ──────────*/
|
|
[Tooltip("Invalid e-mail address.")]
|
|
public NameTranslatedByLanguage[] invalidEmailError;
|
|
[Tooltip("Password confirmation does not match.")]
|
|
public NameTranslatedByLanguage[] passwordMismatchError;
|
|
[Tooltip("Password is too short.")]
|
|
public NameTranslatedByLanguage[] passwordTooShortError;
|
|
[Tooltip("Password is too long.")]
|
|
public NameTranslatedByLanguage[] passwordTooLongError;
|
|
[Tooltip("Password needs at least one uppercase letter.")]
|
|
public NameTranslatedByLanguage[] passwordMissingUppercaseError;
|
|
[Tooltip("Password needs at least one lowercase letter.")]
|
|
public NameTranslatedByLanguage[] passwordMissingLowercaseError;
|
|
[Tooltip("Password needs at least one number.")]
|
|
public NameTranslatedByLanguage[] passwordMissingNumberError;
|
|
[Tooltip("Password needs at least one special character.")]
|
|
public NameTranslatedByLanguage[] passwordMissingSpecialCharError;
|
|
[Tooltip("E-mail already registered.")]
|
|
public NameTranslatedByLanguage[] emailAlreadyInUseError;
|
|
[Tooltip("Credentials are incorrect.")]
|
|
public NameTranslatedByLanguage[] invalidCredentialsError;
|
|
[Tooltip("Generic or unknown error.")]
|
|
public NameTranslatedByLanguage[] genericError;
|
|
|
|
/*────────── Internals ──────────*/
|
|
private IBackendService Backend => BackendManager.Service;
|
|
|
|
[Inject] private BackendSettings backendSettings;
|
|
|
|
/*────────── Unity Lifecycle ──────────*/
|
|
private async void Start()
|
|
{
|
|
ToggleLoading(true, "Initializing backend…");
|
|
await BackendManager.CheckInitialized();
|
|
|
|
if (BackendManager.Service is not OfflineBackendService)
|
|
{
|
|
SecurePrefs.ClearSecurePrefsData();
|
|
}
|
|
else
|
|
{
|
|
PlayerSave.LoadAllPurchased();
|
|
}
|
|
|
|
loadingText.text = LanguageManager.LanguageManager.Instance.GetTextEntryByID("loading_loading");
|
|
Debug.Log("[AUTH MANAGER] trying auto-login…");
|
|
|
|
if (await Backend.TryAutoLoginAsync())
|
|
{
|
|
SceneManager.LoadScene(HomeSceneName);
|
|
return;
|
|
}
|
|
await BackendManager.Service.Logout();
|
|
ToggleLoading(false);
|
|
ClearStatus();
|
|
UILogin.SetActive(true);
|
|
}
|
|
|
|
|
|
/*────────── UI Callbacks ──────────*/
|
|
public void OnLoginWithEmailButton() =>
|
|
SafeFire(async () =>
|
|
{
|
|
ClearStatus();
|
|
ToggleLoading(true, "Verifying credentials…");
|
|
|
|
var res = await Backend.AccountLogin(
|
|
loginEmailInputField.text.Trim(),
|
|
loginPasswordInputField.text);
|
|
|
|
await HandleResult(res, "Login successful!");
|
|
});
|
|
|
|
public void OnPlayAsGuestButton() =>
|
|
SafeFire(async () =>
|
|
{
|
|
ClearStatus();
|
|
ToggleLoading(true, "Joining as guest…");
|
|
|
|
var res = await Backend.PlayAsGuest();
|
|
await HandleResult(res, "Welcome!");
|
|
});
|
|
|
|
public void OnCreateAccountButton() =>
|
|
SafeFire(async () =>
|
|
{
|
|
ClearStatus();
|
|
ToggleLoading(true, "Creating account…");
|
|
|
|
var res = await Backend.AccountRegister(
|
|
createEmailInputField.text.Trim(),
|
|
createPasswordInputField.text,
|
|
createPasswordConfirmInputField.text);
|
|
|
|
await HandleResult(res, "Account created!");
|
|
});
|
|
|
|
private async void SafeFire(Func<UniTask> action)
|
|
{
|
|
try { await action(); }
|
|
catch (Exception ex)
|
|
{
|
|
ToggleLoading(false);
|
|
statusText.text = $"<color=#ff6666>{ex.Message}</color>";
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
|
|
public void OnOpenCreateAccount()
|
|
{
|
|
ClearStatus();
|
|
UILogin.SetActive(false);
|
|
UICreateAccount.SetActive(true);
|
|
}
|
|
|
|
public void OnCancelCreateAccount()
|
|
{
|
|
ClearStatus();
|
|
UICreateAccount.SetActive(false);
|
|
UILogin.SetActive(true);
|
|
}
|
|
|
|
/*────────── Helpers ──────────*/
|
|
private void ToggleLoading(bool visible, string msg = "")
|
|
{
|
|
UILoading.SetActive(visible);
|
|
loadingText.text = msg;
|
|
statusText.gameObject.SetActive(!visible);
|
|
UILogin.SetActive(!visible && !UICreateAccount.activeSelf);
|
|
UICreateAccount.SetActive(!visible && UICreateAccount.activeSelf);
|
|
}
|
|
|
|
private void ClearStatus() => statusText.text = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Central result handler.
|
|
/// </summary>
|
|
private async UniTask HandleResult(RequestResult res, string okMsg)
|
|
{
|
|
if (res.Success)
|
|
{
|
|
loadingText.text = okMsg;
|
|
await UniTask.Delay(600);
|
|
SceneManager.LoadScene(HomeSceneName);
|
|
}
|
|
else
|
|
{
|
|
ToggleLoading(false);
|
|
statusText.text = $"<color=#ff6666>{ParseReason(res.Reason)}</color>";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts backend error codes to translated human-readable messages.
|
|
/// </summary>
|
|
private string ParseReason(string reason) => reason switch
|
|
{
|
|
"INVALID_EMAIL" => GetTranslatedString(invalidEmailError, "Invalid email address."),
|
|
"PASSWORD_MISMATCH" => GetTranslatedString(passwordMismatchError, "Password confirmation does not match."),
|
|
"PASSWORD_TOO_SHORT" => GetTranslatedString(passwordTooShortError, "Password is too short."),
|
|
"PASSWORD_TOO_LONG" => GetTranslatedString(passwordTooLongError, "Password is too long."),
|
|
"PASSWORD_MISSING_UPPERCASE" => GetTranslatedString(passwordMissingUppercaseError, "Password needs an uppercase letter."),
|
|
"PASSWORD_MISSING_LOWERCASE" => GetTranslatedString(passwordMissingLowercaseError, "Password needs a lowercase letter."),
|
|
"PASSWORD_MISSING_NUMBER" => GetTranslatedString(passwordMissingNumberError, "Password needs a number."),
|
|
"PASSWORD_MISSING_SPECIAL_CHAR" => GetTranslatedString(passwordMissingSpecialCharError, "Password needs a special character."),
|
|
"EMAIL_ALREADY_IN_USE" => GetTranslatedString(emailAlreadyInUseError, "Email already registered."),
|
|
"invalid_credentials" => GetTranslatedString(invalidCredentialsError, "Incorrect credentials."),
|
|
_ => GetTranslatedString(genericError, $"Error: {reason}")
|
|
};
|
|
|
|
/// <summary>
|
|
/// Returns the translation matching the current language; falls back to
|
|
/// <paramref name="fallback"/> when no translation is found.
|
|
/// </summary>
|
|
private string GetTranslatedString(NameTranslatedByLanguage[] translations, string fallback)
|
|
{
|
|
string currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage();
|
|
if (translations != null)
|
|
{
|
|
foreach (var trans in translations)
|
|
if (!string.IsNullOrEmpty(trans.LanguageId)
|
|
&& trans.LanguageId.Equals(currentLang)
|
|
&& !string.IsNullOrEmpty(trans.Translate))
|
|
return trans.Translate;
|
|
}
|
|
return fallback;
|
|
}
|
|
}
|
|
}
|