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
{
/// Экран входа/регистрации и выбора адреса сервера.
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);
var language = UiKit.CreateLanguageButton(_root, "Language");
var languageRect = language.GetComponent();
languageRect.anchorMin = new Vector2(1f, 1f);
languageRect.anchorMax = new Vector2(1f, 1f);
languageRect.pivot = new Vector2(1f, 1f);
languageRect.anchoredPosition = new Vector2(-24f, -24f);
languageRect.sizeDelta = new Vector2(72f, 40f);
}
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();
if (image != null)
image.color = background;
var text = button.GetComponentInChildren();
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)
{
if (_loginButton == null || _registerButton == null || _serverUrl == null
|| _userName == null || _password == null)
return;
_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)
{
if (_status == null)
return;
_status.text = text ?? string.Empty;
_status.color = color;
}
}
}