108 lines
3.4 KiB
C#
108 lines
3.4 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace FishGameService.GameService
|
|
{
|
|
public interface IBulletTracker
|
|
{
|
|
void Add(string roomId, string userId, IEnumerable<string> bulletIds);
|
|
bool TryConsume(string roomId, string userId, string bulletId);
|
|
int Count(string roomId, string userId);
|
|
void Clear(string roomId, string userId);
|
|
}
|
|
|
|
public sealed class BulletTracker : IBulletTracker
|
|
{
|
|
private const int MaxBulletsPerPlayer = 100;
|
|
private readonly ConcurrentDictionary<string, PlayerBullets> _players = new();
|
|
|
|
public void Add(string roomId, string userId, IEnumerable<string> bulletIds)
|
|
{
|
|
var bullets = _players.GetOrAdd(Key(roomId, userId), _ => new PlayerBullets());
|
|
lock (bullets.SyncRoot)
|
|
{
|
|
foreach (var bulletId in bulletIds)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(bulletId) || !bullets.Ids.Add(bulletId))
|
|
continue;
|
|
|
|
bullets.Order.Enqueue(bulletId);
|
|
}
|
|
|
|
while (bullets.Ids.Count > MaxBulletsPerPlayer && bullets.Order.Count > 0)
|
|
bullets.Ids.Remove(bullets.Order.Dequeue());
|
|
}
|
|
}
|
|
|
|
public bool TryConsume(string roomId, string userId, string bulletId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(bulletId)
|
|
|| !_players.TryGetValue(Key(roomId, userId), out var bullets))
|
|
return false;
|
|
|
|
lock (bullets.SyncRoot)
|
|
return bullets.Ids.Remove(bulletId);
|
|
}
|
|
|
|
public int Count(string roomId, string userId)
|
|
{
|
|
if (!_players.TryGetValue(Key(roomId, userId), out var bullets))
|
|
return 0;
|
|
|
|
lock (bullets.SyncRoot)
|
|
return bullets.Ids.Count;
|
|
}
|
|
|
|
public void Clear(string roomId, string userId)
|
|
{
|
|
_players.TryRemove(Key(roomId, userId), out _);
|
|
}
|
|
|
|
private static string Key(string roomId, string userId) => $"{roomId}|{userId}";
|
|
|
|
private sealed class PlayerBullets
|
|
{
|
|
public object SyncRoot { get; } = new();
|
|
public HashSet<string> Ids { get; } = new(StringComparer.Ordinal);
|
|
public Queue<string> Order { get; } = new();
|
|
}
|
|
}
|
|
|
|
public interface IFishKillTracker
|
|
{
|
|
bool TryAdd(string roomId, string fishId);
|
|
void Clear(string roomId);
|
|
}
|
|
|
|
public sealed class FishKillTracker : IFishKillTracker
|
|
{
|
|
private const int MaxFishPerRoom = 50;
|
|
private readonly ConcurrentDictionary<string, RoomFishIds> _rooms = new();
|
|
|
|
public bool TryAdd(string roomId, string fishId)
|
|
{
|
|
var room = _rooms.GetOrAdd(roomId, _ => new RoomFishIds());
|
|
lock (room.SyncRoot)
|
|
{
|
|
if (!room.Ids.Add(fishId))
|
|
return false;
|
|
|
|
room.Order.Enqueue(fishId);
|
|
|
|
while (room.Ids.Count > MaxFishPerRoom && room.Order.Count > 0)
|
|
room.Ids.Remove(room.Order.Dequeue());
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public void Clear(string roomId) => _rooms.TryRemove(roomId, out _);
|
|
|
|
private sealed class RoomFishIds
|
|
{
|
|
public object SyncRoot { get; } = new();
|
|
public HashSet<string> Ids { get; } = new(StringComparer.Ordinal);
|
|
public Queue<string> Order { get; } = new();
|
|
}
|
|
}
|
|
}
|