diff --git a/Controllers/RoomController.cs b/Controllers/RoomController.cs new file mode 100644 index 0000000..d687512 --- /dev/null +++ b/Controllers/RoomController.cs @@ -0,0 +1,54 @@ +using MauMau_Server.Websockets; +using Microsoft.AspNetCore.Mvc; + +namespace MauMau_Server.Controllers; + +[ApiController] +[Route("[controller]")] +public class RoomController : ControllerBase +{ + private readonly ILogger _logger; + private readonly IRoomManager _roomManager; + + public RoomController(ILogger logger, IRoomManager roomManager) + { + _logger = logger; + _roomManager = roomManager; + } + + [HttpGet] + public IActionResult GetAll() + { + var rooms = _roomManager.GetAllRooms(); + return Ok(rooms); + } + + [HttpGet("{id}")] + public async Task ConnectToRoom(string id) + { + if (HttpContext.WebSockets.IsWebSocketRequest) + { + if (_roomManager.RoomExists(id)) + { + using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); + var room = _roomManager.GetRoom(id); + await room.InstantiateConnection(webSocket); + } + else + { + HttpContext.Response.StatusCode = 404; + } + } + else + { + HttpContext.Response.StatusCode = 400; + } + } + + [HttpPost] + public IActionResult Post() + { + var id = _roomManager.CreateRoom(); + return Ok(id); + } +} \ No newline at end of file diff --git a/Controllers/WebSocketsController.cs b/Controllers/WebSocketsController.cs deleted file mode 100644 index c786fef..0000000 --- a/Controllers/WebSocketsController.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Net.WebSockets; -using System.Text; -using MauMau_Server.Websockets; -using Microsoft.AspNetCore.Mvc; - -namespace MauMau_Server.Controllers; - -[ApiController] -[Route("[controller]")] -public class WebSocketsController : ControllerBase -{ - private readonly ILogger _logger; - private readonly IWebsocketManager _websocketManager; - private string _id = string.Empty; - - public WebSocketsController(ILogger logger, IWebsocketManager websocketManager) - { - _logger = logger; - _websocketManager = websocketManager; - } - - [HttpGet("/ws")] - public async Task Get() - { - if (HttpContext.WebSockets.IsWebSocketRequest) - { - using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); - _id = _websocketManager.AddConnection(webSocket); - _logger.Log(LogLevel.Information, $"WebSocket connection {_id} established"); - await Echo(webSocket); - } - else - { - HttpContext.Response.StatusCode = 400; - } - } - - private async Task Echo(WebSocket webSocket) - { - var buffer = EmptyBuffer(); - var result = await ReceiveAsync(webSocket, buffer); - - while (!result.CloseStatus.HasValue) - { - var message = Encoding.UTF8.GetString(buffer); - BroadcastAsync(message); - - buffer = EmptyBuffer(); - result = await ReceiveAsync(webSocket, buffer); - } - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - _websocketManager.RemoveConnection(_id); - _logger.Log(LogLevel.Information, $"WebSocket connection {_id} closed"); - } - - private async Task ReceiveAsync(WebSocket webSocket, byte[] buffer) - { - var arraySegment = new ArraySegment(buffer); - var result = await webSocket.ReceiveAsync(arraySegment, CancellationToken.None); - _logger.Log(LogLevel.Information, $"Message received from Client {_id}"); - return result; - } - - private void BroadcastAsync(string message) - { - var bytes = Encoding.Default.GetBytes($"{_id}: {message}"); - var arraySegment = new ArraySegment(bytes); - foreach (var (id, socket) in _websocketManager.GetAllConnections()) - { - socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); - _logger.Log(LogLevel.Information, $"Message from Client {_id} sent to Client {id}"); - } - } - - private static byte[] EmptyBuffer() - { - return new byte[4096]; - } -} \ No newline at end of file diff --git a/Program.cs b/Program.cs index e45d0a5..fe48a95 100644 --- a/Program.cs +++ b/Program.cs @@ -8,7 +8,7 @@ services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle services.AddEndpointsApiExplorer(); services.AddSwaggerGen(); -services.AddScoped(); +services.AddScoped(); var app = builder.Build(); @@ -26,6 +26,13 @@ var webSocketOptions = new WebSocketOptions() app.UseWebSockets(webSocketOptions); +app.UseCors(policyBuilder => +{ + policyBuilder.AllowAnyOrigin(); + policyBuilder.AllowAnyMethod(); + policyBuilder.AllowAnyHeader(); +}); + app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); diff --git a/Websockets/Room.cs b/Websockets/Room.cs new file mode 100644 index 0000000..c511148 --- /dev/null +++ b/Websockets/Room.cs @@ -0,0 +1,74 @@ +using System.Net.WebSockets; +using System.Text; + +namespace MauMau_Server.Websockets; + +public class Room +{ + private readonly Dictionary _connections = new(); + + public async Task InstantiateConnection(WebSocket socket) + { + var socketId = AddConnection(socket); + await HandleConnection(socket, socketId); + } + + private async Task HandleConnection(WebSocket socket, string socketId) + { + var buffer = EmptyBuffer(); + var result = await ReceiveAsync(socket, buffer); + while (!result.CloseStatus.HasValue) + { + var message = $"{socketId}: {Encoding.Default.GetString(buffer)}"; + BroadcastAsync(message); + buffer = EmptyBuffer(); + result = await ReceiveAsync(socket, buffer); + } + await socket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + RemoveConnection(socketId); + } + + private string AddConnection(WebSocket socket) + { + var socketId = Guid.NewGuid().ToString(); + _connections.Add(socketId, socket); + return socketId; + } + + public WebSocket GetConnection(string socketId) + { + return _connections[socketId]; + } + + public Dictionary GetAllConnections() + { + return _connections; + } + + public void RemoveConnection(string socketId) + { + _connections.Remove(socketId); + } + + private async Task ReceiveAsync(WebSocket webSocket, byte[] buffer) + { + var arraySegment = new ArraySegment(buffer); + var result = await webSocket.ReceiveAsync(arraySegment, CancellationToken.None); + return result; + } + + private void BroadcastAsync(string message) + { + var bytes = Encoding.Default.GetBytes(message); + var arraySegment = new ArraySegment(bytes); + foreach (var (id, socket) in GetAllConnections()) + { + socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); + } + } + + private static byte[] EmptyBuffer() + { + return new byte[4096]; + } +} \ No newline at end of file diff --git a/Websockets/RoomManager.cs b/Websockets/RoomManager.cs new file mode 100644 index 0000000..5fb699a --- /dev/null +++ b/Websockets/RoomManager.cs @@ -0,0 +1,43 @@ +namespace MauMau_Server.Websockets; + +public class RoomManager : IRoomManager +{ + private static readonly Dictionary Rooms = new(); + + public string CreateRoom() + { + var roomId = Guid.NewGuid().ToString(); + var room = new Room(); + Rooms.Add(roomId, room); + return roomId; + } + + public Room GetRoom(string roomId) + { + return Rooms[roomId]; + } + + public List GetAllRooms() + { + return Rooms.Keys.ToList(); + } + + public void RemoveRoom(string roomId) + { + Rooms.Remove(roomId); + } + + public bool RoomExists(string roomId) + { + return Rooms.ContainsKey(roomId); + } +} + +public interface IRoomManager +{ + public string CreateRoom(); + public Room GetRoom(string roomId); + public List GetAllRooms(); + public void RemoveRoom(string roomId); + public bool RoomExists(string roomId); +} \ No newline at end of file diff --git a/Websockets/WebsocketManager.cs b/Websockets/WebsocketManager.cs deleted file mode 100644 index bf909c6..0000000 --- a/Websockets/WebsocketManager.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Net.WebSockets; - -namespace MauMau_Server.Websockets; - -public class WebsocketManager : IWebsocketManager -{ - private static readonly Dictionary Connections = new(); - - public string AddConnection(WebSocket socket) - { - var id = Guid.NewGuid().ToString(); - Connections.Add(id, socket); - return id; - } - - public WebSocket GetConnection(string id) - { - return Connections[id]; - } - - public Dictionary GetAllConnections() - { - return Connections; - } - - public void RemoveConnection(string id) - { - Connections.Remove(id); - } -} - -public interface IWebsocketManager -{ - public string AddConnection(WebSocket socket); - public WebSocket GetConnection(string id); - public Dictionary GetAllConnections(); - public void RemoveConnection(string id); -} \ No newline at end of file