using System.Net.WebSockets; using DisplayCommands; using TanksServer.Graphics; namespace TanksServer.Interactivity; internal sealed class ClientScreenServerConnection( WebSocket webSocket, ILogger logger, string? playerName = null ) : WebsocketServerConnection(logger, new ByteChannelWebSocket(webSocket, logger, 0)) { private bool _wantsFrameOnTick = true; private PixelGrid? _lastSentPixels; private PixelGrid? _nextPixels; private readonly PlayerScreenData? _nextPlayerData = playerName != null ? new PlayerScreenData(logger) : null; protected override async ValueTask HandleMessageLockedAsync(Memory _) { if (_nextPixels == null) { _wantsFrameOnTick = true; return; } await SendNowAsync(); } public ValueTask OnGameTickAsync(PixelGrid pixels, GamePixelGrid gamePixelGrid) => LockedAsync(async () => { if (pixels == _lastSentPixels) return; if (_nextPlayerData != null) { _nextPlayerData.Clear(); foreach (var gamePixel in gamePixelGrid) { if (!gamePixel.EntityType.HasValue) continue; _nextPlayerData.Add(gamePixel.EntityType.Value, gamePixel.BelongsTo?.Name == playerName); } } _nextPixels = pixels; if (_wantsFrameOnTick) _ = await SendNowAsync(); }); private async ValueTask SendNowAsync() { var pixels = _nextPixels ?? throw new InvalidOperationException("next pixels not set"); try { await Socket.SendBinaryAsync(pixels.Data, _nextPlayerData == null); if (_nextPlayerData != null) await Socket.SendBinaryAsync(_nextPlayerData.GetPacket()); _lastSentPixels = _nextPixels; _nextPixels = null; _wantsFrameOnTick = false; return true; } catch (WebSocketException ex) { Logger.LogWarning(ex, "send failed"); return false; } } }