using Microsoft.AspNetCore.StaticFiles; using System.Runtime.InteropServices.Marshalling; namespace NotPogo.Core.Rooms { public sealed class Room { public string Key { get; } public string HumanName { get; } public string GameCode { get; } public string RuleId { get; } public int SoftCapacity { get; } public int TabId { get; } public bool Live { get; set; } = true; public IGameRoom Game { get; } readonly List _players = new(); readonly object _lock = new(); public Room(string key, string humanName, string gameCode, string ruleId, int softCapacity, int tabId, bool live, IGameRoom game) { Key = key; HumanName = humanName; GameCode = gameCode; RuleId = ruleId; SoftCapacity = softCapacity; TabId = tabId; Live = live; Game = game; } public bool IsJoinable => Live && PlayerCount < SoftCapacity; public int PlayerCount { get { lock (_lock) return _players.Count; } } public Player[] Snapshot() { lock (_lock) { return [.. _players]; } } public static (string, string)[] PresenceFields(Player p) => [ ("msg.op", "rm.join"), ("scrn", p.ScreenName), ("snme", p.ScreenName), ("pfid", p.ProfileId), ("plid", p.Id.ToString()), ("entl", "ictsebaulovfFmMrRG"), ("priv", "0"), ("aepc", ""), ("acvw", ""), ("agvs", ""), ("alrc", ""), ]; public static (string, string)[][] PlayerState(Player p) => [ [("msg.op", "pl.rm.stte"), ("plid", p.Id.ToString()), ("gmpf", ""), ("glpf", "..."), ("frls", "")], [("msg.op", "pl.rm.stte"), ("plid", p.Id.ToString()), ("tokb", "9999")], [("msg.op", "pl.rm.stte"), ("plid", p.Id.ToString()), ("avtr", ""), ("icon", "avatarminighost")] ]; public async Task JoinAsync(Player player) { Player[] others; lock (_lock) { others = [.. _players]; _players.Add(player); player.Room = this; } await player.SendAsync( ("msg.op", "load"), ("rmst", "start") ); await player.SendAsync(PresenceFields(player)); foreach (var other in others) await player.SendAsync(PresenceFields(other)); await player.SendAsync( ("msg.op", "gm.crea"), ("launch", "y"), ("live", "0"), ("port", "0"), ("iali", "86400000"), ("gins", HumanName), ("dead", "0"), ("iaid", "15000"), ("shut", ""), ("gmid", "0") ); await player.SendAsync( ("msg.op", "load"), ("rmst", "end") ); await player.SendAsync( ("msg.op", "say"), ("chnl", "serv"), ("scrn", ""), ("type", "stnd"), ("text", "NotPogo Is Alpha Software! Some Things May Not Function Correctly.") ); foreach (var other in others) foreach (var m in PlayerState(other)) await player.SendAsync(m); await BroadcastAsync(PresenceFields(player), except: player); foreach (var m in PlayerState(player)) await BroadcastAsync(m); await Game.OnPlayerJoinedAsync(this, player); } public async Task LeaveAsync(Player player) { lock (_lock) { if (!_players.Remove(player)) return; player.Room = null; } await BroadcastAsync([("msg.op", "rm.left"), ("plid", player.Id.ToString()), ("scrn", player.ScreenName)]); await Game.OnPlayerLeftAsync(this, player); } public Task BroadcastAsync((string, string)[] values, Player? except = null) { var msg = new Dictionary(); foreach (var (k, v) in values) msg[k] = v; return BroadcastAsync(msg, except); } public Task BroadcastAsync(Dictionary msg, Player? except = null) => Task.WhenAll(Snapshot().Where(p => p != except).Select(p => p.SendAsync(msg))); public async Task HandleAsync(Player player, string op, Dictionary msg) { switch(op) { case "gm.join": await GameJoinAsync(player); return; case "say": msg.TryGetValue("text", out var text); await BroadcastAsync( [("msg.op", "say"), ("chnl", msg.GetValueOrDefault("chnl", "")), ("plid", player.Id.ToString()), ("scrn", player.ScreenName), ("type", "stnd"), ("text", text ?? "")] ); return; case "rm.left": case "disc": await LeaveAsync(player); return; } if (!await Game.OnMessageAsync(this, player, op, msg)) Console.WriteLine($"[Room {Key}] Unhandled OP '{op}' From {player.ScreenName}"); } public async Task GameJoinAsync(Player player) { string plid = player.Id.ToString(); await player.SendAsync( ("msg.op", "gm.join"), ("plid", plid), ("snme", player.ScreenName), ("gmid", "0"), ("scrn", player.ScreenName), ("join", "join") ); await player.SendAsync( ("msg.op", "gm.lnch"), ("gmid", "0") ); await player.SendAsync( ("msg.op", "tokb"), ("tokb", "9999") ); await player.SendAsync( ("msg.op", "tick"), ("scrn", player.ScreenName), ("text", "Welcome to Not Pogo!") ); await player.SendAsync( ("msg.op", "cnfg"), ("impd", "40") ); await player.SendAsync( ("msg.op", "ext.pref"), ("pref", "") ); } } }