using System.Text; namespace Ashwild.Network { /// /// Converts between a host's SteamID64 and a short, shareable session code. /// A SteamID64 is a 17-digit number; an individual account only differs from the others /// by its 32-bit account id (SteamID64 = IndividualBase + accountId), so we encode just /// that account id in base36 to get a ~6-7 character code mixing digits and letters. /// The conversion is fully reversible, so the joiner can rebuild the exact SteamID64. /// public static class SessionCode { #region Constants /// /// Base offset of every individual (public-universe) SteamID64 — 0x0110000100000000. /// Subtracting it leaves only the 32-bit account id worth encoding. /// private const ulong IndividualBase = 76561197960265728UL; /// /// Base36 alphabet (digits + uppercase letters) used to render the short code. /// private const string Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; #endregion #region Public API /// /// Builds the short shareable code from a full SteamID64. /// public static string FromSteamId(ulong steamId64) { ulong accountId = steamId64 >= IndividualBase ? steamId64 - IndividualBase : steamId64; return ToBase36(accountId); } /// /// Parses a short code back into a full SteamID64. Accepts either the new base36 code /// or a raw legacy SteamID64 string. Returns false when the code is empty or malformed. /// public static bool TryToSteamId(string code, out ulong steamId64) { steamId64 = 0; if (string.IsNullOrWhiteSpace(code)) return false; code = code.Trim().ToUpperInvariant(); // Backward-compat: a raw 17-digit SteamID64 pasted as-is. if (ulong.TryParse(code, out ulong raw) && raw >= IndividualBase) { steamId64 = raw; return true; } if (!TryFromBase36(code, out ulong accountId)) return false; steamId64 = accountId + IndividualBase; return true; } #endregion #region Internal Helpers /// /// Renders an unsigned value as a base36 string (most-significant digit first). /// private static string ToBase36(ulong value) { if (value == 0) return "0"; StringBuilder sb = new StringBuilder(13); while (value > 0) { sb.Insert(0, Alphabet[(int)(value % 36)]); value /= 36; } return sb.ToString(); } /// /// Parses a base36 string into an unsigned value; false on any non-alphabet character. /// private static bool TryFromBase36(string code, out ulong value) { value = 0; foreach (char c in code) { int digit = Alphabet.IndexOf(c); if (digit < 0) return false; value = value * 36 + (ulong)digit; } return true; } #endregion } }