92 lines
2.7 KiB
C#
92 lines
2.7 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace NeonSprawl.Server.Game.Combat;
|
|
|
|
/// <summary>Thread-safe in-memory session player combat HP; empty at startup — lazy rows only (NEO-95).</summary>
|
|
public sealed class InMemoryPlayerCombatHealthStore : IPlayerCombatHealthStore
|
|
{
|
|
private readonly ConcurrentDictionary<string, int> currentHpById = new(StringComparer.Ordinal);
|
|
|
|
private readonly ConcurrentDictionary<string, object> idLocks = new(StringComparer.Ordinal);
|
|
|
|
/// <inheritdoc />
|
|
public bool TryGet(string? playerId, out PlayerCombatHealthSnapshot snapshot)
|
|
{
|
|
var key = NormalizePlayerId(playerId);
|
|
if (key.Length == 0)
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
lock (idLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
EnsureRowInitialized(key);
|
|
snapshot = CreateSnapshot(key, currentHpById[key]);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryApplyDamage(string? playerId, int damage, out PlayerCombatHealthSnapshot snapshot)
|
|
{
|
|
snapshot = default;
|
|
if (damage < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var key = NormalizePlayerId(playerId);
|
|
if (key.Length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
lock (idLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
EnsureRowInitialized(key);
|
|
var current = currentHpById[key];
|
|
var next = Math.Max(0, current - damage);
|
|
currentHpById[key] = next;
|
|
snapshot = CreateSnapshot(key, next);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryResetToFull(string? playerId, out PlayerCombatHealthSnapshot snapshot)
|
|
{
|
|
var key = NormalizePlayerId(playerId);
|
|
if (key.Length == 0)
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
lock (idLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
currentHpById[key] = PlayerCombatHealthDefaults.MaxHp;
|
|
snapshot = CreateSnapshot(key, PlayerCombatHealthDefaults.MaxHp);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private void EnsureRowInitialized(string normalizedId)
|
|
{
|
|
if (!currentHpById.ContainsKey(normalizedId))
|
|
{
|
|
currentHpById[normalizedId] = PlayerCombatHealthDefaults.MaxHp;
|
|
}
|
|
}
|
|
|
|
private static PlayerCombatHealthSnapshot CreateSnapshot(string playerId, int currentHp) =>
|
|
new(
|
|
playerId,
|
|
PlayerCombatHealthDefaults.MaxHp,
|
|
currentHp,
|
|
currentHp <= 0);
|
|
|
|
private static string NormalizePlayerId(string? playerId) =>
|
|
playerId?.Trim().ToLowerInvariant() ?? string.Empty;
|
|
}
|