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 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.LogInformation(DIRECTION_LOG_TEMPLATE, ID, 1, player, direction);
|
|
return;
|
|
} else if (Player2 == player) {
|
|
State.Paddle2.Direction = direction;
|
|
Logger.LogInformation(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.");
|
|
}
|
|
} |