78 lines
2.5 KiB
C#
78 lines
2.5 KiB
C#
namespace NeonSprawl.Server.Game.Targeting;
|
|
|
|
/// <summary>Thread-safe prototype lock store; player keys are trimmed with ordinal case-insensitive lookup (same spirit as position APIs).</summary>
|
|
public sealed class InMemoryPlayerTargetLockStore : IPlayerTargetLockStore
|
|
{
|
|
private readonly object mutex = new();
|
|
private readonly Dictionary<string, PlayerLockRow> rows = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
private readonly record struct PlayerLockRow(string? LockIdLowercase, int Sequence);
|
|
|
|
public (string? LockIdLowercase, int Sequence) GetLockState(string playerId)
|
|
{
|
|
var key = playerId.Trim();
|
|
if (key.Length == 0)
|
|
{
|
|
return (null, 0);
|
|
}
|
|
|
|
lock (mutex)
|
|
{
|
|
return rows.TryGetValue(key, out var row) ? (row.LockIdLowercase, row.Sequence) : (null, 0);
|
|
}
|
|
}
|
|
|
|
public (string? LockIdLowercase, int Sequence) ApplyClear(string playerId)
|
|
{
|
|
var key = playerId.Trim();
|
|
if (key.Length == 0)
|
|
{
|
|
return (null, 0);
|
|
}
|
|
|
|
lock (mutex)
|
|
{
|
|
if (!rows.TryGetValue(key, out var prev))
|
|
{
|
|
return (null, 0);
|
|
}
|
|
|
|
if (prev.LockIdLowercase is null)
|
|
{
|
|
return (null, prev.Sequence);
|
|
}
|
|
|
|
var next = new PlayerLockRow(null, prev.Sequence + 1);
|
|
rows[key] = next;
|
|
// NEO-27 telemetry hook site: `target_changed` fires when lock id transitions.
|
|
// TODO(E9.M1): emit cataloged telemetry event (player id, previous/next lock, cause=clear).
|
|
return (null, next.Sequence);
|
|
}
|
|
}
|
|
|
|
public (string? LockIdLowercase, int Sequence) ApplySet(string playerId, string targetIdLowercase)
|
|
{
|
|
var key = playerId.Trim();
|
|
if (key.Length == 0)
|
|
{
|
|
return (null, 0);
|
|
}
|
|
|
|
lock (mutex)
|
|
{
|
|
var prev = rows.TryGetValue(key, out var r) ? r : new PlayerLockRow(null, 0);
|
|
|
|
if (string.Equals(prev.LockIdLowercase, targetIdLowercase, StringComparison.Ordinal))
|
|
{
|
|
return (prev.LockIdLowercase, prev.Sequence);
|
|
}
|
|
|
|
var next = new PlayerLockRow(targetIdLowercase, prev.Sequence + 1);
|
|
rows[key] = next;
|
|
// NEO-27 telemetry hook site: `target_changed` fires when lock id transitions.
|
|
// TODO(E9.M1): emit cataloged telemetry event (player id, previous/next lock, cause=tab/select).
|
|
return (next.LockIdLowercase, next.Sequence);
|
|
}
|
|
}
|
|
}
|