using System.Collections.Concurrent; using Microsoft.Extensions.Options; namespace NeonSprawl.Server.Game.PositionState; /// Thread-safe in-memory positions; seeds the configured dev player on construction. public sealed class InMemoryPositionStateStore : IPositionStateStore { private readonly ConcurrentDictionary _positions = new(StringComparer.OrdinalIgnoreCase); public InMemoryPositionStateStore(IOptions options) { var o = options.Value; var id = o.DevPlayerId.Trim(); if (id.Length == 0) throw new InvalidOperationException("Game:DevPlayerId must be non-empty."); var p = o.DefaultPosition ?? new DefaultPositionOptions(); _positions[id] = new PositionSnapshot(p.X, p.Y, p.Z, 0); } public bool TryGetPosition(string playerId, out PositionSnapshot snapshot) { var key = playerId?.Trim(); if (string.IsNullOrEmpty(key)) { snapshot = default; return false; } return _positions.TryGetValue(key, out snapshot); } public bool TryApplyMoveTarget(string playerId, double x, double y, double z, out PositionSnapshot snapshot) { var key = playerId?.Trim(); if (string.IsNullOrEmpty(key)) { snapshot = default; return false; } while (true) { if (!_positions.TryGetValue(key, out var previous)) { snapshot = default; return false; } var next = new PositionSnapshot(x, y, z, previous.Sequence + 1); if (_positions.TryUpdate(key, next, previous)) { snapshot = next; return true; } } } }