using System.Collections.Concurrent; using Microsoft.Extensions.Options; using NeonSprawl.Server.Game.PositionState; namespace NeonSprawl.Server.Game.Items; /// Thread-safe in-memory inventory; seeds configured dev player with an empty snapshot (NEO-54). public sealed class InMemoryPlayerInventoryStore(IOptions options) : IPlayerInventoryStore { private readonly ConcurrentDictionary byPlayer = CreateInitialMap(options.Value); private readonly ConcurrentDictionary playerLocks = new(StringComparer.OrdinalIgnoreCase); private static ConcurrentDictionary CreateInitialMap(GamePositionOptions o) { var id = NormalizePlayerId(o.DevPlayerId); var map = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); map[id] = PlayerInventorySnapshot.Empty(); return map; } /// public bool TryGetSnapshot(string playerId, out PlayerInventorySnapshot snapshot) { var key = NormalizePlayerId(playerId); if (key.Length == 0 || !byPlayer.TryGetValue(key, out var existing)) { snapshot = PlayerInventorySnapshot.Empty(); return false; } lock (playerLocks.GetOrAdd(key, _ => new object())) { snapshot = Clone(existing); return true; } } /// public bool TryReplaceSnapshot(string playerId, PlayerInventorySnapshot snapshot) { var key = NormalizePlayerId(playerId); if (key.Length == 0 || !byPlayer.ContainsKey(key)) { return false; } lock (playerLocks.GetOrAdd(key, _ => new object())) { byPlayer[key] = Clone(snapshot); return true; } } private static PlayerInventorySnapshot Clone(PlayerInventorySnapshot source) => new() { BagSlots = CloneSlots(source.BagSlots), EquipmentSlots = CloneSlots(source.EquipmentSlots), }; private static InventorySlotState[] CloneSlots(InventorySlotState[] slots) { var copy = new InventorySlotState[slots.Length]; for (var i = 0; i < slots.Length; i++) { var s = slots[i]; copy[i] = new InventorySlotState(s.SlotIndex, s.ItemId, s.Quantity); } return copy; } private static string NormalizePlayerId(string? playerId) { var t = playerId?.Trim(); if (string.IsNullOrEmpty(t)) { return string.Empty; } return t.ToLowerInvariant(); } }