diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs
index 4da8f2d2..42b559a2 100644
--- a/MinecraftClient/Program.cs
+++ b/MinecraftClient/Program.cs
@@ -12,7 +12,7 @@ using MinecraftClient.WinAPI;
namespace MinecraftClient
{
///
- /// Minecraft Console Client by ORelio and Contributors (c) 2012-2017.
+ /// Minecraft Console Client by ORelio and Contributors (c) 2012-2018.
/// Allows to connect to any Minecraft server, send and receive text, automated scripts.
/// This source code is released under the CDDL 1.0 License.
///
@@ -102,7 +102,7 @@ namespace MinecraftClient
{
bool cacheLoaded = SessionCache.InitializeDiskCache();
if (Settings.DebugMessages)
- ConsoleIO.WriteLineFormatted(cacheLoaded ? "§8Session cache has been successfully loaded from disk." : "§8Cached sessions could not be loaded from disk");
+ ConsoleIO.WriteLineFormatted(cacheLoaded ? "§8Session data has been successfully loaded from disk." : "§8No sessions could be loaded from disk");
}
//Asking the user to type in missing data such as Username and Password
diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs
index 42990604..6e3ea218 100644
--- a/MinecraftClient/Protocol/ProtocolHandler.cs
+++ b/MinecraftClient/Protocol/ProtocolHandler.cs
@@ -8,7 +8,6 @@ using System.Net.Sockets;
using System.Net.Security;
using MinecraftClient.Protocol.Handlers.Forge;
-
namespace MinecraftClient.Protocol
{
///
@@ -229,10 +228,10 @@ namespace MinecraftClient.Protocol
if (Settings.DebugMessages)
ConsoleIO.WriteLineFormatted("§8Debug: Login Request: " + json_request);
int code = DoHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result);
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Debug: Login Response: " + result);
if (code == 200)
{
- if (Settings.DebugMessages)
- ConsoleIO.WriteLineFormatted("§8Debug: Login Response: " + result);
if (result.Contains("availableProfiles\":[]}"))
{
return LoginResult.NotPremium;
diff --git a/MinecraftClient/Protocol/SessionCache/SessionCache.cs b/MinecraftClient/Protocol/SessionCache/SessionCache.cs
index 7e76c0f0..474cae06 100644
--- a/MinecraftClient/Protocol/SessionCache/SessionCache.cs
+++ b/MinecraftClient/Protocol/SessionCache/SessionCache.cs
@@ -13,7 +13,16 @@ namespace MinecraftClient.Protocol.SessionCache
///
public static class SessionCache
{
- private const string SessionCacheFile = "SessionCache.db";
+ private const string SessionCacheFilePlaintext = "SessionCache.ini";
+ 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 Dictionary sessions = new Dictionary();
private static FileSystemWatcher cachemonitor = new FileSystemWatcher();
private static Timer updatetimer = new Timer(100);
@@ -74,7 +83,7 @@ namespace MinecraftClient.Protocol.SessionCache
{
cachemonitor.Path = AppDomain.CurrentDomain.BaseDirectory;
cachemonitor.IncludeSubdirectories = false;
- cachemonitor.Filter = SessionCacheFile;
+ cachemonitor.Filter = SessionCacheFilePlaintext;
cachemonitor.NotifyFilter = NotifyFilters.LastWrite;
cachemonitor.Changed += new FileSystemEventHandler(OnChanged);
cachemonitor.EnableRaisingEvents = true;
@@ -118,17 +127,71 @@ namespace MinecraftClient.Protocol.SessionCache
/// True if data is successfully loaded
private static bool LoadFromDisk()
{
- if (Settings.DebugMessages)
- ConsoleIO.WriteLineFormatted("§8Updating session cache from disk");
-
- if (File.Exists(SessionCacheFile))
+ //Grab sessions in the Minecraft directory
+ if (File.Exists(SessionCacheFileMinecraft))
{
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Loading Minecraft profiles: " + Path.GetFileName(SessionCacheFileMinecraft));
+ Json.JSONData mcSession = new Json.JSONData(Json.JSONData.DataType.String);
try
{
- using (FileStream fs = new FileStream(SessionCacheFile, FileMode.Open, FileAccess.Read, FileShare.Read))
+ mcSession = Json.ParseJson(File.ReadAllText(SessionCacheFileMinecraft));
+ }
+ catch (IOException) { /* Failed to read file from disk -- ignoring */ }
+ if (mcSession.Type == Json.JSONData.DataType.Object
+ && mcSession.Properties.ContainsKey("clientToken")
+ && mcSession.Properties.ContainsKey("authenticationDatabase"))
+ {
+ Guid temp;
+ string clientID = mcSession.Properties["clientToken"].StringValue.Replace("-", "");
+ Dictionary sessionItems = mcSession.Properties["authenticationDatabase"].Properties;
+ foreach (string key in sessionItems.Keys)
{
- sessions = (Dictionary)formatter.Deserialize(fs);
- return true;
+ if (Guid.TryParseExact(key, "N", out temp))
+ {
+ Dictionary sessionItem = sessionItems[key].Properties;
+ if (sessionItem.ContainsKey("displayName")
+ && sessionItem.ContainsKey("accessToken")
+ && sessionItem.ContainsKey("username")
+ && sessionItem.ContainsKey("uuid"))
+ {
+ string login = sessionItem["username"].StringValue.ToLower();
+ try
+ {
+ SessionToken session = SessionToken.FromString(String.Join(",",
+ sessionItem["accessToken"].StringValue,
+ sessionItem["displayName"].StringValue,
+ sessionItem["uuid"].StringValue.Replace("-", ""),
+ clientID
+ ));
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Loaded session: " + login + ':' + session.ID);
+ sessions[login] = session;
+ }
+ catch (InvalidDataException) { /* Not a valid session */ }
+ }
+ }
+ }
+ }
+ }
+
+ //Serialized session cache file in binary format
+ if (File.Exists(SessionCacheFileSerialized))
+ {
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Converting session cache from disk: " + SessionCacheFileSerialized);
+
+ try
+ {
+ using (FileStream fs = new FileStream(SessionCacheFileSerialized, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ Dictionary sessionsTemp = (Dictionary)formatter.Deserialize(fs);
+ foreach (KeyValuePair item in sessionsTemp)
+ {
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Loaded session: " + item.Key + ':' + item.Value.ID);
+ sessions[item.Key] = item.Value;
+ }
}
}
catch (IOException ex)
@@ -140,7 +203,43 @@ namespace MinecraftClient.Protocol.SessionCache
ConsoleIO.WriteLineFormatted("§8Got malformed data while reading session cache from disk: " + ex2.Message);
}
}
- return false;
+
+ //User-editable session cache file in text format
+ if (File.Exists(SessionCacheFilePlaintext))
+ {
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Loading session cache from disk: " + SessionCacheFilePlaintext);
+
+ foreach (string line in File.ReadAllLines(SessionCacheFilePlaintext))
+ {
+ if (!line.Trim().StartsWith("#"))
+ {
+ string[] keyValue = line.Split('=');
+ if (keyValue.Length == 2)
+ {
+ try
+ {
+ string login = keyValue[0].ToLower();
+ SessionToken session = SessionToken.FromString(keyValue[1]);
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Loaded session: " + login + ':' + session.ID);
+ sessions[login] = session;
+ }
+ catch (InvalidDataException e)
+ {
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Ignoring session token string '" + keyValue[1] + "': " + e.Message);
+ }
+ }
+ else if (Settings.DebugMessages)
+ {
+ ConsoleIO.WriteLineFormatted("§8Ignoring invalid session token line: " + line);
+ }
+ }
+ }
+ }
+
+ return sessions.Count > 0;
}
///
@@ -151,7 +250,7 @@ namespace MinecraftClient.Protocol.SessionCache
if (Settings.DebugMessages)
ConsoleIO.WriteLineFormatted("§8Saving session cache to disk");
- bool fileexists = File.Exists(SessionCacheFile);
+ bool fileexists = File.Exists(SessionCacheFilePlaintext);
IOException lastEx = null;
int attempt = 1;
@@ -159,20 +258,13 @@ namespace MinecraftClient.Protocol.SessionCache
{
try
{
- using (FileStream fs = new FileStream(SessionCacheFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
- {
- cachemonitor.EnableRaisingEvents = false;
-
- // delete existing file contents
- if (fileexists)
- {
- fs.SetLength(0);
- fs.Flush();
- }
-
- formatter.Serialize(fs, sessions);
- cachemonitor.EnableRaisingEvents = true;
- }
+ List sessionCacheLines = new List();
+ sessionCacheLines.Add("# Generated by MCC v" + Program.Version + " - Edit at own risk!");
+ foreach (KeyValuePair entry in sessions)
+ sessionCacheLines.Add(entry.Key + '=' + entry.Value.ToString());
+ File.WriteAllLines(SessionCacheFilePlaintext, sessionCacheLines);
+ //if (File.Exists(SessionCacheFileSerialized))
+ // File.Delete(SessionCacheFileSerialized);
return;
}
catch (IOException ex)
diff --git a/MinecraftClient/Protocol/SessionToken.cs b/MinecraftClient/Protocol/SessionToken.cs
index d00824f9..e7b52f64 100644
--- a/MinecraftClient/Protocol/SessionToken.cs
+++ b/MinecraftClient/Protocol/SessionToken.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.IO;
namespace MinecraftClient.Protocol
{
@@ -17,5 +19,35 @@ namespace MinecraftClient.Protocol
PlayerID = String.Empty;
ClientID = String.Empty;
}
+
+ public override string ToString()
+ {
+ return String.Join(",", ID, PlayerName, PlayerID, ClientID);
+ }
+
+ public static SessionToken FromString(string tokenString)
+ {
+ string[] fields = tokenString.Split(',');
+ if (fields.Length < 4)
+ throw new InvalidDataException("Invalid string format");
+
+ SessionToken session = new SessionToken();
+ session.ID = fields[0];
+ session.PlayerName = fields[1];
+ session.PlayerID = fields[2];
+ session.ClientID = fields[3];
+
+ Guid temp;
+ if (!Guid.TryParseExact(session.ID, "N", out temp))
+ throw new InvalidDataException("Invalid session ID");
+ if (!ChatBot.IsValidName(session.PlayerName))
+ throw new InvalidDataException("Invalid player name");
+ if (!Guid.TryParseExact(session.PlayerID, "N", out temp))
+ throw new InvalidDataException("Invalid player ID");
+ if (!Guid.TryParseExact(session.ClientID, "N", out temp))
+ throw new InvalidDataException("Invalid client ID");
+
+ return session;
+ }
}
}
diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs
index 18ec4b22..31ae1b08 100644
--- a/MinecraftClient/Settings.cs
+++ b/MinecraftClient/Settings.cs
@@ -91,7 +91,7 @@ namespace MinecraftClient
public static bool DisplayChatLinks = true;
public static bool TerrainAndMovements = false;
public static string PrivateMsgsCmdName = "tell";
- public static CacheType SessionCaching = CacheType.None;
+ public static CacheType SessionCaching = CacheType.Disk;
public static bool DebugMessages = false;
public static bool ResolveSrvRecords = true;
public static bool ResolveSrvRecordsShortTimeout = true;
@@ -539,7 +539,7 @@ namespace MinecraftClient
+ "showxpbarmessages=true # Messages displayed above xp bar\r\n"
+ "showchatlinks=true # Show links embedded in chat messages\r\n"
+ "terrainandmovements=false # Uses more ram, cpu, bandwidth\r\n"
- + "sessioncache=memory # Use 'none', 'memory' or 'disk' (disk session storing is experimental)\r\n"
+ + "sessioncache=disk # How to retain session tokens. Use 'none', 'memory' or 'disk'\r\n"
+ "resolvesrvrecords=fast # Use 'false', 'fast' (5s timeout), or 'true'. Required for joining some servers.\r\n"
+ "accountlist=accounts.txt # See README > 'Servers and Accounts file' for more info about this file\r\n"
+ "serverlist=servers.txt # See README > 'Servers and Accounts file' for more info about this file\r\n"