diff --git a/tanks-backend/TanksServer/Interactivity/ClientScreenServerConnection.cs b/tanks-backend/TanksServer/Interactivity/ClientScreenServerConnection.cs index 1294934..588ef57 100644 --- a/tanks-backend/TanksServer/Interactivity/ClientScreenServerConnection.cs +++ b/tanks-backend/TanksServer/Interactivity/ClientScreenServerConnection.cs @@ -5,42 +5,19 @@ using TanksServer.Graphics; namespace TanksServer.Interactivity; -internal sealed class ClientScreenServerConnection : IWebsocketServerConnection, IDisposable +internal sealed class ClientScreenServerConnection( + WebSocket webSocket, + ILogger logger, + TimeSpan minFrameTime, + Guid? playerGuid = null +) : WebsocketServerConnection(logger, new ByteChannelWebSocket(webSocket, logger, 0)), + IDisposable { - private readonly ByteChannelWebSocket _channel; - private readonly ILogger _logger; private readonly SemaphoreSlim _wantedFrames = new(1); - private readonly Guid? _playerGuid; - private readonly PlayerScreenData? _playerScreenData; - private readonly TimeSpan _minFrameTime; - + private readonly PlayerScreenData? _playerScreenData = playerGuid.HasValue ? new PlayerScreenData(logger) : null; private DateTime _nextFrameAfter = DateTime.Now; - public ClientScreenServerConnection( - WebSocket webSocket, - ILogger logger, - TimeSpan minFrameTime, - Guid? playerGuid = null - ) - { - _logger = logger; - _minFrameTime = minFrameTime; - - _playerGuid = playerGuid; - if (playerGuid.HasValue) - _playerScreenData = new PlayerScreenData(logger); - - _channel = new ByteChannelWebSocket(webSocket, logger, 0); - Done = ReceiveAsync(); - } - - public Task Done { get; } - - public void Dispose() - { - _wantedFrames.Dispose(); - Done.Dispose(); - } + public void Dispose() => _wantedFrames.Dispose(); public async Task SendAsync(PixelGrid pixels, GamePixelGrid gamePixelGrid) { @@ -49,26 +26,26 @@ internal sealed class ClientScreenServerConnection : IWebsocketServerConnection, if (!await _wantedFrames.WaitAsync(TimeSpan.Zero)) { - _logger.LogTrace("client does not want a frame yet"); + logger.LogTrace("client does not want a frame yet"); return; } - _nextFrameAfter = DateTime.Today + _minFrameTime; + _nextFrameAfter = DateTime.Today + minFrameTime; if (_playerScreenData != null) RefreshPlayerSpecificData(gamePixelGrid); - _logger.LogTrace("sending"); + logger.LogTrace("sending"); try { - _logger.LogTrace("sending {} bytes of pixel data", pixels.Data.Length); - await _channel.SendAsync(pixels.Data, _playerScreenData == null); + logger.LogTrace("sending {} bytes of pixel data", pixels.Data.Length); + await Socket.SendAsync(pixels.Data, _playerScreenData == null); if (_playerScreenData != null) - await _channel.SendAsync(_playerScreenData.GetPacket()); + await Socket.SendAsync(_playerScreenData.GetPacket()); } catch (WebSocketException ex) { - _logger.LogWarning(ex, "send failed"); + logger.LogWarning(ex, "send failed"); } } @@ -80,20 +57,9 @@ internal sealed class ClientScreenServerConnection : IWebsocketServerConnection, { if (!gamePixel.EntityType.HasValue) continue; - _playerScreenData.Add(gamePixel.EntityType.Value, gamePixel.BelongsTo?.Id == _playerGuid); + _playerScreenData.Add(gamePixel.EntityType.Value, gamePixel.BelongsTo?.Id == playerGuid); } } - private async Task ReceiveAsync() - { - await foreach (var _ in _channel.ReadAllAsync()) - _wantedFrames.Release(); - _logger.LogTrace("done receiving"); - } - - public Task CloseAsync() - { - _logger.LogDebug("closing connection"); - return _channel.CloseAsync(); - } + protected override void HandleMessage(Memory _) => _wantedFrames.Release(); } diff --git a/tanks-backend/TanksServer/Interactivity/ControlsServer.cs b/tanks-backend/TanksServer/Interactivity/ControlsServer.cs index aa1fc9b..78c88bd 100644 --- a/tanks-backend/TanksServer/Interactivity/ControlsServer.cs +++ b/tanks-backend/TanksServer/Interactivity/ControlsServer.cs @@ -13,7 +13,7 @@ internal sealed class ControlsServer( var clientLogger = loggerFactory.CreateLogger(); var sock = new ControlsServerConnection(ws, clientLogger, player); await AddConnection(sock); - await sock.Done; + await sock.ReceiveAsync(); await RemoveConnection(sock); } } diff --git a/tanks-backend/TanksServer/Interactivity/ControlsServerConnection.cs b/tanks-backend/TanksServer/Interactivity/ControlsServerConnection.cs index e30095a..8a178b1 100644 --- a/tanks-backend/TanksServer/Interactivity/ControlsServerConnection.cs +++ b/tanks-backend/TanksServer/Interactivity/ControlsServerConnection.cs @@ -2,65 +2,12 @@ using System.Net.WebSockets; namespace TanksServer.Interactivity; -internal sealed class ControlsServerConnection : IWebsocketServerConnection +internal sealed class ControlsServerConnection( + WebSocket socket, + ILogger logger, + Player player +) : WebsocketServerConnection(logger, new ByteChannelWebSocket(socket, logger, 2)) { - private readonly ByteChannelWebSocket _binaryWebSocket; - private readonly ILogger _logger; - private readonly Player _player; - - public ControlsServerConnection(WebSocket socket, ILogger logger, Player player) - { - _logger = logger; - _player = player; - _binaryWebSocket = new ByteChannelWebSocket(socket, logger, 2); - Done = ReceiveAsync(); - } - - public Task Done { get; } - - private async Task ReceiveAsync() - { - await foreach (var buffer in _binaryWebSocket.ReadAllAsync()) - { - var type = (MessageType)buffer.Span[0]; - var control = (InputType)buffer.Span[1]; - - _logger.LogTrace("player input {} {} {}", _player.Id, type, control); - - var isEnable = type switch - { - MessageType.Enable => true, - MessageType.Disable => false, - _ => throw new ArgumentException("invalid message type") - }; - - _player.LastInput = DateTime.Now; - - switch (control) - { - case InputType.Forward: - _player.Controls.Forward = isEnable; - break; - case InputType.Backward: - _player.Controls.Backward = isEnable; - break; - case InputType.Left: - _player.Controls.TurnLeft = isEnable; - break; - case InputType.Right: - _player.Controls.TurnRight = isEnable; - break; - case InputType.Shoot: - _player.Controls.Shoot = isEnable; - break; - default: - throw new ArgumentException("invalid control type"); - } - } - } - - public Task CloseAsync() => _binaryWebSocket.CloseAsync(); - private enum MessageType : byte { Enable = 0x01, @@ -75,4 +22,42 @@ internal sealed class ControlsServerConnection : IWebsocketServerConnection Right = 0x04, Shoot = 0x05 } + + protected override void HandleMessage(Memory buffer) + { + var type = (MessageType)buffer.Span[0]; + var control = (InputType)buffer.Span[1]; + + logger.LogTrace("player input {} {} {}", player.Id, type, control); + + var isEnable = type switch + { + MessageType.Enable => true, + MessageType.Disable => false, + _ => throw new ArgumentException("invalid message type") + }; + + player.LastInput = DateTime.Now; + + switch (control) + { + case InputType.Forward: + player.Controls.Forward = isEnable; + break; + case InputType.Backward: + player.Controls.Backward = isEnable; + break; + case InputType.Left: + player.Controls.TurnLeft = isEnable; + break; + case InputType.Right: + player.Controls.TurnRight = isEnable; + break; + case InputType.Shoot: + player.Controls.Shoot = isEnable; + break; + default: + throw new ArgumentException("invalid control type"); + } + } } diff --git a/tanks-backend/TanksServer/Interactivity/IWebsocketServerConnection.cs b/tanks-backend/TanksServer/Interactivity/IWebsocketServerConnection.cs deleted file mode 100644 index 10210cb..0000000 --- a/tanks-backend/TanksServer/Interactivity/IWebsocketServerConnection.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace TanksServer.Interactivity; - -internal interface IWebsocketServerConnection -{ - Task CloseAsync(); - - Task Done { get; } -} diff --git a/tanks-backend/TanksServer/Interactivity/PlayerScreenData.cs b/tanks-backend/TanksServer/Interactivity/PlayerScreenData.cs index e39fe4c..f79e7a8 100644 --- a/tanks-backend/TanksServer/Interactivity/PlayerScreenData.cs +++ b/tanks-backend/TanksServer/Interactivity/PlayerScreenData.cs @@ -7,7 +7,7 @@ namespace TanksServer.Interactivity; internal sealed class PlayerScreenData(ILogger logger) { private readonly Memory _data = new byte[MapService.PixelsPerRow * MapService.PixelsPerColumn / 2]; - private int _count = 0; + private int _count; public void Clear() { diff --git a/tanks-backend/TanksServer/Interactivity/WebsocketServer.cs b/tanks-backend/TanksServer/Interactivity/WebsocketServer.cs index a4bd0eb..e86f0b0 100644 --- a/tanks-backend/TanksServer/Interactivity/WebsocketServer.cs +++ b/tanks-backend/TanksServer/Interactivity/WebsocketServer.cs @@ -2,10 +2,10 @@ using Microsoft.Extensions.Hosting; namespace TanksServer.Interactivity; -internal class WebsocketServer( +internal abstract class WebsocketServer( ILogger logger ) : IHostedLifecycleService, IDisposable - where T : IWebsocketServerConnection + where T : WebsocketServerConnection { private readonly SemaphoreSlim _mutex = new(1, 1); private bool _closing; @@ -56,7 +56,7 @@ internal class WebsocketServer( protected async Task HandleClientAsync(T connection) { await AddConnection(connection); - await connection.Done; + await connection.ReceiveAsync(); await RemoveConnection(connection); } diff --git a/tanks-backend/TanksServer/Interactivity/WebsocketServerConnection.cs b/tanks-backend/TanksServer/Interactivity/WebsocketServerConnection.cs new file mode 100644 index 0000000..891b1ba --- /dev/null +++ b/tanks-backend/TanksServer/Interactivity/WebsocketServerConnection.cs @@ -0,0 +1,23 @@ +namespace TanksServer.Interactivity; + +internal abstract class WebsocketServerConnection( + ILogger logger, + ByteChannelWebSocket socket) +{ + protected readonly ByteChannelWebSocket Socket = socket; + + public Task CloseAsync() + { + logger.LogDebug("closing connection"); + return Socket.CloseAsync(); + } + + public async Task ReceiveAsync() + { + await foreach (var buffer in Socket.ReadAllAsync()) + HandleMessage(buffer); + logger.LogTrace("done receiving"); + } + + protected abstract void HandleMessage(Memory buffer); +}