Separated WebSocketService to another service
This commit is contained in:
parent
ad0d8c7d0a
commit
fab3e3d13f
@ -6,7 +6,6 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using pacMan.Controllers;
|
||||
using pacMan.Game;
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Services;
|
||||
using pacMan.Utils;
|
||||
|
||||
@ -16,16 +15,16 @@ public class GameControllerTests
|
||||
{
|
||||
private IActionService _actionService = null!;
|
||||
private GameController _controller = null!;
|
||||
private IWebSocketService _webSocketService = null!;
|
||||
private GameService _gameService = null!;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_webSocketService = Substitute.For<WebSocketService>(Substitute.For<ILogger<WebSocketService>>());
|
||||
_gameService = Substitute.For<GameService>(Substitute.For<ILogger<GameService>>());
|
||||
_actionService = Substitute.For<ActionService>(
|
||||
Substitute.For<ILogger<ActionService>>(), _webSocketService
|
||||
Substitute.For<ILogger<ActionService>>(), _gameService
|
||||
);
|
||||
_controller = new GameController(Substitute.For<ILogger<GameController>>(), _webSocketService, _actionService);
|
||||
_controller = new GameController(Substitute.For<ILogger<GameController>>(), _gameService, _actionService);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -42,18 +41,14 @@ public class GameControllerTests
|
||||
var result = runMethod!.Invoke(_controller, new object[] { wssResult, data });
|
||||
|
||||
if (result is ArraySegment<byte> resultSegment)
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(resultSegment, Has.Count.EqualTo(data.Length));
|
||||
Assert.That(Encoding.UTF8.GetString(resultSegment.ToArray()), Is.EqualTo(data.GetString(data.Length)));
|
||||
});
|
||||
// TODO not working, works like a normal method call
|
||||
// _actionService.ReceivedWithAnyArgs().DoAction(default!);
|
||||
}
|
||||
// TODO not working, works like a normal method call
|
||||
// _actionService.ReceivedWithAnyArgs().DoAction(default!);
|
||||
else
|
||||
{
|
||||
Assert.Fail("Result is not an ArraySegment<byte>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using pacMan.Game;
|
||||
using pacMan.Game.Items;
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Services;
|
||||
|
||||
namespace BackendTests.Services;
|
||||
@ -16,13 +15,13 @@ public class ActionServiceTests
|
||||
|
||||
private readonly Player _whitePlayer = (Player)Players.Create("white");
|
||||
private ActionMessage _blackMessage = null!;
|
||||
private GameService _gameService = null!;
|
||||
private ActionMessage _redMessage = null!;
|
||||
private IActionService _service = null!;
|
||||
|
||||
private Queue<DirectionalPosition> _spawns = null!;
|
||||
|
||||
private ActionMessage _whiteMessage = null!;
|
||||
private IWebSocketService _wssSub = null!;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
@ -43,8 +42,8 @@ public class ActionServiceTests
|
||||
Action = GameAction.PlayerInfo,
|
||||
Data = JsonSerializer.Serialize(new { Player = _redPlayer, Spawns = CreateQueue() })
|
||||
};
|
||||
_wssSub = Substitute.For<WebSocketService>(Substitute.For<ILogger<WebSocketService>>());
|
||||
_service = new ActionService(Substitute.For<ILogger<ActionService>>(), _wssSub);
|
||||
_gameService = Substitute.For<GameService>(Substitute.For<ILogger<GameService>>());
|
||||
_service = new ActionService(Substitute.For<ILogger<ActionService>>(), _gameService);
|
||||
}
|
||||
|
||||
private static Queue<DirectionalPosition> CreateQueue() =>
|
||||
|
@ -3,7 +3,6 @@ using BackendTests.TestUtils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using pacMan.Game;
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Services;
|
||||
using pacMan.Utils;
|
||||
|
||||
@ -14,7 +13,7 @@ public class WebSocketServiceTests
|
||||
private readonly DirectionalPosition _spawn3By3Up = new()
|
||||
{ At = new Position { X = 3, Y = 3 }, Direction = Direction.Up };
|
||||
|
||||
private IWebSocketService _service = null!;
|
||||
private GameService _service = null!;
|
||||
|
||||
private Queue<DirectionalPosition> _spawns = null!;
|
||||
|
||||
@ -22,7 +21,7 @@ public class WebSocketServiceTests
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_service = new WebSocketService(Substitute.For<ILogger<WebSocketService>>());
|
||||
_service = new GameService(Substitute.For<ILogger<GameService>>());
|
||||
_spawns = new Queue<DirectionalPosition>(new[]
|
||||
{
|
||||
_spawn3By3Up,
|
||||
@ -40,7 +39,7 @@ public class WebSocketServiceTests
|
||||
var segment = "test".ToArraySegment();
|
||||
using var webSocket = Substitute.For<WebSocket>();
|
||||
|
||||
_service.Send(webSocket, segment);
|
||||
_service.Send(webSocket, segment).Wait();
|
||||
|
||||
webSocket.ReceivedWithAnyArgs().SendAsync(default, default, default, default);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.Net.WebSockets;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using pacMan.Game;
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Services;
|
||||
using pacMan.Utils;
|
||||
|
||||
@ -13,8 +12,8 @@ public class GameController : GenericController // TODO reconnect using player i
|
||||
{
|
||||
private readonly IActionService _actionService;
|
||||
|
||||
public GameController(ILogger<GameController> logger, IWebSocketService wsService, IActionService actionService) :
|
||||
base(logger, wsService) =>
|
||||
public GameController(ILogger<GameController> logger, GameService gameService, IActionService actionService) :
|
||||
base(logger, gameService) =>
|
||||
_actionService = actionService;
|
||||
|
||||
[HttpGet("connect")]
|
||||
@ -24,7 +23,7 @@ public class GameController : GenericController // TODO reconnect using player i
|
||||
public IEnumerable<GameGroup> GetAllGames()
|
||||
{
|
||||
Logger.Log(LogLevel.Information, "Returning all games");
|
||||
return WsService.Games;
|
||||
return GameService.Games;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
using System.Net.WebSockets;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Services;
|
||||
|
||||
namespace pacMan.Controllers;
|
||||
|
||||
public abstract class GenericController : ControllerBase
|
||||
public abstract class GenericController : ControllerBase // TODO only use WebSocketService in this class
|
||||
{
|
||||
private const int BufferSize = 1024 * 4;
|
||||
protected readonly GameService GameService;
|
||||
protected readonly ILogger<GenericController> Logger;
|
||||
protected readonly IWebSocketService WsService;
|
||||
private WebSocket? _webSocket;
|
||||
|
||||
protected GenericController(ILogger<GenericController> logger, IWebSocketService wsService)
|
||||
protected GenericController(ILogger<GenericController> logger, GameService gameService)
|
||||
{
|
||||
Logger = logger;
|
||||
WsService = wsService;
|
||||
GameService = gameService;
|
||||
Logger.Log(LogLevel.Debug, "WebSocket Controller created");
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ public abstract class GenericController : ControllerBase
|
||||
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
Logger.Log(LogLevel.Information, "WebSocket connection established to {}", HttpContext.Connection.Id);
|
||||
_webSocket = webSocket;
|
||||
WsService.Connections += WsServiceOnFire;
|
||||
GameService.Connections += WsServiceOnFire;
|
||||
await Echo();
|
||||
}
|
||||
else
|
||||
@ -37,7 +37,7 @@ public abstract class GenericController : ControllerBase
|
||||
private async Task WsServiceOnFire(ArraySegment<byte> segment)
|
||||
{
|
||||
if (_webSocket == null) return;
|
||||
await WsService.Send(_webSocket, segment);
|
||||
await GameService.Send(_webSocket, segment);
|
||||
}
|
||||
|
||||
|
||||
@ -50,16 +50,16 @@ public abstract class GenericController : ControllerBase
|
||||
do
|
||||
{
|
||||
var buffer = new byte[BufferSize];
|
||||
result = await WsService.Receive(_webSocket, buffer);
|
||||
result = await GameService.Receive(_webSocket, buffer);
|
||||
|
||||
if (result.CloseStatus.HasValue) break;
|
||||
|
||||
var segment = Run(result, buffer);
|
||||
|
||||
WsService.SendToAll(segment);
|
||||
GameService.SendToAll(segment);
|
||||
} while (true);
|
||||
|
||||
await WsService.Close(_webSocket, result.CloseStatus.Value, result.CloseStatusDescription);
|
||||
await GameService.Close(_webSocket, result.CloseStatus.Value, result.CloseStatusDescription);
|
||||
}
|
||||
catch (WebSocketException e)
|
||||
{
|
||||
@ -71,5 +71,5 @@ public abstract class GenericController : ControllerBase
|
||||
|
||||
protected abstract ArraySegment<byte> Run(WebSocketReceiveResult result, byte[] data);
|
||||
|
||||
protected virtual void Disconnect() => WsService.Connections -= WsServiceOnFire;
|
||||
protected virtual void Disconnect() => GameService.Connections -= WsServiceOnFire;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
using System.Net.WebSockets;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Services;
|
||||
|
||||
namespace pacMan.Controllers;
|
||||
|
||||
@ -8,9 +8,7 @@ namespace pacMan.Controllers;
|
||||
[Route("api/[controller]")]
|
||||
public class WsController : GenericController
|
||||
{
|
||||
public WsController(ILogger<WsController> logger, IWebSocketService wsService) : base(logger, wsService)
|
||||
{
|
||||
}
|
||||
public WsController(ILogger<WsController> logger, GameService gameService) : base(logger, gameService) { }
|
||||
|
||||
[HttpGet]
|
||||
public override async Task Accept() => await base.Accept();
|
||||
@ -20,4 +18,4 @@ public class WsController : GenericController
|
||||
var segment = new ArraySegment<byte>(data, 0, result.Count);
|
||||
return segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,10 @@
|
||||
using System.Net.WebSockets;
|
||||
using pacMan.Game;
|
||||
using pacMan.Game.Items;
|
||||
using pacMan.Services;
|
||||
|
||||
namespace pacMan.Interfaces;
|
||||
|
||||
public interface IWebSocketService
|
||||
{
|
||||
SynchronizedCollection<GameGroup> Games { get; }
|
||||
int CountConnected { get; }
|
||||
event Func<ArraySegment<byte>, Task>? Connections;
|
||||
Task Send(WebSocket webSocket, ArraySegment<byte> segment);
|
||||
void SendToAll(ArraySegment<byte> segment);
|
||||
Task<WebSocketReceiveResult> Receive(WebSocket webSocket, byte[] buffer);
|
||||
Task Close(WebSocket webSocket, WebSocketCloseStatus closeStatus, string? closeStatusDescription);
|
||||
GameGroup AddPlayer(IPlayer player, Queue<DirectionalPosition> spawns);
|
||||
}
|
||||
|
@ -6,7 +6,9 @@ var builder = WebApplication.CreateBuilder(args);
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllersWithViews();
|
||||
builder.Services.AddSingleton<IWebSocketService, WebSocketService>()
|
||||
builder.Services
|
||||
.AddSingleton<IWebSocketService, WebSocketService>()
|
||||
.AddSingleton<GameService>()
|
||||
.AddTransient<IActionService, ActionService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
@ -2,7 +2,6 @@ using System.Text.Json;
|
||||
using Microsoft.CSharp.RuntimeBinder;
|
||||
using pacMan.Game;
|
||||
using pacMan.Game.Items;
|
||||
using pacMan.Interfaces;
|
||||
|
||||
namespace pacMan.Services;
|
||||
|
||||
@ -21,14 +20,14 @@ public interface IActionService
|
||||
public class ActionService : IActionService
|
||||
{
|
||||
private readonly IDiceCup _diceCup;
|
||||
private readonly GameService _gameService;
|
||||
private readonly ILogger<ActionService> _logger;
|
||||
private readonly IWebSocketService _wsService;
|
||||
|
||||
public ActionService(ILogger<ActionService> logger, IWebSocketService wsService)
|
||||
public ActionService(ILogger<ActionService> logger, GameService gameService)
|
||||
{
|
||||
_logger = logger;
|
||||
_diceCup = new DiceCup();
|
||||
_wsService = wsService;
|
||||
_gameService = gameService;
|
||||
}
|
||||
|
||||
public GameGroup? Group { get; set; }
|
||||
@ -63,7 +62,7 @@ public class ActionService : IActionService
|
||||
PlayerInfoData data = JsonSerializer.Deserialize<PlayerInfoData>(message.Data);
|
||||
Player = data.Player;
|
||||
|
||||
Group = _wsService.AddPlayer(Player, data.Spawns);
|
||||
Group = _gameService.AddPlayer(Player, data.Spawns);
|
||||
}
|
||||
catch (RuntimeBinderException e)
|
||||
{
|
||||
@ -112,6 +111,6 @@ public struct PlayerInfoData
|
||||
|
||||
public struct ReadyData
|
||||
{
|
||||
public required bool AllReady { get; set; }
|
||||
public required bool AllReady { get; init; }
|
||||
public required IEnumerable<IPlayer> Players { get; set; }
|
||||
}
|
||||
|
35
pac-man-board-game/Services/GameService.cs
Normal file
35
pac-man-board-game/Services/GameService.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using pacMan.Game;
|
||||
using pacMan.Game.Items;
|
||||
|
||||
namespace pacMan.Services;
|
||||
|
||||
public class GameService : WebSocketService
|
||||
{
|
||||
public GameService(ILogger<GameService> logger) : base(logger) { }
|
||||
|
||||
public SynchronizedCollection<GameGroup> Games { get; } = new();
|
||||
|
||||
public event Func<ArraySegment<byte>, Task>? Connections; // TODO remove and use GameGroup
|
||||
|
||||
public void SendToAll(ArraySegment<byte> segment)
|
||||
{
|
||||
Connections?.Invoke(segment);
|
||||
}
|
||||
|
||||
public GameGroup AddPlayer(IPlayer player, Queue<DirectionalPosition> spawns)
|
||||
{
|
||||
var index = 0;
|
||||
try
|
||||
{
|
||||
while (!Games[index].AddPlayer(player)) index++;
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
var game = new GameGroup(spawns);
|
||||
game.AddPlayer(player);
|
||||
Games.Add(game);
|
||||
}
|
||||
|
||||
return Games[index];
|
||||
}
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
using System.Net.WebSockets;
|
||||
using pacMan.Game;
|
||||
using pacMan.Game.Items;
|
||||
using pacMan.Interfaces;
|
||||
using pacMan.Utils;
|
||||
|
||||
@ -8,20 +6,14 @@ namespace pacMan.Services;
|
||||
|
||||
public class WebSocketService : IWebSocketService
|
||||
{
|
||||
private readonly ILogger<WebSocketService> _logger;
|
||||
protected readonly ILogger<WebSocketService> Logger;
|
||||
|
||||
public WebSocketService(ILogger<WebSocketService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
Logger = logger;
|
||||
logger.Log(LogLevel.Debug, "WebSocket Service created");
|
||||
}
|
||||
|
||||
public SynchronizedCollection<GameGroup> Games { get; } = new();
|
||||
|
||||
public int CountConnected => Connections?.GetInvocationList().Length ?? 0;
|
||||
|
||||
public event Func<ArraySegment<byte>, Task>? Connections; // TODO remove and use GameGroup
|
||||
|
||||
public async Task Send(WebSocket webSocket, ArraySegment<byte> segment)
|
||||
{
|
||||
await webSocket.SendAsync(
|
||||
@ -30,18 +22,13 @@ public class WebSocketService : IWebSocketService
|
||||
true,
|
||||
CancellationToken.None);
|
||||
|
||||
_logger.Log(LogLevel.Debug, "Message sent to WebSocket");
|
||||
}
|
||||
|
||||
public void SendToAll(ArraySegment<byte> segment)
|
||||
{
|
||||
Connections?.Invoke(segment);
|
||||
Logger.Log(LogLevel.Debug, "Message sent to WebSocket");
|
||||
}
|
||||
|
||||
public async Task<WebSocketReceiveResult> Receive(WebSocket webSocket, byte[] buffer)
|
||||
{
|
||||
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
_logger.Log(LogLevel.Debug,
|
||||
Logger.Log(LogLevel.Debug,
|
||||
"Message \"{}\" received from WebSocket",
|
||||
buffer.GetString(result.Count));
|
||||
return result;
|
||||
@ -54,23 +41,6 @@ public class WebSocketService : IWebSocketService
|
||||
closeStatusDescription,
|
||||
CancellationToken.None);
|
||||
|
||||
_logger.Log(LogLevel.Information, "WebSocket connection closed");
|
||||
}
|
||||
|
||||
public GameGroup AddPlayer(IPlayer player, Queue<DirectionalPosition> spawns)
|
||||
{
|
||||
var index = 0;
|
||||
try
|
||||
{
|
||||
while (!Games[index].AddPlayer(player)) index++;
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
var game = new GameGroup(spawns);
|
||||
game.AddPlayer(player);
|
||||
Games.Add(game);
|
||||
}
|
||||
|
||||
return Games[index];
|
||||
Logger.Log(LogLevel.Information, "WebSocket connection closed");
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user