99 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			99 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System.ComponentModel;
 | |
| using Microsoft.AspNetCore.SignalR;
 | |
| 
 | |
| namespace PongGame.Hubs;
 | |
| 
 | |
| public class PongRoom {
 | |
|     public string ID { get; }
 | |
|     private readonly ILogger Logger;
 | |
|     private const string JOIN_LOG_TEMPLATE = "[{ID}] {Player} joined pong room as player {Number}!";
 | |
|     private const string LEAVE_LOG_TEMPLATE = "[{ID}] Player {Number} {Player} left pong room!";
 | |
|     private const string DIRECTION_LOG_TEMPLATE = "[{ID}] Player {Number} {player} moves paddle in direction: {direction}!";
 | |
| 
 | |
|     public PongRoom(string id, ILogger logger) {
 | |
|         ID = id;
 | |
|         Logger = logger;
 | |
|         gameWorker.DoWork += GameLoop;
 | |
|     }
 | |
| 
 | |
|     public PongPlayer? Player1 { get; private set; }
 | |
|     public PongPlayer? Player2 { get; private set; }
 | |
|     private PongGameState State = PongGameState.Initial;
 | |
| 
 | |
|     public void Join(PongPlayer player) {
 | |
|         // TODO: synchronize this
 | |
|         if (Player1 is null) {
 | |
|             Player1 = player;
 | |
|             Logger.LogInformation(JOIN_LOG_TEMPLATE, ID, player, 1);
 | |
|         } else if (Player2 is null) {
 | |
|             Player2 = player;
 | |
|             Logger.LogInformation(JOIN_LOG_TEMPLATE, ID, player, 1);
 | |
|         } else
 | |
|             throw new HubException($"Lobby [{ID}] is already full!");
 | |
|         _ = Task.Run(PlayersChanged);
 | |
|         player.ConnectedRoom = this;
 | |
|     }
 | |
| 
 | |
|     public void Leave(PongPlayer player) {
 | |
|         if (Player1 == player) {
 | |
|             Player1 = null;
 | |
|             Logger.LogInformation(LEAVE_LOG_TEMPLATE, ID, 1, player);
 | |
|         } else if (Player2 == player) {
 | |
|             Player2 = null;
 | |
|             Logger.LogInformation(LEAVE_LOG_TEMPLATE, ID, 2, player);
 | |
|         }
 | |
|         player.ConnectedRoom = null;
 | |
|         _ = Task.Run(PlayersChanged);
 | |
|     }
 | |
| 
 | |
|     private readonly BackgroundWorker gameWorker = new() {
 | |
|         WorkerSupportsCancellation = true
 | |
|     };
 | |
| 
 | |
|     private void GameLoop(object? sender, DoWorkEventArgs e) {
 | |
|         while (!gameWorker.CancellationPending
 | |
|             && Player1 is PongPlayer p1
 | |
|             && Player2 is PongPlayer p2
 | |
|             && State.Status is GameStatus.InProgress) {
 | |
|             PongGameState.Update(ref State);
 | |
|             _ = Task.Run(() => Task.WhenAll(
 | |
|                 p1.Client.ReceiveGameState(State),
 | |
|                 p2.Client.ReceiveGameState(State)));
 | |
|             Thread.Sleep(1000 / 60);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private Task PlayersChanged() {
 | |
|         if (Player1 is PongPlayer player1 && Player2 is PongPlayer player2) {
 | |
|             Logger.LogInformation("[{ID}] Pong game started: {player1} vs. {player2}", ID, player1, player2);
 | |
|             ResumeGame();
 | |
|         } else if (Player1 is null && Player2 is null) {
 | |
|             CloseRoom();
 | |
|         } else
 | |
|             PauseGame();
 | |
|         return Task.CompletedTask;
 | |
|     }
 | |
| 
 | |
|     private void ResumeGame() {
 | |
|         State.Status = GameStatus.InProgress;
 | |
|         if (!gameWorker.IsBusy) gameWorker.RunWorkerAsync();
 | |
|     }
 | |
| 
 | |
|     private void PauseGame() => State.Status = GameStatus.Paused;
 | |
|     private void CloseRoom() => State.Status = GameStatus.Finished;
 | |
| 
 | |
|     public override string ToString() => $"[{ID}]";
 | |
| 
 | |
|     public void MovePaddle(PongPlayer player, PongPaddleDirection direction) {
 | |
|         if (Player1 == player) {
 | |
|             State.Paddle1.Direction = direction;
 | |
|             Logger.LogDebug(DIRECTION_LOG_TEMPLATE, ID, 1, player, direction);
 | |
|             return;
 | |
|         } else if (Player2 == player) {
 | |
|             State.Paddle2.Direction = direction;
 | |
|             Logger.LogDebug(DIRECTION_LOG_TEMPLATE, ID, 2, player, direction);
 | |
|             return;
 | |
|         }
 | |
|         throw new InvalidOperationException("Player is not in this room, but moved! Assumably players room wasn't deleted.");
 | |
|     }
 | |
| } |