100 lines
2.8 KiB
C#
100 lines
2.8 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace NeonSprawl.Server.Game.Gathering;
|
|
|
|
/// <summary>Thread-safe in-memory node instance capacity; empty at startup — lazy rows only (NEO-61).</summary>
|
|
public sealed class InMemoryResourceNodeInstanceStore : IResourceNodeInstanceStore
|
|
{
|
|
private readonly ConcurrentDictionary<string, int> remainingById = new(StringComparer.Ordinal);
|
|
|
|
private readonly ConcurrentDictionary<string, object> idLocks = new(StringComparer.Ordinal);
|
|
|
|
/// <inheritdoc />
|
|
public bool TryGetRemainingGathers(string interactableId, out ResourceNodeInstanceSnapshot snapshot)
|
|
{
|
|
var key = NormalizeInteractableId(interactableId);
|
|
if (key.Length == 0)
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
lock (idLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
if (!remainingById.TryGetValue(key, out var remaining))
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
snapshot = new ResourceNodeInstanceSnapshot(key, remaining);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryEnsureInitialized(string interactableId, int initialRemainingGathers)
|
|
{
|
|
var key = NormalizeInteractableId(interactableId);
|
|
if (key.Length == 0 || initialRemainingGathers < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
lock (idLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
if (remainingById.ContainsKey(key))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
remainingById[key] = initialRemainingGathers;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryDecrementRemainingGathers(
|
|
string interactableId,
|
|
out int previousRemaining,
|
|
out int remainingGathers)
|
|
{
|
|
previousRemaining = 0;
|
|
remainingGathers = 0;
|
|
var key = NormalizeInteractableId(interactableId);
|
|
if (key.Length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
lock (idLocks.GetOrAdd(key, _ => new object()))
|
|
{
|
|
if (!remainingById.TryGetValue(key, out var current) || current <= 0)
|
|
{
|
|
if (current == 0)
|
|
{
|
|
remainingGathers = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
previousRemaining = current;
|
|
remainingGathers = current - 1;
|
|
remainingById[key] = remainingGathers;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
internal static string NormalizeInteractableId(string? interactableId)
|
|
{
|
|
var trimmed = interactableId?.Trim();
|
|
if (string.IsNullOrEmpty(trimmed))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return trimmed.ToLowerInvariant();
|
|
}
|
|
}
|