69 lines
2.1 KiB
C#
69 lines
2.1 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace NeonSprawl.Server.Game.Npc;
|
|
|
|
/// <summary>Thread-safe in-memory aggro holders for prototype NPC instances (NEO-92).</summary>
|
|
public sealed class InMemoryThreatStateStore : IThreatStateStore
|
|
{
|
|
private readonly ConcurrentDictionary<string, string?> holderByNpcId = new(StringComparer.Ordinal);
|
|
|
|
private readonly ConcurrentDictionary<string, object> idLocks = new(StringComparer.Ordinal);
|
|
|
|
/// <inheritdoc />
|
|
public bool TryGet(string? npcInstanceId, out ThreatStateSnapshot snapshot)
|
|
{
|
|
var key = NormalizeNpcInstanceId(npcInstanceId);
|
|
if (key.Length == 0 || !PrototypeNpcRegistry.TryGet(key, out _))
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
lock (idLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
if (!holderByNpcId.TryGetValue(key, out var holder))
|
|
{
|
|
holder = null;
|
|
holderByNpcId[key] = holder;
|
|
}
|
|
|
|
snapshot = new ThreatStateSnapshot(key, holder);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TrySetHolder(string? npcInstanceId, string? playerIdLowercaseOrNull)
|
|
{
|
|
var key = NormalizeNpcInstanceId(npcInstanceId);
|
|
if (key.Length == 0 || !PrototypeNpcRegistry.TryGet(key, out _))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var normalizedHolder = NormalizePlayerId(playerIdLowercaseOrNull);
|
|
|
|
lock (idLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
holderByNpcId[key] = normalizedHolder;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryClearHolder(string? npcInstanceId) => TrySetHolder(npcInstanceId, null);
|
|
|
|
private static string NormalizeNpcInstanceId(string? npcInstanceId) =>
|
|
npcInstanceId?.Trim().ToLowerInvariant() ?? string.Empty;
|
|
|
|
private static string? NormalizePlayerId(string? playerId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(playerId))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return playerId.Trim().ToLowerInvariant();
|
|
}
|
|
}
|