using System.Collections.Concurrent; using Steamworks; namespace SVSim.EmulatedEntrypoint.Services; public class SteamSessionService : IDisposable { private readonly ConcurrentDictionary _validatedSessionTickets = new(); private readonly object _initLock = new(); private bool _steamInitialized; private const int ShadowVerseAppId = 453480; /// /// Validates if a given session ticket is valid, and matches up with the given steamid. /// /// the ticket, represented as a hexadecimal string /// the steamid that should be associated with the ticket /// whether the ticket is valid for the given steamid public bool IsTicketValidForUser(string ticket, ulong steamId) { if (string.IsNullOrEmpty(ticket)) { // Caller already shouldn't pass null/empty here, but a misshaped request body // (e.g. wrong casing) used to NRE on the ConcurrentDictionary lookup below. // Fail cleanly so the auth pipeline returns 401 instead of crashing the request. return false; } if (_validatedSessionTickets.TryGetValue(ticket, out ulong storedSteamId)) { return storedSteamId == steamId; } EnsureSteamInitialized(); List ticketBytes = new List(); for (int i = 0; i < ticket.Length; i += 2) { ticketBytes.Add(Convert.ToByte(ticket.Substring(i, 2), 16)); } var steamCheckResults = SteamServer.BeginAuthSession(ticketBytes.ToArray(), new SteamId { Value = steamId }); if (steamCheckResults) { _validatedSessionTickets.TryAdd(ticket, steamId); } return steamCheckResults; } private void EnsureSteamInitialized() { if (_steamInitialized) return; lock (_initLock) { if (_steamInitialized) return; SteamServer.Init(ShadowVerseAppId, new SteamServerInit { GamePort = default, QueryPort = default }); _steamInitialized = true; } } public void Dispose() { if (_steamInitialized) { SteamServer.Shutdown(); } } }