48 lines
1.4 KiB
C#
48 lines
1.4 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace NeonSprawl.Server.Game.AbilityInput;
|
|
|
|
/// <summary>Thread-safe in-memory cooldown map (NEO-32); not persisted across process restarts.</summary>
|
|
public sealed class InMemoryPlayerAbilityCooldownStore : IPlayerAbilityCooldownStore
|
|
{
|
|
private readonly ConcurrentDictionary<string, ConcurrentDictionary<int, DateTimeOffset>> ends = new(StringComparer.Ordinal);
|
|
|
|
public bool IsOnCooldown(string playerId, int slotIndex, DateTimeOffset now)
|
|
{
|
|
if (!ends.TryGetValue(playerId, out var perSlot))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!perSlot.TryGetValue(slotIndex, out var end))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (now >= end)
|
|
{
|
|
perSlot.TryRemove(slotIndex, out _);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void StartCooldown(string playerId, int slotIndex, DateTimeOffset now, TimeSpan duration)
|
|
{
|
|
var perSlot = ends.GetOrAdd(playerId, _ => new ConcurrentDictionary<int, DateTimeOffset>());
|
|
perSlot[slotIndex] = now + duration;
|
|
}
|
|
|
|
public bool TryGetCooldownEndUtc(string playerId, int slotIndex, out DateTimeOffset endUtc)
|
|
{
|
|
endUtc = default;
|
|
if (!ends.TryGetValue(playerId, out var perSlot))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return perSlot.TryGetValue(slotIndex, out endUtc);
|
|
}
|
|
}
|