using System; using System.Globalization; using System.Linq; using System.Threading; namespace AMNet.Server; /// /// Represents a storage location to load/read cards from /// internal class CardPresenter { private readonly Lock _cardLock = new(); private StoredCard _currentCard; public record StoredCard(byte[] Value, string OriginalValue, long ExpiresAt) { public bool Expired => Environment.TickCount64 >= ExpiresAt; public override string ToString() => string.Join(" ", Enumerable.Range(0, 5).Select(x => OriginalValue.Substring(x * 4, 4))); } /// /// Returns the currently active card without clearing it /// public StoredCard PeekActiveCard() { lock (_cardLock) { return _currentCard; } } public StoredCard TakeActiveCard() { lock (_cardLock) { var card = _currentCard; _currentCard = null; return card; } } /// /// Sets the card /// /// The card id to set /// How long the card should be presented to the game for in milliseconds /// Whether the card was set successfully public StoredCard SetCard(string cardId, long validFor = 5000) { // ensure the matrix code is 20-digits long, otherwise pad with zeros var matrixCode = cardId.Replace(" ", "").PadLeft(20, '0'); var bytes = new byte[10]; for (var i = 0; i < bytes.Length; i++) { var value = matrixCode.Substring(i * 2, 2); if (byte.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var b)) { bytes[i] = b; } else { return null; } } var card = new StoredCard(bytes, matrixCode, Environment.TickCount64 + validFor); lock (_cardLock) { _currentCard = card; } return card; } }