neon-sprawl/server/NeonSprawl.Server/Game/Rewards/InMemoryRewardDeliveryStore.cs

70 lines
2.1 KiB
C#

using System.Collections.Concurrent;
namespace NeonSprawl.Server.Game.Rewards;
/// <summary>Thread-safe in-memory quest completion reward delivery records (NEO-126).</summary>
public sealed class InMemoryRewardDeliveryStore : IRewardDeliveryStore
{
private readonly ConcurrentDictionary<string, RewardDeliveryEvent> eventsByKey = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary<string, object> keyLocks = new(StringComparer.Ordinal);
/// <inheritdoc />
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.ToArray(),
GrantedSkillXp = deliveryEvent.GrantedSkillXp.ToArray(),
};
return true;
}
}
/// <inheritdoc />
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);
}
}
/// <summary>Dev fixture only — removes one player+quest delivery row (Bruno / test reset).</summary>
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()))
{
var removed = eventsByKey.TryRemove(key, out _);
keyLocks.TryRemove(key, out _);
return removed;
}
}
}