neon-sprawl/server/NeonSprawl.Server/Game/Encounters/InMemoryEncounterCompleteEv...

69 lines
2.1 KiB
C#

using System.Collections.Concurrent;
namespace NeonSprawl.Server.Game.Encounters;
/// <summary>Thread-safe in-memory encounter completion event records (NEO-107).</summary>
public sealed class InMemoryEncounterCompleteEventStore : IEncounterCompleteEventStore
{
private readonly ConcurrentDictionary<string, EncounterCompleteEvent> eventsByKey = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary<string, object> keyLocks = new(StringComparer.Ordinal);
/// <inheritdoc />
public bool TryRecord(EncounterCompleteEvent completeEvent)
{
var key = EncounterProgressIds.MakeProgressKey(completeEvent.PlayerId, completeEvent.EncounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
if (eventsByKey.ContainsKey(key))
{
return false;
}
eventsByKey[key] = completeEvent with
{
GrantedItems = [..completeEvent.GrantedItems],
};
return true;
}
}
/// <inheritdoc />
public bool TryGet(string playerId, string encounterId, out EncounterCompleteEvent completeEvent)
{
completeEvent = default;
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
return eventsByKey.TryGetValue(key, out completeEvent);
}
}
/// <summary>Dev fixture only — removes one player+encounter event row (NEO-108 Bruno reset).</summary>
public bool TryClear(string playerId, string encounterId)
{
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
var removed = eventsByKey.TryRemove(key, out _);
keyLocks.TryRemove(key, out _);
return removed;
}
}
}