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); 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 async Task JoinRoom(string roomId) { AssertNotInRoom(); var room = await Lobby.JoinRoom(Player, roomId); return room.ID; } public Task MovePaddle(int dir) { var room = AssertInRoom(); var direction = (PongPaddleDirection)dir; if (!Enum.IsDefined(direction)) throw new HubException($"Invalid direction: {dir}!"); return room.MovePaddle(Player, direction); } public Task LeaveRoom() => Lobby.LeaveRoom(Player); public Task RequestUsernameChange(string username) => Lobby.ChangeUsername(Player, username); public override Task OnDisconnectedAsync(Exception? exception) { Lobby.RemovePlayer(Player); return Task.CompletedTask; } }