acdream/src/AcDream.Core/Social/FriendsState.cs
Erik dcb61efb5a refactor(runtime): expose canonical gameplay state
Move character options and movement skills into the Runtime-owned character graph, expose borrowed inventory, character, and social views, and route retained UI state commands through generation-gated typed Runtime contracts. Preserve the existing synchronous wire path while deleting the App-owned option and skill mirrors and extending normalized parity checkpoints.

Co-authored-by: Codex <codex@openai.com>
2026-07-26 09:12:30 +02:00

96 lines
2.7 KiB
C#

namespace AcDream.Core.Social;
/// <summary>Authoritative client-side copy of retail's <c>FriendDataList</c>.</summary>
public sealed class FriendsState
{
private readonly object _gate = new();
private readonly List<FriendEntry> _entries = new();
private long _revision;
public int Count
{
get { lock (_gate) return _entries.Count; }
}
public long Revision => Interlocked.Read(ref _revision);
public IReadOnlyList<FriendEntry> 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<uint> Friends,
IReadOnlyList<uint> FriendOf);
public enum FriendsUpdateType : uint
{
Full = 0,
Add = 1,
Remove = 2,
RemoveSilent = 3,
OnlineStatus = 4,
}
public sealed record FriendsUpdate(
FriendsUpdateType Type,
IReadOnlyList<FriendEntry> Entries);