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

56 lines
1.7 KiB
C#

using System.Collections.Concurrent;
namespace NeonSprawl.Server.Game.Encounters;
/// <summary>Thread-safe in-memory encounter completion flags (NEO-104).</summary>
public sealed class InMemoryEncounterCompletionStore : IEncounterCompletionStore
{
private readonly ConcurrentDictionary<string, DateTimeOffset> completedAtByKey = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary<string, object> keyLocks = new(StringComparer.Ordinal);
/// <inheritdoc />
public bool IsCompleted(string playerId, string encounterId)
{
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
return key.Length > 0 && completedAtByKey.ContainsKey(key);
}
/// <inheritdoc />
public bool TryMarkCompleted(string playerId, string encounterId, DateTimeOffset completedAt)
{
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
if (completedAtByKey.ContainsKey(key))
{
return false;
}
completedAtByKey[key] = completedAt;
return true;
}
}
/// <inheritdoc />
public bool TryGetCompletedAt(string playerId, string encounterId, out DateTimeOffset completedAt)
{
completedAt = default;
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
return completedAtByKey.TryGetValue(key, out completedAt);
}
}
}