69 lines
2.1 KiB
C#
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.ToArray(),
|
|
};
|
|
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;
|
|
}
|
|
}
|
|
}
|