using System.Collections.Concurrent; using System.Reflection; using System.Text.Json; namespace NotPogo.Core.Rooms { public static class RoomManager { static readonly ConcurrentDictionary Rooms = new(); static readonly Dictionary> 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>(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> BuildFactories() { var map = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var type in typeof(IGameRoom).Assembly.GetTypes() .Where(t => typeof(IGameRoom).IsAssignableFrom(t) && !t.IsAbstract)) { foreach (var attr in type.GetCustomAttributes()) { // constructor convention: (string ruleId) if the game has modes, otherwise parameterless Func 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 ForGame(string gameCode) => Rooms.Values.Where(r => r.GameCode == gameCode); } }