60 lines
3.1 KiB
C#
60 lines
3.1 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Reflection;
|
|
using System.Text.Json;
|
|
|
|
namespace NotPogo.Core.Rooms
|
|
{
|
|
public static class RoomManager
|
|
{
|
|
static readonly ConcurrentDictionary<string, Room> Rooms = new();
|
|
|
|
static readonly Dictionary<string, Func<string, IGameRoom>> Factories = BuildFactories();
|
|
|
|
public sealed record RoomDef(string Key, string Name, string Rule, int Capacity = 50);
|
|
|
|
public static void LoadFromFile(string path)
|
|
{
|
|
var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true };
|
|
var games = JsonSerializer.Deserialize<Dictionary<string, RoomDef[]>>(File.ReadAllText(path), opts)!;
|
|
|
|
foreach (var (gameCode, rooms) in games)
|
|
foreach (var r in rooms)
|
|
Define(r.Key, r.Name, gameCode, r.Rule, true, r.Capacity);
|
|
}
|
|
|
|
static Dictionary<string, Func<string, IGameRoom>> BuildFactories()
|
|
{
|
|
var map = new Dictionary<string, Func<string, IGameRoom>>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (var type in typeof(IGameRoom).Assembly.GetTypes()
|
|
.Where(t => typeof(IGameRoom).IsAssignableFrom(t) && !t.IsAbstract))
|
|
{
|
|
foreach (var attr in type.GetCustomAttributes<GameRoomAttribute>())
|
|
{
|
|
// constructor convention: (string ruleId) if the game has modes, otherwise parameterless
|
|
Func<string, IGameRoom> factory =
|
|
type.GetConstructor([typeof(string)]) is not null ? rule => (IGameRoom)Activator.CreateInstance(type, rule)!
|
|
: type.GetConstructor(Type.EmptyTypes) is not null ? _ => (IGameRoom)Activator.CreateInstance(type)!
|
|
: throw new InvalidOperationException($"{type.Name} needs a (string ruleId) or parameterless constructor");
|
|
|
|
if (!map.TryAdd(attr.GameCode, factory))
|
|
throw new InvalidOperationException($"Game code '{attr.GameCode}' is claimed by more than one room class");
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
|
|
static IGameRoom CreateGame(string gameCode, string ruleId) =>
|
|
Factories.TryGetValue(gameCode, out var create) ? create(ruleId) : new ChatOnlyRoom();
|
|
|
|
public static Room Define(string key, string humanName, string gameCode, string ruleId, bool isLive, int softCapacity = 50, int tabId = 1) =>
|
|
Rooms.GetOrAdd(key, _ => new Room(key, humanName, gameCode, ruleId, softCapacity, tabId, isLive, CreateGame(gameCode, ruleId)));
|
|
|
|
public static Room GetOrCreate(string key, string gameCode, string ruleId = "") =>
|
|
Rooms.GetOrAdd(key, _ => new Room(key, key, gameCode, ruleId, 50, 1, false, CreateGame(gameCode, ruleId)));
|
|
|
|
public static Room? Find(string key) => Rooms.TryGetValue(key, out var r) ? r : null;
|
|
public static IEnumerable<Room> ForGame(string gameCode) => Rooms.Values.Where(r => r.GameCode == gameCode);
|
|
}
|
|
}
|