Minecraft-Console-Client/MinecraftClient/Protocol/Session/SessionCache.cs

221 lines
9.3 KiB
C#
Raw Normal View History

2023-09-15 14:44:11 +08:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Timers;
2024-12-22 13:20:04 +01:00
using MessagePack;
2023-09-15 14:44:11 +08:00
using static MinecraftClient.Settings;
2024-04-16 21:21:08 +03:00
using static MinecraftClient.Settings.MainConfigHelper.MainConfig.AdvancedConfig;
2023-09-15 14:44:11 +08:00
namespace MinecraftClient.Protocol.Session
{
/// <summary>
/// Handle sessions caching and storage.
/// </summary>
public static class SessionCache
{
private const string SessionCacheFileSerialized = "SessionCache.db";
private static readonly string SessionCacheFileMinecraft = String.Concat(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Path.DirectorySeparatorChar,
".minecraft",
Path.DirectorySeparatorChar,
"launcher_profiles.json"
);
private static FileMonitor? cachemonitor;
private static readonly Dictionary<string, SessionToken> sessions = new();
private static readonly Timer updatetimer = new(100);
private static readonly List<KeyValuePair<string, SessionToken>> pendingadds = new();
/// <summary>
/// Retrieve whether SessionCache contains a session for the given login.
/// </summary>
/// <param name="login">User login used with Minecraft.net</param>
/// <returns>TRUE if session is available</returns>
public static bool Contains(string login)
{
return sessions.ContainsKey(login);
}
/// <summary>
/// Store a session and save it to disk if required.
/// </summary>
/// <param name="login">User login used with Minecraft.net</param>
/// <param name="session">User session token used with Minecraft.net</param>
public static void Store(string login, SessionToken session)
{
if (Contains(login))
{
sessions[login] = session;
}
else
{
sessions.Add(login, session);
}
if (Config.Main.Advanced.SessionCache == CacheType.disk && updatetimer.Enabled == true)
{
pendingadds.Add(new KeyValuePair<string, SessionToken>(login, session));
}
else if (Config.Main.Advanced.SessionCache == CacheType.disk)
{
SaveToDisk();
}
}
/// <summary>
/// Retrieve a session token for the given login.
/// </summary>
/// <param name="login">User login used with Minecraft.net</param>
/// <returns>SessionToken for given login</returns>
public static SessionToken Get(string login)
{
return sessions[login];
}
/// <summary>
/// Initialize cache monitoring to keep cache updated with external changes.
/// </summary>
/// <returns>TRUE if session tokens are seeded from file</returns>
public static bool InitializeDiskCache()
{
2024-12-22 13:20:04 +01:00
cachemonitor = new FileMonitor(AppDomain.CurrentDomain.BaseDirectory, SessionCacheFileSerialized, new FileSystemEventHandler(OnChanged));
2023-09-15 14:44:11 +08:00
updatetimer.Elapsed += HandlePending;
return LoadFromDisk();
}
/// <summary>
/// Reloads cache on external cache file change.
/// </summary>
/// <param name="sender">Sender</param>
/// <param name="e">Event data</param>
private static void OnChanged(object sender, FileSystemEventArgs e)
{
updatetimer.Stop();
updatetimer.Start();
}
/// <summary>
/// Called after timer elapsed. Reads disk cache and adds new/modified sessions back.
/// </summary>
/// <param name="sender">Sender</param>
/// <param name="e">Event data</param>
private static void HandlePending(object? sender, ElapsedEventArgs e)
{
updatetimer.Stop();
LoadFromDisk();
foreach (KeyValuePair<string, SessionToken> pending in pendingadds.ToArray())
{
Store(pending.Key, pending.Value);
pendingadds.Remove(pending);
}
}
/// <summary>
/// Reads cache file and loads SessionTokens into SessionCache.
/// </summary>
/// <returns>True if data is successfully loaded</returns>
private static bool LoadFromDisk()
{
2024-12-22 13:20:04 +01:00
// Grab sessions in the Minecraft directory
2023-09-15 14:44:11 +08:00
if (File.Exists(SessionCacheFileMinecraft))
{
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loading, Path.GetFileName(SessionCacheFileMinecraft)));
System.Text.Json.Nodes.JsonNode? mcSession = null;
2023-09-15 14:44:11 +08:00
try
{
mcSession = Json.ParseJson(File.ReadAllText(SessionCacheFileMinecraft));
}
catch (IOException) { /* Failed to read file from disk -- ignoring */ }
if (mcSession is System.Text.Json.Nodes.JsonObject mcSessionObj
&& mcSessionObj.ContainsKey("clientToken")
&& mcSessionObj.ContainsKey("authenticationDatabase"))
2023-09-15 14:44:11 +08:00
{
string clientID = mcSession["clientToken"]!.GetStringValue().Replace("-", "");
var sessionItems = mcSession["authenticationDatabase"]!.AsObject();
foreach (var kvp in sessionItems)
2023-09-15 14:44:11 +08:00
{
string key = kvp.Key;
2023-09-15 14:44:11 +08:00
if (Guid.TryParseExact(key, "N", out Guid temp))
{
var sessionItem = kvp.Value!.AsObject();
2023-09-15 14:44:11 +08:00
if (sessionItem.ContainsKey("displayName")
&& sessionItem.ContainsKey("accessToken")
&& sessionItem.ContainsKey("username")
&& sessionItem.ContainsKey("uuid"))
{
string login = Settings.ToLowerIfNeed(sessionItem["username"]!.GetStringValue());
2023-09-15 14:44:11 +08:00
try
{
SessionToken session = SessionToken.FromString(String.Join(",",
sessionItem["accessToken"]!.GetStringValue(),
sessionItem["displayName"]!.GetStringValue(),
sessionItem["uuid"]!.GetStringValue().Replace("-", ""),
2023-09-15 14:44:11 +08:00
clientID
));
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loaded, login, session.ID));
sessions[login] = session;
}
catch (InvalidDataException) { /* Not a valid session */ }
}
}
}
}
}
2024-12-22 13:20:04 +01:00
// Serialized session cache file in binary format
2023-09-15 14:44:11 +08:00
if (File.Exists(SessionCacheFileSerialized))
{
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_converting, SessionCacheFileSerialized));
try
{
using FileStream fs = new(SessionCacheFileSerialized, FileMode.Open, FileAccess.Read, FileShare.Read);
2024-12-22 13:20:04 +01:00
// Deserialize using MessagePack
Dictionary<string, SessionToken> sessionsTemp = MessagePackSerializer.Deserialize<Dictionary<string, SessionToken>>(fs);
2023-09-15 14:44:11 +08:00
foreach (KeyValuePair<string, SessionToken> item in sessionsTemp)
{
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loaded, item.Key, item.Value.ID));
sessions[item.Key] = item.Value;
}
}
catch (IOException ex)
{
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_read_fail, ex.Message));
}
2024-12-22 13:20:04 +01:00
catch (MessagePackSerializationException ex2)
2023-09-15 14:44:11 +08:00
{
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_malformed, ex2.Message));
}
}
return sessions.Count > 0;
}
/// <summary>
/// Saves SessionToken's from SessionCache into cache file.
/// </summary>
private static void SaveToDisk()
{
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted("§8" + Translations.cache_saving, acceptnewlines: true);
try
{
2024-12-22 13:20:04 +01:00
using FileStream fs = new(SessionCacheFileSerialized, FileMode.Create, FileAccess.Write, FileShare.None);
// Serialize using MessagePack
MessagePackSerializer.Serialize(fs, sessions);
2023-09-15 14:44:11 +08:00
}
catch (IOException e)
{
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_save_fail, e.Message));
}
}
}
}