using System.Collections.Concurrent; namespace NeonSprawl.Server.Game.Gathering; /// Thread-safe in-memory node instance capacity; empty at startup — lazy rows only (NEO-61). public sealed class InMemoryResourceNodeInstanceStore : IResourceNodeInstanceStore { private readonly ConcurrentDictionary remainingById = new(StringComparer.Ordinal); private readonly ConcurrentDictionary idLocks = new(StringComparer.Ordinal); /// public bool TryGetRemainingGathers(string interactableId, out ResourceNodeInstanceSnapshot snapshot) { var key = ResourceNodeInstanceIds.Normalize(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; } } /// public bool TryEnsureInitialized(string interactableId, int initialRemainingGathers) { var key = ResourceNodeInstanceIds.Normalize(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; } } /// public bool TryDecrementRemainingGathers( string interactableId, out int previousRemaining, out int remainingGathers) { previousRemaining = 0; remainingGathers = 0; var key = ResourceNodeInstanceIds.Normalize(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; } } /// public bool TrySetRemainingGathers(string interactableId, int remainingGathers) { var key = ResourceNodeInstanceIds.Normalize(interactableId); if (key.Length == 0 || remainingGathers < 0) { return false; } lock (idLocks.GetOrAdd(key, _ => new object())) { remainingById[key] = remainingGathers; return true; } } }