namespace OpenAC.MosswartMassacre.Sharing; /// One character heard from through the backend. internal sealed class Peer { public Peer(string characterName) { CharacterName = characterName; } public string CharacterName { get; } public uint PlayerId { get; set; } public int CurrentHealth { get; set; } public int MaxHealth { get; set; } public int CurrentStamina { get; set; } public int MaxStamina { get; set; } public int CurrentMana { get; set; } public int MaxMana { get; set; } public double EW { get; set; } public double NS { get; set; } /// Height in metres. public double Z { get; set; } public double Heading { get; set; } public bool HasPosition { get; set; } public IReadOnlyList Tags { get; set; } = []; public DateTime LastUpdateUtc { get; set; } } /// /// The peers heard from, by name. A peer not heard from for fifteen seconds /// drops out, the rule the host applies to its own peers; the original kept /// logged-off peers forever. /// internal sealed class PeerTable { public static readonly TimeSpan Staleness = TimeSpan.FromSeconds(15); private readonly Dictionary _peers = new(StringComparer.OrdinalIgnoreCase); public Peer Upsert(string name, DateTime nowUtc) { if (!_peers.TryGetValue(name, out Peer? peer)) { peer = new Peer(name); _peers[name] = peer; } peer.LastUpdateUtc = nowUtc; return peer; } public bool TryGet(string name, out Peer? peer) => _peers.TryGetValue(name, out peer); /// The peers heard from within the last fifteen seconds, by name. public IReadOnlyList Current(DateTime nowUtc) { foreach (string stale in _peers.Values .Where(peer => nowUtc - peer.LastUpdateUtc > Staleness) .Select(peer => peer.CharacterName) .ToList()) { _peers.Remove(stale); } return [.. _peers.Values.OrderBy(peer => peer.CharacterName, StringComparer.OrdinalIgnoreCase)]; } public void Clear() => _peers.Clear(); }