using Microsoft.AspNetCore.SignalR; namespace PongGame.Hubs; public class PongHub : Hub { private const string PLAYER_KEY = "PLAYER"; private readonly PongLobby Lobby; private readonly ILogger Logger; public PongHub(PongLobby lobby, ILogger logger) : base() { Lobby = lobby; Logger = logger; } private PongPlayer Player { get => (Context.Items.TryGetValue(PLAYER_KEY, out var player) ? player as PongPlayer : null) ?? throw new InvalidProgramException("Player was not assigned at connection start!"); set => Context.Items[PLAYER_KEY] = value; } public override Task OnConnectedAsync() { Player = Lobby.CreatePlayer(); Player.Client = Clients.Client(Context.ConnectionId); Player.Username = "Anon"; return Task.CompletedTask; } private void AssertNotInRoom() { if (Player.ConnectedRoom is PongRoom currentRoom) throw new HubException($"User is already connected to room [{currentRoom}]"); } private PongRoom AssertInRoom() { if (Player.ConnectedRoom is not PongRoom currentRoom) throw new HubException($"User is not in any room!"); return currentRoom; } public Task CreateRoom() { AssertNotInRoom(); var room = Lobby.CreateRoom(Player); return Task.FromResult(room.ID); } public Task JoinRoom(string roomId) { AssertNotInRoom(); var room = Lobby.JoinRoom(Player, roomId); return Task.FromResult(room.ID); } public Task MovePaddle(int dir) { var room = AssertInRoom(); var direction = (PongPaddleDirection)dir; if (!Enum.IsDefined(direction)) throw new HubException($"Invalid direction: {dir}!"); room.MovePaddle(Player, direction); return Task.CompletedTask; } public Task LeaveRoom() { Lobby.LeaveRoom(Player); return Task.CompletedTask; } public Task RequestUsernameChange(string username) { // TOOD: check this Logger.LogInformation("Player {Player} requested username change to [{username}]", Player, username); Player.Username = username; return Task.CompletedTask; } public override Task OnDisconnectedAsync(Exception? exception) { Lobby.RemovePlayer(Player); return Task.CompletedTask; } }