using System.Collections.Concurrent;
namespace NeonSprawl.Server.Game.Encounters;
/// Thread-safe in-memory encounter progress; empty at startup (NEO-104).
public sealed class InMemoryEncounterProgressStore : IEncounterProgressStore
{
private sealed class ProgressRow(bool started, HashSet defeatedNpcInstanceIds)
{
public bool Started { get; } = started;
public HashSet DefeatedNpcInstanceIds { get; } = defeatedNpcInstanceIds;
}
private readonly ConcurrentDictionary byKey = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary keyLocks = new(StringComparer.Ordinal);
///
public bool TryGetProgress(string playerId, string encounterId, out EncounterProgressSnapshot snapshot)
{
snapshot = default!;
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
if (!byKey.TryGetValue(key, out var row))
{
return false;
}
snapshot = new EncounterProgressSnapshot(
EncounterProgressIds.NormalizePlayerId(playerId),
EncounterProgressIds.NormalizeEncounterId(encounterId),
row.Started,
new HashSet(row.DefeatedNpcInstanceIds, StringComparer.Ordinal));
return true;
}
}
///
public bool TryActivate(string playerId, string encounterId)
{
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
if (byKey.TryGetValue(key, out var existing) && existing.Started)
{
return false;
}
byKey[key] = new ProgressRow(
started: true,
defeatedNpcInstanceIds: new HashSet(StringComparer.Ordinal));
return true;
}
}
///
public bool TryAddDefeatedTarget(string playerId, string encounterId, string npcInstanceId)
{
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
var npcKey = EncounterProgressIds.NormalizeNpcInstanceId(npcInstanceId);
if (key.Length == 0 || npcKey.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
if (!byKey.TryGetValue(key, out var row) || !row.Started)
{
return false;
}
return row.DefeatedNpcInstanceIds.Add(npcKey);
}
}
}