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("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(); } } }