89 lines
2.8 KiB
C#
89 lines
2.8 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace NeonSprawl.Server.Game.Encounters;
|
|
|
|
/// <summary>Thread-safe in-memory encounter progress; empty at startup (NEO-104).</summary>
|
|
public sealed class InMemoryEncounterProgressStore : IEncounterProgressStore
|
|
{
|
|
private sealed class ProgressRow(bool started, HashSet<string> defeatedNpcInstanceIds)
|
|
{
|
|
public bool Started { get; } = started;
|
|
|
|
public HashSet<string> DefeatedNpcInstanceIds { get; } = defeatedNpcInstanceIds;
|
|
}
|
|
|
|
private readonly ConcurrentDictionary<string, ProgressRow> byKey = new(StringComparer.Ordinal);
|
|
|
|
private readonly ConcurrentDictionary<string, object> keyLocks = new(StringComparer.Ordinal);
|
|
|
|
/// <inheritdoc />
|
|
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<string>(row.DefeatedNpcInstanceIds, StringComparer.Ordinal));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
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<string>(StringComparer.Ordinal));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
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);
|
|
}
|
|
}
|
|
}
|