Files
LittleSim/src/LittleSim.Web/Pages/Index.razor.cs
T

133 lines
4.4 KiB
C#

using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
namespace LittleSim.Web.Pages
{
public partial class Index
{
[Inject]
private NavigationManager Navigation { get; set; } = null!;
private LittleSimWebGame? _game;
private string _serverAddress = "";
private Uri? _pendingServer; // запрошен Connect до создания игры — подключим в первом тике
private ConnectionStatus _lastStatus = ConnectionStatus.Idle;
private int _lastCount = -1;
private int _frame;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
if (firstRender)
{
_serverAddress = DefaultServerAddress();
StateHasChanged();
JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
}
}
// Гонится из requestAnimationFrame (index.html). Игру создаём лениво в первом тике —
// мира она не создаёт, лишь ждёт адрес сервера из формы.
[JSInvokable]
public void TickDotNet()
{
if (_game == null)
{
_game = new LittleSimWebGame();
_game.Run();
}
_game.Tick();
if (_pendingServer is not null)
{
_game.Connect(_pendingServer);
_pendingServer = null;
}
// Перерисовываем оверлей только при смене статуса (или изредка — чтобы освежить счётчик),
// а не каждый кадр rAF.
var status = _game.Status;
var count = _game.EntityCount;
if (
status != _lastStatus
|| (status == ConnectionStatus.Connected && count != _lastCount && _frame % 15 == 0)
)
{
_lastStatus = status;
_lastCount = count;
InvokeAsync(StateHasChanged);
}
_frame++;
}
private void OnAddressKey(KeyboardEventArgs e)
{
if (e.Key == "Enter")
{
Connect();
}
}
private void Connect()
{
if (!Uri.TryCreate(_serverAddress?.Trim(), UriKind.Absolute, out var uri))
{
return; // адрес не похож на ws://… — игнорируем
}
if (_game is not null)
{
_game.Connect(uri);
}
else
{
_pendingServer = uri;
}
}
private string StatusText =>
_lastStatus switch
{
ConnectionStatus.Connecting => "подключение…",
ConnectionStatus.Connected => $"подключено · {_lastCount} жит.",
ConnectionStatus.Reconnecting => "переподключение…",
_ => "не подключено",
};
private string StatusClass =>
_lastStatus switch
{
ConnectionStatus.Connected => "ok",
ConnectionStatus.Connecting or ConnectionStatus.Reconnecting => "warn",
_ => "idle",
};
// Адрес по умолчанию: ?server=ws://host:port из URL страницы, иначе хост страницы на порту сервера.
private string DefaultServerAddress()
{
var page = new Uri(Navigation.Uri);
var query = page.Query.TrimStart('?');
foreach (var pair in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
{
var separator = pair.IndexOf('=');
if (separator > 0 && pair[..separator] == "server")
{
return Uri.UnescapeDataString(pair[(separator + 1)..]);
}
}
return new UriBuilder
{
Scheme = "ws",
Host = page.Host,
Port = WebNetSchema.DefaultPort,
}.Uri.ToString();
}
}
}