2022-11-04 15:13:56 +01:00
|
|
|
|
using System.Numerics;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using Microsoft.AspNetCore.SignalR;
|
2022-11-03 12:29:35 +01:00
|
|
|
|
|
|
|
|
|
namespace PongGame.Hubs;
|
|
|
|
|
|
|
|
|
|
public class PongLobby {
|
|
|
|
|
private readonly HashSet<PongPlayer> connectedPlayers = new();
|
|
|
|
|
private readonly Dictionary<string, PongRoom> PongRooms = new();
|
|
|
|
|
|
|
|
|
|
public PongLobby(ILogger<PongLobby> logger)
|
|
|
|
|
=> Logger = logger;
|
|
|
|
|
|
|
|
|
|
public const int ROOM_ID_LENGTH = 4;
|
|
|
|
|
|
|
|
|
|
public PongPlayer CreatePlayer() {
|
|
|
|
|
var player = new PongPlayer();
|
|
|
|
|
lock (connectedPlayers)
|
|
|
|
|
connectedPlayers.Add(player);
|
|
|
|
|
return player;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RemovePlayer(PongPlayer player) {
|
|
|
|
|
if (player.ConnectedRoom is PongRoom room)
|
|
|
|
|
room.Leave(player);
|
|
|
|
|
lock (connectedPlayers)
|
|
|
|
|
_ = connectedPlayers.Remove(player);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public PongRoom CreateRoom(PongPlayer player) {
|
|
|
|
|
PongRoom room;
|
|
|
|
|
lock (PongRooms) {
|
|
|
|
|
room = new(GenerateRoomId(), Logger);
|
|
|
|
|
PongRooms.Add(room.ID, room);
|
|
|
|
|
}
|
|
|
|
|
room.Join(player);
|
|
|
|
|
return room;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-04 15:13:56 +01:00
|
|
|
|
public Task<PongRoom> JoinRoom(PongPlayer player, string roomId) {
|
2022-11-03 12:29:35 +01:00
|
|
|
|
PongRoom? room;
|
|
|
|
|
lock (PongRooms) {
|
|
|
|
|
room = PongRooms.GetValueOrDefault(roomId);
|
|
|
|
|
}
|
|
|
|
|
if (room is null) throw new HubException($"Room [{roomId}] not found!");
|
|
|
|
|
room.Join(player);
|
2022-11-04 15:13:56 +01:00
|
|
|
|
return Task.FromResult(room);
|
2022-11-03 12:29:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-04 15:13:56 +01:00
|
|
|
|
public Task LeaveRoom(PongPlayer player) {
|
|
|
|
|
if (player.ConnectedRoom is PongRoom room) {
|
2022-11-03 12:29:35 +01:00
|
|
|
|
room.Leave(player);
|
2022-11-04 15:13:56 +01:00
|
|
|
|
if (room.IsEmpty)
|
|
|
|
|
lock (PongRooms)
|
|
|
|
|
_ = PongRooms.Remove(room.ID);
|
|
|
|
|
}
|
|
|
|
|
return Task.CompletedTask;
|
2022-11-03 12:29:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly Random random = new();
|
|
|
|
|
private readonly ILogger<PongLobby> Logger;
|
|
|
|
|
private const string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
|
|
|
|
|
|
private string GenerateRoomId() {
|
|
|
|
|
string id;
|
|
|
|
|
do {
|
|
|
|
|
id = string.Concat(Enumerable.Range(0, ROOM_ID_LENGTH).Select(_ => ALPHABET[random.Next(ALPHABET.Length)]));
|
|
|
|
|
} while (PongRooms.ContainsKey(id));
|
|
|
|
|
return id;
|
|
|
|
|
}
|
2022-11-04 15:13:56 +01:00
|
|
|
|
|
|
|
|
|
public async Task ChangeUsername(PongPlayer player, string username) {
|
|
|
|
|
username = ValidateUsername(username);
|
|
|
|
|
if (player.Username == username) {
|
|
|
|
|
await player.Client.UsernameChanged(username);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
lock (connectedPlayers) {
|
|
|
|
|
// TODO: separate hashset for usernames
|
|
|
|
|
if (connectedPlayers.Select(i => i.Username).Contains(username))
|
|
|
|
|
throw new HubException($"Username {username} is already taken!");
|
|
|
|
|
}
|
|
|
|
|
Logger.LogInformation("Player {Player} requested username change to [{username}]", player, username);
|
|
|
|
|
player.Username = username;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static readonly Regex UsernameRegex = new(@"^(?!.*[._ -]{2})[\w._ -]{3,20}$", RegexOptions.Compiled, TimeSpan.FromMilliseconds(200));
|
|
|
|
|
|
|
|
|
|
private static string ValidateUsername(string username) {
|
|
|
|
|
username = username.Trim();
|
|
|
|
|
if (!UsernameRegex.IsMatch(username))
|
|
|
|
|
throw new HubException($"At most 20 characters, no two consecutive symbols");
|
|
|
|
|
return username;
|
|
|
|
|
}
|
2022-11-03 12:29:35 +01:00
|
|
|
|
}
|