using System.Collections.Concurrent;
namespace NeonSprawl.Server.Game.Rewards;
/// Thread-safe in-memory quest completion reward delivery records (NEO-126).
public sealed class InMemoryRewardDeliveryStore : IRewardDeliveryStore
{
private readonly ConcurrentDictionary eventsByKey = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary keyLocks = new(StringComparer.Ordinal);
///
public bool TryRecord(RewardDeliveryEvent deliveryEvent)
{
var key = RewardDeliveryIds.MakeStoreKey(deliveryEvent.PlayerId, deliveryEvent.QuestId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
if (eventsByKey.ContainsKey(key))
{
return false;
}
eventsByKey[key] = deliveryEvent with
{
GrantedItems = [..deliveryEvent.GrantedItems],
GrantedSkillXp = [..deliveryEvent.GrantedSkillXp],
GrantedReputation = [..deliveryEvent.GrantedReputation],
};
return true;
}
}
///
public bool TryGet(string playerId, string questId, out RewardDeliveryEvent deliveryEvent)
{
deliveryEvent = default;
var key = RewardDeliveryIds.MakeStoreKey(playerId, questId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
return eventsByKey.TryGetValue(key, out deliveryEvent);
}
}
/// Dev fixture only — removes one player+quest delivery row (Bruno / test reset).
public bool TryClear(string playerId, string questId)
{
var key = RewardDeliveryIds.MakeStoreKey(playerId, questId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
_ = eventsByKey.TryRemove(key, out _);
keyLocks.TryRemove(key, out _);
return true;
}
}
}