133 lines
4.4 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|