From fec1687cb7e2a0c7c056e78f20e16665ef6627d5 Mon Sep 17 00:00:00 2001 From: Justin Slauson Date: Wed, 2 Mar 2016 18:16:19 -0700 Subject: [PATCH] Updated docs and cleaned up. --- MinecraftClient/Cache/SessionCache.cs | 288 +++---- MinecraftClient/Program.cs | 828 ++++++++++---------- MinecraftClient/Protocol/ProtocolHandler.cs | 4 - 3 files changed, 563 insertions(+), 557 deletions(-) diff --git a/MinecraftClient/Cache/SessionCache.cs b/MinecraftClient/Cache/SessionCache.cs index 3d83be40..9064d232 100644 --- a/MinecraftClient/Cache/SessionCache.cs +++ b/MinecraftClient/Cache/SessionCache.cs @@ -1,139 +1,149 @@ -using MinecraftClient.Protocol; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; -using System.Security.Cryptography; -using System.Text; - -namespace MinecraftClient.Cache -{ - public static class SessionCache - { - const string filename = "cache.bin"; - private static Dictionary sessions = new Dictionary(); - private static FileSystemWatcher cachemonitor = new FileSystemWatcher(); - - private static BinaryFormatter formatter = new BinaryFormatter(); - - public static bool Contains(string login) - { - return sessions.ContainsKey(login); - } - - public static void Store(string login, SessionToken session) - { - if (Contains(login)) - { - sessions[login] = session; - } - else - { - sessions.Add(login, session); - } - - if (Settings.CacheType == CacheType.DISK) - { - SaveToDisk(); - } - } - - public static SessionToken Get(string login) - { - return sessions[login]; - } - - public static bool LoadFromDisk() - { - cachemonitor.Path = AppDomain.CurrentDomain.BaseDirectory; - cachemonitor.IncludeSubdirectories = false; - cachemonitor.Filter = filename; - cachemonitor.NotifyFilter = NotifyFilters.LastWrite; - cachemonitor.Changed += new FileSystemEventHandler(OnChanged); - cachemonitor.EnableRaisingEvents = true; - - return ReadCacheFile(); - } - - public static void OnChanged(object source, FileSystemEventArgs e) - { - ReadCacheFile(); - } - - private static bool ReadCacheFile() - { - if (File.Exists(filename)) - { - try - { - using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - sessions = (Dictionary)formatter.Deserialize(fs); - return true; - } - } - catch (IOException ex) - { - Console.WriteLine("Error reading cached sessions from disk: " + ex.Message); - } - catch (SerializationException) - { - Console.WriteLine("Error getting sessions from cache file "); - } - } - return false; - } - - public static void SaveToDisk() - { - bool fileexists = File.Exists(filename); - - using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) - { - cachemonitor.EnableRaisingEvents = false; - if (fileexists) - { - fs.SetLength(0); - fs.Flush(); - } - - formatter.Serialize(fs, sessions); - cachemonitor.EnableRaisingEvents = true; - } - - } - - private static byte[] GetHash(FileStream fs, bool resetposition = true) - { - using (var md5 = MD5.Create()) - { - long pos = fs.Position; - byte[] hash = md5.ComputeHash(fs); - - fs.Position = resetposition ? pos : fs.Position; - return hash; - } - } - - private static bool HashesEqual(byte[] hash1, byte[] hash2) - { - if (hash1.Length != hash2.Length) - { - return false; - } - - for (int i = 0; i < hash1.Length; i++) - { - if (hash1[i] != hash2[i]) - { - return false; - } - } - - return true; - } - - } -} +using MinecraftClient.Protocol; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; + +namespace MinecraftClient.Cache +{ + /// + /// Handle sessions caching and storage. + /// + + public static class SessionCache + { + const string filename = "cache.bin"; + private static Dictionary sessions = new Dictionary(); + private static FileSystemWatcher cachemonitor = new FileSystemWatcher(); + + private static BinaryFormatter formatter = new BinaryFormatter(); + + /// + /// Retrieve whether SessionCache contains a session for the given login. + /// + /// User login used with Minecraft.net + /// TRUE if session is available + + public static bool Contains(string login) + { + return sessions.ContainsKey(login); + } + + /// + /// Store a session and save it to disk if required. + /// + /// User login used with Minecraft.net + /// User session token used with Minecraft.net + + public static void Store(string login, SessionToken session) + { + if (Contains(login)) + { + sessions[login] = session; + } + else + { + sessions.Add(login, session); + } + + if (Settings.CacheType == CacheType.DISK) + { + SaveToDisk(); + } + } + + /// + /// Retrieve a session token for the given login. + /// + /// User login used with Minecraft.net + /// SessionToken for given login + + public static SessionToken Get(string login) + { + return sessions[login]; + } + + /// + /// Initialize cache monitoring to keep cache updated with external changes. + /// + /// TRUE if session tokens are seeded from file + + public static bool InitializeDiskCache() + { + cachemonitor.Path = AppDomain.CurrentDomain.BaseDirectory; + cachemonitor.IncludeSubdirectories = false; + cachemonitor.Filter = filename; + cachemonitor.NotifyFilter = NotifyFilters.LastWrite; + cachemonitor.Changed += new FileSystemEventHandler(OnChanged); + cachemonitor.EnableRaisingEvents = true; + + return LoadFromDisk(); + } + + /// + /// Reloads cache on external cache file change. + /// + /// Sender + /// Event data + + private static void OnChanged(object source, FileSystemEventArgs e) + { + LoadFromDisk(); + } + + /// + /// Reads cache file and loads SessionTokens into SessionCache. + /// + /// True if data is successfully loaded + + private static bool LoadFromDisk() + { + if (File.Exists(filename)) + { + try + { + using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + sessions = (Dictionary)formatter.Deserialize(fs); + return true; + } + } + catch (IOException ex) + { + Console.WriteLine("Error reading cached sessions from disk: " + ex.Message); + } + catch (SerializationException) + { + Console.WriteLine("Malformed sessions from cache file "); + } + } + return false; + } + + /// + /// Saves SessionToken's from SessionCache into cache file. + /// + + private static void SaveToDisk() + { + bool fileexists = File.Exists(filename); + + using (FileStream fs = new FileStream(filename, 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; + } + + } + } +} diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 083e0d29..c8d15486 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -1,414 +1,414 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using MinecraftClient.Protocol; -using System.Reflection; -using System.Threading; -using MinecraftClient.Protocol.Handlers.Forge; - -namespace MinecraftClient -{ - /// - /// Minecraft Console Client by ORelio (c) 2012-2014. - /// Allows to connect to any Minecraft server, send and receive text, automated scripts. - /// This source code is released under the CDDL 1.0 License. - /// - - static class Program - { - private static McTcpClient Client; - public static string[] startupargs; - - public const string Version = "1.8.2"; - public const string MCLowestVersion = "1.4.6"; - public const string MCHighestVersion = "1.8.8"; - - private static Thread offlinePrompt = null; - private static bool useMcVersionOnce = false; - - /// - /// The main entry point of Minecraft Console Client - /// - - static void Main(string[] args) - { - Console.WriteLine("Console Client for MC {0} to {1} - v{2} - By ORelio & Contributors", MCLowestVersion, MCHighestVersion, Version); - - //Basic Input/Output ? - if (args.Length >= 1 && args[args.Length - 1] == "BasicIO") - { - ConsoleIO.basicIO = true; - Console.OutputEncoding = Console.InputEncoding = Encoding.GetEncoding(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage); - args = args.Where(o => !Object.ReferenceEquals(o, args[args.Length - 1])).ToArray(); - } - - //Process ini configuration file - if (args.Length >= 1 && System.IO.File.Exists(args[0]) && System.IO.Path.GetExtension(args[0]).ToLower() == ".ini") - { - Settings.LoadSettings(args[0]); - - //remove ini configuration file from arguments array - List args_tmp = args.ToList(); - args_tmp.RemoveAt(0); - args = args_tmp.ToArray(); - } - else if (System.IO.File.Exists("MinecraftClient.ini")) - { - Settings.LoadSettings("MinecraftClient.ini"); - } - else Settings.WriteDefaultSettings("MinecraftClient.ini"); - - //Other command-line arguments - if (args.Length >= 1) - { - Settings.Login = args[0]; - if (args.Length >= 2) - { - Settings.Password = args[1]; - if (args.Length >= 3) - { - Settings.SetServerIP(args[2]); - - //Single command? - if (args.Length >= 4) - { - Settings.SingleCommand = args[3]; - } - } - } - } - - if (Settings.ConsoleTitle != "") - { - Settings.Username = "New Window"; - Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); - } - - //Load cached sessions from disk if necessary - if (Settings.CacheType == Cache.CacheType.DISK) - { - ConsoleIO.WriteLineFormatted(Cache.SessionCache.LoadFromDisk() ? "Cached sessions loaded." : "§8Cached sessions could not be loaded from disk"); - } - - //Asking the user to type in missing data such as Username and Password - - if (Settings.Login == "") - { - Console.Write(ConsoleIO.basicIO ? "Please type the username of your choice.\n" : "Username : "); - Settings.Login = Console.ReadLine(); - } - if (Settings.Password == "" && (Settings.CacheType == Cache.CacheType.NONE || !Cache.SessionCache.Contains(Settings.Login))) - { - RequestPassword(); - } - - startupargs = args; - InitializeClient(); - } - - /// - /// Reduest user to submit password. - /// - private static void RequestPassword() - { - Console.Write(ConsoleIO.basicIO ? "Please type the password for " + Settings.Login + ".\n" : "Password : "); - Settings.Password = ConsoleIO.basicIO ? Console.ReadLine() : ConsoleIO.ReadPassword(); - if (Settings.Password == "") { Settings.Password = "-"; } - if (!ConsoleIO.basicIO) - { - //Hide password length - Console.CursorTop--; Console.Write("Password : <******>"); - for (int i = 19; i < Console.BufferWidth; i++) { Console.Write(' '); } - } - } - - /// - /// Start a new Client - /// - - private static void InitializeClient() - { - SessionToken session = new SessionToken(); - - ProtocolHandler.LoginResult result = ProtocolHandler.LoginResult.LoginRequired; - - if (Settings.Password == "-") - { - ConsoleIO.WriteLineFormatted("§8You chose to run in offline mode."); - result = ProtocolHandler.LoginResult.Success; - session.PlayerID = "0"; - session.PlayerName = Settings.Login; - } - else - { - // Validate cached session or login new session. - if (Settings.CacheType != Cache.CacheType.NONE && Cache.SessionCache.Contains(Settings.Login)) - { - session = Cache.SessionCache.Get(Settings.Login); - result = ProtocolHandler.GetTokenValidation(session); - - if (result != ProtocolHandler.LoginResult.Success && Settings.Password == "") - { - RequestPassword(); - } - - Console.WriteLine("Cached session is " + (result == ProtocolHandler.LoginResult.Success ? "valid." : "invalid.")); - - } - - if (result != ProtocolHandler.LoginResult.Success) - { - Console.WriteLine("Connecting to Minecraft.net..."); - result = ProtocolHandler.GetLogin(Settings.Login, Settings.Password, out session); - - if (result == ProtocolHandler.LoginResult.Success && Settings.CacheType != Cache.CacheType.NONE) - { - Cache.SessionCache.Store(Settings.Login, session); - } - } - - } - - if (result == ProtocolHandler.LoginResult.Success) - { - Settings.Username = session.PlayerName; - - if (Settings.ConsoleTitle != "") - Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); - - if (Settings.playerHeadAsIcon) - ConsoleIcon.setPlayerIconAsync(Settings.Username); - - Console.WriteLine("Success. (session ID: " + session.ID + ')'); - - //ProtocolHandler.RealmsListWorlds(Settings.Username, PlayerID, sessionID); //TODO REMOVE - - if (Settings.ServerIP == "") - { - Console.Write("Server IP : "); - Settings.SetServerIP(Console.ReadLine()); - } - - //Get server version - int protocolversion = 0; - ForgeInfo forgeInfo = null; - - if (Settings.ServerVersion != "" && Settings.ServerVersion.ToLower() != "auto") - { - protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(Settings.ServerVersion); - - if (protocolversion != 0) - { - ConsoleIO.WriteLineFormatted("§8Using Minecraft version " + Settings.ServerVersion + " (protocol v" + protocolversion + ')'); - } - else ConsoleIO.WriteLineFormatted("§8Unknown or not supported MC version '" + Settings.ServerVersion + "'.\nSwitching to autodetection mode."); - - if (useMcVersionOnce) - { - useMcVersionOnce = false; - Settings.ServerVersion = ""; - } - } - - if (protocolversion == 0) - { - Console.WriteLine("Retrieving Server Info..."); - if (!ProtocolHandler.GetServerInfo(Settings.ServerIP, Settings.ServerPort, ref protocolversion, ref forgeInfo)) - { - HandleFailure("Failed to ping this IP.", true, ChatBots.AutoRelog.DisconnectReason.ConnectionLost); - return; - } - } - - if (forgeInfo != null && !forgeInfo.Mods.Any()) - { - forgeInfo = null; - } - - if (protocolversion != 0) - { - try - { - //Start the main TCP client - if (Settings.SingleCommand != "") - { - Client = new McTcpClient(session.PlayerName, session.PlayerID, session.ID, Settings.ServerIP, Settings.ServerPort, protocolversion, forgeInfo, Settings.SingleCommand); - } - else Client = new McTcpClient(session.PlayerName, session.PlayerID, session.ID, protocolversion, forgeInfo, Settings.ServerIP, Settings.ServerPort); - - //Update console title - if (Settings.ConsoleTitle != "") - Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); - } - catch (NotSupportedException) { HandleFailure("Cannot connect to the server : This version is not supported !", true); } - } - else HandleFailure("Failed to determine server version.", true); - } - else - { - Console.ForegroundColor = ConsoleColor.Gray; - string failureMessage = "Minecraft Login failed : "; - switch (result) - { - case ProtocolHandler.LoginResult.AccountMigrated: failureMessage += "Account migrated, use e-mail as username."; break; - case ProtocolHandler.LoginResult.ServiceUnavailable: failureMessage += "Login servers are unavailable. Please try again later."; break; - case ProtocolHandler.LoginResult.WrongPassword: failureMessage += "Incorrect password."; break; - case ProtocolHandler.LoginResult.NotPremium: failureMessage += "User not premium."; break; - case ProtocolHandler.LoginResult.OtherError: failureMessage += "Network error."; break; - case ProtocolHandler.LoginResult.SSLError: failureMessage += "SSL Error."; break; - default: failureMessage += "Unknown Error."; break; - } - if (result == ProtocolHandler.LoginResult.SSLError && isUsingMono) - { - ConsoleIO.WriteLineFormatted("§8It appears that you are using Mono to run this program." - + '\n' + "The first time, you have to import HTTPS certificates using:" - + '\n' + "mozroots --import --ask-remove"); - return; - } - HandleFailure(failureMessage, false, ChatBot.DisconnectReason.LoginRejected); - } - } - - /// - /// Disconnect the current client from the server and restart it - /// - - public static void Restart() - { - new Thread(new ThreadStart(delegate - { - if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); } - if (offlinePrompt != null) { offlinePrompt.Abort(); offlinePrompt = null; ConsoleIO.Reset(); } - Console.WriteLine("Restarting Minecraft Console Client..."); - InitializeClient(); - })).Start(); - } - - /// - /// Disconnect the current client from the server and exit the app - /// - - public static void Exit() - { - new Thread(new ThreadStart(delegate - { - if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); } - if (offlinePrompt != null) { offlinePrompt.Abort(); offlinePrompt = null; ConsoleIO.Reset(); } - if (Settings.playerHeadAsIcon) { ConsoleIcon.revertToCMDIcon(); } - Environment.Exit(0); - })).Start(); - } - - /// - /// Handle fatal errors such as ping failure, login failure, server disconnection, and so on. - /// Allows AutoRelog to perform on fatal errors, prompt for server version, and offline commands. - /// - /// Error message to display and optionally pass to AutoRelog bot - /// Specify if the error is related to an incompatible or unkown server version - /// If set, the error message will be processed by the AutoRelog bot - - public static void HandleFailure(string errorMessage = null, bool versionError = false, ChatBots.AutoRelog.DisconnectReason? disconnectReason = null) - { - if (!String.IsNullOrEmpty(errorMessage)) - { - ConsoleIO.Reset(); - while (Console.KeyAvailable) - Console.ReadKey(true); - Console.WriteLine(errorMessage); - - if (disconnectReason.HasValue) - { - if (ChatBots.AutoRelog.OnDisconnectStatic(disconnectReason.Value, errorMessage)) - return; //AutoRelog is triggering a restart of the client - } - } - - if (Settings.interactiveMode) - { - if (versionError) - { - Console.Write("Server version : "); - Settings.ServerVersion = Console.ReadLine(); - if (Settings.ServerVersion != "") - { - useMcVersionOnce = true; - Restart(); - return; - } - } - - if (offlinePrompt == null) - { - offlinePrompt = new Thread(new ThreadStart(delegate - { - string command = " "; - ConsoleIO.WriteLineFormatted("Not connected to any server. Use '" + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + "help' for help."); - ConsoleIO.WriteLineFormatted("Or press Enter to exit Minecraft Console Client."); - while (command.Length > 0) - { - if (!ConsoleIO.basicIO) { ConsoleIO.Write('>'); } - command = Console.ReadLine().Trim(); - if (command.Length > 0) - { - string message = ""; - - if (Settings.internalCmdChar != ' ' - && command[0] == Settings.internalCmdChar) - command = command.Substring(1); - - if (command.StartsWith("reco")) - { - message = new Commands.Reco().Run(null, Settings.ExpandVars(command)); - } - else if (command.StartsWith("connect")) - { - message = new Commands.Connect().Run(null, Settings.ExpandVars(command)); - } - else if (command.StartsWith("exit") || command.StartsWith("quit")) - { - message = new Commands.Exit().Run(null, Settings.ExpandVars(command)); - } - else if (command.StartsWith("help")) - { - ConsoleIO.WriteLineFormatted("§8MCC: " + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + new Commands.Reco().CMDDesc); - ConsoleIO.WriteLineFormatted("§8MCC: " + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + new Commands.Connect().CMDDesc); - } - else ConsoleIO.WriteLineFormatted("§8Unknown command '" + command.Split(' ')[0] + "'."); - - if (message != "") - ConsoleIO.WriteLineFormatted("§8MCC: " + message); - } - } - })); - offlinePrompt.Start(); - } - } - } - - /// - /// Detect if the user is running Minecraft Console Client through Mono - /// - - public static bool isUsingMono - { - get - { - return Type.GetType("Mono.Runtime") != null; - } - } - - /// - /// Enumerate types in namespace through reflection - /// - /// Namespace to process - /// Assembly to use. Default is Assembly.GetExecutingAssembly() - /// - - public static Type[] GetTypesInNamespace(string nameSpace, Assembly assembly = null) - { - if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); } - return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MinecraftClient.Protocol; +using System.Reflection; +using System.Threading; +using MinecraftClient.Protocol.Handlers.Forge; + +namespace MinecraftClient +{ + /// + /// Minecraft Console Client by ORelio (c) 2012-2014. + /// Allows to connect to any Minecraft server, send and receive text, automated scripts. + /// This source code is released under the CDDL 1.0 License. + /// + + static class Program + { + private static McTcpClient Client; + public static string[] startupargs; + + public const string Version = "1.8.2"; + public const string MCLowestVersion = "1.4.6"; + public const string MCHighestVersion = "1.8.8"; + + private static Thread offlinePrompt = null; + private static bool useMcVersionOnce = false; + + /// + /// The main entry point of Minecraft Console Client + /// + + static void Main(string[] args) + { + Console.WriteLine("Console Client for MC {0} to {1} - v{2} - By ORelio & Contributors", MCLowestVersion, MCHighestVersion, Version); + + //Basic Input/Output ? + if (args.Length >= 1 && args[args.Length - 1] == "BasicIO") + { + ConsoleIO.basicIO = true; + Console.OutputEncoding = Console.InputEncoding = Encoding.GetEncoding(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage); + args = args.Where(o => !Object.ReferenceEquals(o, args[args.Length - 1])).ToArray(); + } + + //Process ini configuration file + if (args.Length >= 1 && System.IO.File.Exists(args[0]) && System.IO.Path.GetExtension(args[0]).ToLower() == ".ini") + { + Settings.LoadSettings(args[0]); + + //remove ini configuration file from arguments array + List args_tmp = args.ToList(); + args_tmp.RemoveAt(0); + args = args_tmp.ToArray(); + } + else if (System.IO.File.Exists("MinecraftClient.ini")) + { + Settings.LoadSettings("MinecraftClient.ini"); + } + else Settings.WriteDefaultSettings("MinecraftClient.ini"); + + //Other command-line arguments + if (args.Length >= 1) + { + Settings.Login = args[0]; + if (args.Length >= 2) + { + Settings.Password = args[1]; + if (args.Length >= 3) + { + Settings.SetServerIP(args[2]); + + //Single command? + if (args.Length >= 4) + { + Settings.SingleCommand = args[3]; + } + } + } + } + + if (Settings.ConsoleTitle != "") + { + Settings.Username = "New Window"; + Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); + } + + //Load cached sessions from disk if necessary + if (Settings.CacheType == Cache.CacheType.DISK) + { + Console.WriteLine(Cache.SessionCache.InitializeDiskCache() ? "Cached sessions loaded." : "§8Cached sessions could not be loaded from disk"); + } + + //Asking the user to type in missing data such as Username and Password + + if (Settings.Login == "") + { + Console.Write(ConsoleIO.basicIO ? "Please type the username of your choice.\n" : "Username : "); + Settings.Login = Console.ReadLine(); + } + if (Settings.Password == "" && (Settings.CacheType == Cache.CacheType.NONE || !Cache.SessionCache.Contains(Settings.Login))) + { + RequestPassword(); + } + + startupargs = args; + InitializeClient(); + } + + /// + /// Reduest user to submit password. + /// + private static void RequestPassword() + { + Console.Write(ConsoleIO.basicIO ? "Please type the password for " + Settings.Login + ".\n" : "Password : "); + Settings.Password = ConsoleIO.basicIO ? Console.ReadLine() : ConsoleIO.ReadPassword(); + if (Settings.Password == "") { Settings.Password = "-"; } + if (!ConsoleIO.basicIO) + { + //Hide password length + Console.CursorTop--; Console.Write("Password : <******>"); + for (int i = 19; i < Console.BufferWidth; i++) { Console.Write(' '); } + } + } + + /// + /// Start a new Client + /// + + private static void InitializeClient() + { + SessionToken session = new SessionToken(); + + ProtocolHandler.LoginResult result = ProtocolHandler.LoginResult.LoginRequired; + + if (Settings.Password == "-") + { + ConsoleIO.WriteLineFormatted("§8You chose to run in offline mode."); + result = ProtocolHandler.LoginResult.Success; + session.PlayerID = "0"; + session.PlayerName = Settings.Login; + } + else + { + // Validate cached session or login new session. + if (Settings.CacheType != Cache.CacheType.NONE && Cache.SessionCache.Contains(Settings.Login)) + { + session = Cache.SessionCache.Get(Settings.Login); + result = ProtocolHandler.GetTokenValidation(session); + + if (result != ProtocolHandler.LoginResult.Success && Settings.Password == "") + { + RequestPassword(); + } + + Console.WriteLine("Cached session is " + (result == ProtocolHandler.LoginResult.Success ? "valid." : "invalid.")); + + } + + if (result != ProtocolHandler.LoginResult.Success) + { + Console.WriteLine("Connecting to Minecraft.net..."); + result = ProtocolHandler.GetLogin(Settings.Login, Settings.Password, out session); + + if (result == ProtocolHandler.LoginResult.Success && Settings.CacheType != Cache.CacheType.NONE) + { + Cache.SessionCache.Store(Settings.Login, session); + } + } + + } + + if (result == ProtocolHandler.LoginResult.Success) + { + Settings.Username = session.PlayerName; + + if (Settings.ConsoleTitle != "") + Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); + + if (Settings.playerHeadAsIcon) + ConsoleIcon.setPlayerIconAsync(Settings.Username); + + Console.WriteLine("Success. (session ID: " + session.ID + ')'); + + //ProtocolHandler.RealmsListWorlds(Settings.Username, PlayerID, sessionID); //TODO REMOVE + + if (Settings.ServerIP == "") + { + Console.Write("Server IP : "); + Settings.SetServerIP(Console.ReadLine()); + } + + //Get server version + int protocolversion = 0; + ForgeInfo forgeInfo = null; + + if (Settings.ServerVersion != "" && Settings.ServerVersion.ToLower() != "auto") + { + protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(Settings.ServerVersion); + + if (protocolversion != 0) + { + ConsoleIO.WriteLineFormatted("§8Using Minecraft version " + Settings.ServerVersion + " (protocol v" + protocolversion + ')'); + } + else ConsoleIO.WriteLineFormatted("§8Unknown or not supported MC version '" + Settings.ServerVersion + "'.\nSwitching to autodetection mode."); + + if (useMcVersionOnce) + { + useMcVersionOnce = false; + Settings.ServerVersion = ""; + } + } + + if (protocolversion == 0) + { + Console.WriteLine("Retrieving Server Info..."); + if (!ProtocolHandler.GetServerInfo(Settings.ServerIP, Settings.ServerPort, ref protocolversion, ref forgeInfo)) + { + HandleFailure("Failed to ping this IP.", true, ChatBots.AutoRelog.DisconnectReason.ConnectionLost); + return; + } + } + + if (forgeInfo != null && !forgeInfo.Mods.Any()) + { + forgeInfo = null; + } + + if (protocolversion != 0) + { + try + { + //Start the main TCP client + if (Settings.SingleCommand != "") + { + Client = new McTcpClient(session.PlayerName, session.PlayerID, session.ID, Settings.ServerIP, Settings.ServerPort, protocolversion, forgeInfo, Settings.SingleCommand); + } + else Client = new McTcpClient(session.PlayerName, session.PlayerID, session.ID, protocolversion, forgeInfo, Settings.ServerIP, Settings.ServerPort); + + //Update console title + if (Settings.ConsoleTitle != "") + Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); + } + catch (NotSupportedException) { HandleFailure("Cannot connect to the server : This version is not supported !", true); } + } + else HandleFailure("Failed to determine server version.", true); + } + else + { + Console.ForegroundColor = ConsoleColor.Gray; + string failureMessage = "Minecraft Login failed : "; + switch (result) + { + case ProtocolHandler.LoginResult.AccountMigrated: failureMessage += "Account migrated, use e-mail as username."; break; + case ProtocolHandler.LoginResult.ServiceUnavailable: failureMessage += "Login servers are unavailable. Please try again later."; break; + case ProtocolHandler.LoginResult.WrongPassword: failureMessage += "Incorrect password."; break; + case ProtocolHandler.LoginResult.NotPremium: failureMessage += "User not premium."; break; + case ProtocolHandler.LoginResult.OtherError: failureMessage += "Network error."; break; + case ProtocolHandler.LoginResult.SSLError: failureMessage += "SSL Error."; break; + default: failureMessage += "Unknown Error."; break; + } + if (result == ProtocolHandler.LoginResult.SSLError && isUsingMono) + { + ConsoleIO.WriteLineFormatted("§8It appears that you are using Mono to run this program." + + '\n' + "The first time, you have to import HTTPS certificates using:" + + '\n' + "mozroots --import --ask-remove"); + return; + } + HandleFailure(failureMessage, false, ChatBot.DisconnectReason.LoginRejected); + } + } + + /// + /// Disconnect the current client from the server and restart it + /// + + public static void Restart() + { + new Thread(new ThreadStart(delegate + { + if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); } + if (offlinePrompt != null) { offlinePrompt.Abort(); offlinePrompt = null; ConsoleIO.Reset(); } + Console.WriteLine("Restarting Minecraft Console Client..."); + InitializeClient(); + })).Start(); + } + + /// + /// Disconnect the current client from the server and exit the app + /// + + public static void Exit() + { + new Thread(new ThreadStart(delegate + { + if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); } + if (offlinePrompt != null) { offlinePrompt.Abort(); offlinePrompt = null; ConsoleIO.Reset(); } + if (Settings.playerHeadAsIcon) { ConsoleIcon.revertToCMDIcon(); } + Environment.Exit(0); + })).Start(); + } + + /// + /// Handle fatal errors such as ping failure, login failure, server disconnection, and so on. + /// Allows AutoRelog to perform on fatal errors, prompt for server version, and offline commands. + /// + /// Error message to display and optionally pass to AutoRelog bot + /// Specify if the error is related to an incompatible or unkown server version + /// If set, the error message will be processed by the AutoRelog bot + + public static void HandleFailure(string errorMessage = null, bool versionError = false, ChatBots.AutoRelog.DisconnectReason? disconnectReason = null) + { + if (!String.IsNullOrEmpty(errorMessage)) + { + ConsoleIO.Reset(); + while (Console.KeyAvailable) + Console.ReadKey(true); + Console.WriteLine(errorMessage); + + if (disconnectReason.HasValue) + { + if (ChatBots.AutoRelog.OnDisconnectStatic(disconnectReason.Value, errorMessage)) + return; //AutoRelog is triggering a restart of the client + } + } + + if (Settings.interactiveMode) + { + if (versionError) + { + Console.Write("Server version : "); + Settings.ServerVersion = Console.ReadLine(); + if (Settings.ServerVersion != "") + { + useMcVersionOnce = true; + Restart(); + return; + } + } + + if (offlinePrompt == null) + { + offlinePrompt = new Thread(new ThreadStart(delegate + { + string command = " "; + ConsoleIO.WriteLineFormatted("Not connected to any server. Use '" + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + "help' for help."); + ConsoleIO.WriteLineFormatted("Or press Enter to exit Minecraft Console Client."); + while (command.Length > 0) + { + if (!ConsoleIO.basicIO) { ConsoleIO.Write('>'); } + command = Console.ReadLine().Trim(); + if (command.Length > 0) + { + string message = ""; + + if (Settings.internalCmdChar != ' ' + && command[0] == Settings.internalCmdChar) + command = command.Substring(1); + + if (command.StartsWith("reco")) + { + message = new Commands.Reco().Run(null, Settings.ExpandVars(command)); + } + else if (command.StartsWith("connect")) + { + message = new Commands.Connect().Run(null, Settings.ExpandVars(command)); + } + else if (command.StartsWith("exit") || command.StartsWith("quit")) + { + message = new Commands.Exit().Run(null, Settings.ExpandVars(command)); + } + else if (command.StartsWith("help")) + { + ConsoleIO.WriteLineFormatted("§8MCC: " + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + new Commands.Reco().CMDDesc); + ConsoleIO.WriteLineFormatted("§8MCC: " + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + new Commands.Connect().CMDDesc); + } + else ConsoleIO.WriteLineFormatted("§8Unknown command '" + command.Split(' ')[0] + "'."); + + if (message != "") + ConsoleIO.WriteLineFormatted("§8MCC: " + message); + } + } + })); + offlinePrompt.Start(); + } + } + } + + /// + /// Detect if the user is running Minecraft Console Client through Mono + /// + + public static bool isUsingMono + { + get + { + return Type.GetType("Mono.Runtime") != null; + } + } + + /// + /// Enumerate types in namespace through reflection + /// + /// Namespace to process + /// Assembly to use. Default is Assembly.GetExecutingAssembly() + /// + + public static Type[] GetTypesInNamespace(string nameSpace, Assembly assembly = null) + { + if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); } + return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray(); + } + } +} diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs index 6fa2a5e4..6adad9ed 100644 --- a/MinecraftClient/Protocol/ProtocolHandler.cs +++ b/MinecraftClient/Protocol/ProtocolHandler.cs @@ -216,8 +216,6 @@ namespace MinecraftClient.Protocol } } - public enum ValidationResult { Validated, NewTokenRequired, Error }; - /// /// Validates whether accessToken must be refreshed /// @@ -250,8 +248,6 @@ namespace MinecraftClient.Protocol return LoginResult.OtherError; } } - - public enum NewTokenResult { Success, InvalidToken, NullError, OtherError } /// /// Refreshes invalid token