using System; using System.IO; using System.Text.Json; namespace codelanguage; /// Serializable Pager session used for one-click craft:// joins. public sealed record CraftSavedSession( string HubUrl, string Username, string SessionToken, DateTimeOffset SavedAtUtc); /// /// Local, per-user session cache written by the Pager after a successful Hub /// sign-in. The opaque Hub token lets a newly launched Dream Seeker join a /// craft:// link without asking the player to type their key again. /// public static class CraftSessionStore { public static string DefaultPath => Path.Combine( System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "CraftPlusPlus", "pager-session.json"); public static CraftSavedSession? Load(string? path = null) { string file = ResolvePath(path); try { if (!File.Exists(file)) return null; var session = JsonSerializer.Deserialize(File.ReadAllText(file)); if (session == null || string.IsNullOrWhiteSpace(session.Username) || string.IsNullOrWhiteSpace(session.SessionToken)) return null; return session; } catch { return null; } } public static void Save(string hubUrl, string username, string sessionToken, string? path = null) { if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(sessionToken)) return; string file = ResolvePath(path); try { string? directory = Path.GetDirectoryName(file); if (!string.IsNullOrEmpty(directory)) Directory.CreateDirectory(directory); var session = new CraftSavedSession(hubUrl ?? "", username, sessionToken, DateTimeOffset.UtcNow); string temporary = file + ".tmp"; File.WriteAllText(temporary, JsonSerializer.Serialize(session)); File.Move(temporary, file, overwrite: true); if (!OperatingSystem.IsWindows()) File.SetUnixFileMode(file, UnixFileMode.UserRead | UnixFileMode.UserWrite); } catch { // The session cache is best-effort; manual login remains available. } } public static void Clear(string? path = null) { string file = ResolvePath(path); try { if (File.Exists(file)) File.Delete(file); } catch { } } private static string ResolvePath(string? path) => string.IsNullOrWhiteSpace(path) ? DefaultPath : path; }