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();
}