namespace AcDream.Core.Social;
/// Authoritative client-side copy of retail's FriendDataList.
public sealed class FriendsState
{
private readonly object _gate = new();
private readonly List _entries = new();
private long _revision;
public int Count
{
get { lock (_gate) return _entries.Count; }
}
public long Revision => Interlocked.Read(ref _revision);
public IReadOnlyList Snapshot()
{
lock (_gate) return _entries.ToArray();
}
public bool TryGet(uint characterId, out FriendEntry? friend)
{
lock (_gate)
{
friend = _entries.Find(candidate => candidate.Id == characterId);
return friend is not null;
}
}
public void Apply(FriendsUpdate update)
{
ArgumentNullException.ThrowIfNull(update);
lock (_gate)
{
switch (update.Type)
{
case FriendsUpdateType.Full:
_entries.Clear();
_entries.AddRange(update.Entries);
break;
case FriendsUpdateType.Add:
foreach (FriendEntry entry in update.Entries)
{
int index = _entries.FindIndex(candidate => candidate.Id == entry.Id);
if (index >= 0) _entries[index] = entry;
else _entries.Add(entry);
}
break;
case FriendsUpdateType.Remove:
case FriendsUpdateType.RemoveSilent:
foreach (FriendEntry entry in update.Entries)
_entries.RemoveAll(candidate => candidate.Id == entry.Id);
break;
case FriendsUpdateType.OnlineStatus:
foreach (FriendEntry entry in update.Entries)
{
int index = _entries.FindIndex(candidate => candidate.Id == entry.Id);
if (index >= 0) _entries[index] = entry;
}
break;
}
Interlocked.Increment(ref _revision);
}
}
public void Clear()
{
lock (_gate)
{
_entries.Clear();
Interlocked.Increment(ref _revision);
}
}
}
public sealed record FriendEntry(
uint Id,
string Name,
bool Online,
bool AppearOffline,
IReadOnlyList Friends,
IReadOnlyList FriendOf);
public enum FriendsUpdateType : uint
{
Full = 0,
Add = 1,
Remove = 2,
RemoveSilent = 3,
OnlineStatus = 4,
}
public sealed record FriendsUpdate(
FriendsUpdateType Type,
IReadOnlyList Entries);