81 lines
2.4 KiB
C#
81 lines
2.4 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);
|
|
if (key.Length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
lock (keyLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
return 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>Dev fixture only — removes one player+encounter completion 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 = completedAtByKey.TryRemove(key, out _);
|
|
keyLocks.TryRemove(key, out _);
|
|
return removed;
|
|
}
|
|
}
|
|
}
|