250 lines
9.0 KiB
C#
250 lines
9.0 KiB
C#
using System;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using Vanguard.Api;
|
||
using Vanguard.App;
|
||
using Vanguard.Auth;
|
||
|
||
namespace Vanguard.UI
|
||
{
|
||
/// <summary>Экран входа/регистрации и выбора адреса сервера.</summary>
|
||
public sealed class LoginView
|
||
{
|
||
private readonly AppConfig _config;
|
||
private readonly AuthService _auth;
|
||
private readonly Action _onAuthenticated;
|
||
|
||
private readonly RectTransform _root;
|
||
private readonly InputField _serverUrl;
|
||
private readonly InputField _userName;
|
||
private readonly InputField _password;
|
||
private readonly Button _loginButton;
|
||
private readonly Button _registerButton;
|
||
private readonly Text _status;
|
||
private bool _busy;
|
||
|
||
public LoginView(
|
||
Transform parent,
|
||
AppConfig config,
|
||
AuthService auth,
|
||
Action onAuthenticated)
|
||
{
|
||
_config = config;
|
||
_auth = auth;
|
||
_onAuthenticated = onAuthenticated;
|
||
|
||
_root = UiKit.CreatePanel(parent, "LoginScreen", new Color(0.01f, 0.02f, 0.04f, 0.28f));
|
||
|
||
// Мягкое неоновое свечение за панелью; 3D-стол остаётся виден по краям.
|
||
UiKit.CreateCenteredCard(
|
||
_root,
|
||
"LoginGlow",
|
||
new Vector2(760f, 900f),
|
||
new Color(0.04f, 0.22f, 0.80f, 0.12f));
|
||
|
||
var card = UiKit.CreateCenteredCard(
|
||
_root,
|
||
"LoginCard",
|
||
new Vector2(620f, 820f),
|
||
new Color(0.018f, 0.032f, 0.065f, 0.92f));
|
||
|
||
var layout = UiKit.AddVerticalLayout(card, 32, 10);
|
||
layout.childAlignment = TextAnchor.MiddleCenter;
|
||
|
||
var eyebrow = UiKit.CreateText(
|
||
card, "Eyebrow", "VANGUARD // NETWORK", 13,
|
||
new Color(0.35f, 0.72f, 1f));
|
||
UiKit.SetPreferredHeight(eyebrow, 20f);
|
||
|
||
var title = UiKit.CreateText(
|
||
card, "Title", "VANGUARD", 56, new Color(0.88f, 0.94f, 1f));
|
||
title.fontStyle = FontStyle.Bold;
|
||
UiKit.SetPreferredHeight(title, 66f);
|
||
|
||
var subtitle = UiKit.CreateText(
|
||
card, "Subtitle", "RE:MASTER · СЕТЕВОЙ КЛИЕНТ", 18,
|
||
new Color(0.52f, 0.64f, 0.86f));
|
||
UiKit.SetPreferredHeight(subtitle, 30f);
|
||
|
||
var accent = UiKit.CreatePanel(card, "Accent", new Color(0.12f, 0.52f, 1f, 0.9f));
|
||
UiKit.SetPreferredHeight(accent, 3f);
|
||
|
||
AddCaption(card, "Сервер");
|
||
_serverUrl = UiKit.CreateInput(card, "ServerUrl", "http://192.168.31.158:18080");
|
||
_serverUrl.text = _config.ServerBaseUrl;
|
||
UiKit.SetPreferredHeight(_serverUrl, 54f);
|
||
|
||
AddCaption(card, "Имя игрока");
|
||
_userName = UiKit.CreateInput(card, "UserName", "3–32 символа");
|
||
UiKit.SetPreferredHeight(_userName, 54f);
|
||
|
||
AddCaption(card, "Пароль");
|
||
_password = UiKit.CreateInput(card, "Password", "8–128 символов", password: true);
|
||
UiKit.SetPreferredHeight(_password, 54f);
|
||
|
||
_loginButton = UiKit.CreateButton(card, "LoginButton", "ВОЙТИ", OnLoginClicked, 22);
|
||
TintButton(_loginButton, new Color(0.10f, 0.42f, 0.95f, 1f), Color.white);
|
||
UiKit.SetPreferredHeight(_loginButton, 56f);
|
||
|
||
_registerButton = UiKit.CreateButton(
|
||
card, "RegisterButton", "СОЗДАТЬ АККАУНТ", OnRegisterClicked, 19);
|
||
TintButton(
|
||
_registerButton,
|
||
new Color(0.05f, 0.10f, 0.18f, 0.95f),
|
||
new Color(0.48f, 0.76f, 1f));
|
||
UiKit.SetPreferredHeight(_registerButton, 48f);
|
||
|
||
_status = UiKit.CreateText(
|
||
card, "Status", string.Empty, 17,
|
||
new Color(0.66f, 0.74f, 0.90f),
|
||
TextAnchor.UpperCenter);
|
||
_status.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||
UiKit.SetPreferredHeight(_status, 64f);
|
||
|
||
var footer = UiKit.CreateText(
|
||
card,
|
||
"Footer",
|
||
"v" + Application.version + " · стенд 192.168.31.158:18080 · localhost:5080",
|
||
14,
|
||
new Color(0.38f, 0.46f, 0.62f));
|
||
UiKit.SetPreferredHeight(footer, 24f);
|
||
}
|
||
|
||
public void Show(string status = null)
|
||
{
|
||
UiKit.SetActive(_root, true);
|
||
if (!string.IsNullOrEmpty(status))
|
||
SetStatus(status, new Color(0.70f, 0.75f, 0.86f));
|
||
}
|
||
|
||
public void Hide() => UiKit.SetActive(_root, false);
|
||
|
||
private static void AddCaption(Transform parent, string text)
|
||
{
|
||
var caption = UiKit.CreateText(
|
||
parent,
|
||
"Caption_" + text,
|
||
text,
|
||
16,
|
||
new Color(0.55f, 0.65f, 0.82f),
|
||
TextAnchor.MiddleLeft);
|
||
UiKit.SetPreferredHeight(caption, 22f);
|
||
}
|
||
|
||
private static void TintButton(Button button, Color background, Color label)
|
||
{
|
||
var image = button.GetComponent<Image>();
|
||
if (image != null)
|
||
image.color = background;
|
||
|
||
var text = button.GetComponentInChildren<Text>();
|
||
if (text != null)
|
||
text.color = label;
|
||
}
|
||
|
||
private async void OnLoginClicked()
|
||
{
|
||
await SubmitAsync(register: false);
|
||
}
|
||
|
||
private async void OnRegisterClicked()
|
||
{
|
||
await SubmitAsync(register: true);
|
||
}
|
||
|
||
private async Task SubmitAsync(bool register)
|
||
{
|
||
if (_busy)
|
||
return;
|
||
|
||
var userName = (_userName.text ?? string.Empty).Trim();
|
||
var password = _password.text ?? string.Empty;
|
||
|
||
if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
|
||
{
|
||
SetStatus("Введите имя игрока и пароль.", new Color(1f, 0.70f, 0.35f));
|
||
return;
|
||
}
|
||
|
||
_config.SetServerBaseUrl(_serverUrl.text);
|
||
_serverUrl.text = _config.ServerBaseUrl;
|
||
|
||
SetBusy(true, register ? "Создаём аккаунт…" : "Подключаемся…");
|
||
|
||
try
|
||
{
|
||
if (register)
|
||
await _auth.RegisterAsync(userName, password);
|
||
else
|
||
await _auth.LoginAsync(userName, password);
|
||
|
||
_password.text = string.Empty;
|
||
SetStatus("Готово.", new Color(0.45f, 0.85f, 0.55f));
|
||
_onAuthenticated();
|
||
}
|
||
catch (ApiException exception)
|
||
{
|
||
SetStatus(FormatError(exception), new Color(1f, 0.55f, 0.45f));
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
Debug.LogException(exception);
|
||
var detail = exception.Message ?? string.Empty;
|
||
if (detail.IndexOf("Insecure connection", StringComparison.OrdinalIgnoreCase) >= 0)
|
||
{
|
||
SetStatus(
|
||
"Unity блокирует HTTP. Player Settings → Allow downloads over HTTP = Always, затем перезапусти Unity.",
|
||
new Color(1f, 0.55f, 0.45f));
|
||
}
|
||
else
|
||
{
|
||
SetStatus(
|
||
"Не удалось связаться: " + detail,
|
||
new Color(1f, 0.55f, 0.45f));
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
SetBusy(false, null);
|
||
}
|
||
}
|
||
|
||
private static string FormatError(ApiException exception)
|
||
{
|
||
if (exception.StatusCode == 0)
|
||
return "Нет связи с сервером. Проверьте адрес и запущен ли сервер.";
|
||
|
||
if (exception.Violations.Count > 0)
|
||
{
|
||
var details = string.Join("\n", exception.Violations.Take(3).Select(item => item.Message));
|
||
return exception.Message + "\n" + details;
|
||
}
|
||
|
||
return string.IsNullOrEmpty(exception.Message)
|
||
? "Сервер вернул ошибку " + exception.StatusCode + "."
|
||
: exception.Message;
|
||
}
|
||
|
||
private void SetBusy(bool busy, string status)
|
||
{
|
||
_busy = busy;
|
||
_loginButton.interactable = !busy;
|
||
_registerButton.interactable = !busy;
|
||
_serverUrl.interactable = !busy;
|
||
_userName.interactable = !busy;
|
||
_password.interactable = !busy;
|
||
|
||
if (status != null)
|
||
SetStatus(status, new Color(0.70f, 0.75f, 0.86f));
|
||
}
|
||
|
||
private void SetStatus(string text, Color color)
|
||
{
|
||
_status.text = text ?? string.Empty;
|
||
_status.color = color;
|
||
}
|
||
}
|
||
}
|