move more websocket logic into base classes
This commit is contained in:
parent
57c0d229f1
commit
fb675e59ff
|
@ -5,42 +5,19 @@ using TanksServer.Graphics;
|
|||
|
||||
namespace TanksServer.Interactivity;
|
||||
|
||||
internal sealed class ClientScreenServerConnection : IWebsocketServerConnection, IDisposable
|
||||
internal sealed class ClientScreenServerConnection(
|
||||
WebSocket webSocket,
|
||||
ILogger<ClientScreenServerConnection> logger,
|
||||
TimeSpan minFrameTime,
|
||||
Guid? playerGuid = null
|
||||
) : WebsocketServerConnection(logger, new ByteChannelWebSocket(webSocket, logger, 0)),
|
||||
IDisposable
|
||||
{
|
||||
private readonly ByteChannelWebSocket _channel;
|
||||
private readonly ILogger<ClientScreenServerConnection> _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<ClientScreenServerConnection> 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<byte> _) => _wantedFrames.Release();
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ internal sealed class ControlsServer(
|
|||
var clientLogger = loggerFactory.CreateLogger<ControlsServerConnection>();
|
||||
var sock = new ControlsServerConnection(ws, clientLogger, player);
|
||||
await AddConnection(sock);
|
||||
await sock.Done;
|
||||
await sock.ReceiveAsync();
|
||||
await RemoveConnection(sock);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,65 +2,12 @@ using System.Net.WebSockets;
|
|||
|
||||
namespace TanksServer.Interactivity;
|
||||
|
||||
internal sealed class ControlsServerConnection : IWebsocketServerConnection
|
||||
internal sealed class ControlsServerConnection(
|
||||
WebSocket socket,
|
||||
ILogger<ControlsServerConnection> logger,
|
||||
Player player
|
||||
) : WebsocketServerConnection(logger, new ByteChannelWebSocket(socket, logger, 2))
|
||||
{
|
||||
private readonly ByteChannelWebSocket _binaryWebSocket;
|
||||
private readonly ILogger<ControlsServerConnection> _logger;
|
||||
private readonly Player _player;
|
||||
|
||||
public ControlsServerConnection(WebSocket socket, ILogger<ControlsServerConnection> 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<byte> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
namespace TanksServer.Interactivity;
|
||||
|
||||
internal interface IWebsocketServerConnection
|
||||
{
|
||||
Task CloseAsync();
|
||||
|
||||
Task Done { get; }
|
||||
}
|
|
@ -7,7 +7,7 @@ namespace TanksServer.Interactivity;
|
|||
internal sealed class PlayerScreenData(ILogger logger)
|
||||
{
|
||||
private readonly Memory<byte> _data = new byte[MapService.PixelsPerRow * MapService.PixelsPerColumn / 2];
|
||||
private int _count = 0;
|
||||
private int _count;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
|
|
|
@ -2,10 +2,10 @@ using Microsoft.Extensions.Hosting;
|
|||
|
||||
namespace TanksServer.Interactivity;
|
||||
|
||||
internal class WebsocketServer<T>(
|
||||
internal abstract class WebsocketServer<T>(
|
||||
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<T>(
|
|||
protected async Task HandleClientAsync(T connection)
|
||||
{
|
||||
await AddConnection(connection);
|
||||
await connection.Done;
|
||||
await connection.ReceiveAsync();
|
||||
await RemoveConnection(connection);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<byte> buffer);
|
||||
}
|
Loading…
Reference in a new issue