/// <summary> /// Manual HTTPS request since we must directly use a TcpClient because of the proxy. /// This method connects to the server, enables SSL, do the request and read the response. /// </summary> /// <param name="headers">Request headers and optional body (POST)</param> /// <param name="host">Host to connect to</param> /// <param name="result">Request result</param> /// <returns>HTTP Status code</returns> private static int doHTTPSRequest(List <string> headers, string host, ref string result) { string postResult = null; int statusCode = 520; AutoTimeout.Perform(() => { TcpClient client = ProxyHandler.newTcpClient(host, 443, true); SslStream stream = new SslStream(client.GetStream()); stream.AuthenticateAsClient(host); stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", headers.ToArray()))); System.IO.StreamReader sr = new System.IO.StreamReader(stream); string raw_result = sr.ReadToEnd(); if (raw_result.StartsWith("HTTP/1.1")) { postResult = raw_result.Substring(raw_result.IndexOf("\r\n\r\n") + 4); statusCode = Settings.str2int(raw_result.Split(' ')[1]); } else { statusCode = 520; //Web server is returning an unknown error } }, TimeSpan.FromSeconds(30)); result = postResult; return(statusCode); }
/// <summary> /// Send a http request to the server. Proxy is handled automatically /// </summary> /// <param name="method">Method in string representation</param> /// <param name="body">Optional request body</param> /// <returns></returns> private Response Send(string method, string body = "") { List <string> requestMessage = new List <string>() { string.Format("{0} {1} {2}", method.ToUpper(), path, httpVersion) // Request line }; foreach (string key in Headers) // Headers { var value = Headers[key]; requestMessage.Add(string.Format("{0}: {1}", key, value)); } requestMessage.Add(""); // <CR><LF> if (body != "") { requestMessage.Add(body); } else { requestMessage.Add(""); // <CR><LF> } if (Settings.DebugMessages) { foreach (string l in requestMessage) { ConsoleIO.WriteLine("< " + l); } } Response response = Response.Empty(); AutoTimeout.Perform(() => { TcpClient client = ProxyHandler.newTcpClient(host, port, true); Stream stream; if (isSecure) { stream = new SslStream(client.GetStream()); ((SslStream)stream).AuthenticateAsClient(host); } else { stream = client.GetStream(); } string h = string.Join("\r\n", requestMessage.ToArray()); byte[] data = Encoding.ASCII.GetBytes(h); stream.Write(data, 0, data.Length); stream.Flush(); StreamReader sr = new StreamReader(stream); string rawResult = sr.ReadToEnd(); response = ParseResponse(rawResult); try { sr.Close(); stream.Close(); client.Close(); } catch { } }, TimeSpan.FromSeconds(30)); return(response); }
/// <summary> /// Retrieve information about a Minecraft server /// </summary> /// <param name="serverIP">Server IP to ping</param> /// <param name="serverPort">Server Port to ping</param> /// <param name="protocolversion">Will contain protocol version, if ping successful</param> /// <returns>TRUE if ping was successful</returns> public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, ref ForgeInfo forgeInfo) { bool success = false; int protocolversionTmp = 0; ForgeInfo forgeInfoTmp = null; if (AutoTimeout.Perform(() => { try { if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp) || Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp)) { success = true; } else { Translations.WriteLineFormatted("error.unexpect_response"); } } catch (Exception e) { ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message)); } }, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30))) { if (protocolversion != 0 && protocolversion != protocolversionTmp) { Translations.WriteLineFormatted("error.version_different"); } if (protocolversion == 0 && protocolversionTmp <= 1) { Translations.WriteLineFormatted("error.no_version_report"); } if (protocolversion == 0) { protocolversion = protocolversionTmp; } forgeInfo = forgeInfoTmp; return(success); } else { Translations.WriteLineFormatted("error.connection_timeout"); return(false); } }
/// <summary> /// Retrieve information about a Minecraft server /// </summary> /// <param name="serverIP">Server IP to ping</param> /// <param name="serverPort">Server Port to ping</param> /// <param name="protocolversion">Will contain protocol version, if ping successful</param> /// <returns>TRUE if ping was successful</returns> public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, ref ForgeInfo forgeInfo) { bool success = false; int protocolversionTmp = 0; ForgeInfo forgeInfoTmp = null; if (AutoTimeout.Perform(() => { try { if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp) || Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp)) { success = true; } else { ConsoleIO.WriteLineFormatted("§8Unexpected response from the server (is that a Minecraft server?)"); } } catch (Exception e) { ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message)); } }, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30))) { if (protocolversion != 0 && protocolversion != protocolversionTmp) { ConsoleIO.WriteLineFormatted("§8Server reports a different version than manually set. Login may not work."); } if (protocolversion == 0 && protocolversionTmp <= 1) { ConsoleIO.WriteLineFormatted("§8Server does not report its protocol version, autodetection will not work."); } if (protocolversion == 0) { protocolversion = protocolversionTmp; } forgeInfo = forgeInfoTmp; return(success); } else { ConsoleIO.WriteLineFormatted("§8A timeout occured while attempting to connect to this IP."); return(false); } }
/// <summary> /// Retrieve information about a Minecraft server /// </summary> /// <param name="serverIP">Server IP to ping</param> /// <param name="serverPort">Server Port to ping</param> /// <param name="protocolversion">Will contain protocol version, if ping successful</param> /// <returns>TRUE if ping was successful</returns> public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, ref ForgeInfo forgeInfo) { bool success = false; int protocolversionTmp = 0; ForgeInfo forgeInfoTmp = null; if (AutoTimeout.Perform(() => { try { if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp) || Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp)) { success = true; } else { ConsoleIO.WriteLineFormatted("§c[警告]§8无法连接至非Minecraft服务器之外的服务器!"); } } catch (Exception e) { ConsoleIO.WriteLineFormatted(String.Format("§e[信息]§8{0}: {1}", e.GetType().FullName, e.Message)); } }, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30))) { if (protocolversion != 0 && protocolversion != protocolversionTmp) { ConsoleIO.WriteLineFormatted("§e[信息]§8服务器发送了不同的版本,可能无法登入,请手动输入版本."); } if (protocolversion == 0 && protocolversionTmp <= 1) { ConsoleIO.WriteLineFormatted("§e[信息]§8服务器未通知协议版本,可能会无法登入,请手动输入版本."); } if (protocolversion == 0) { protocolversion = protocolversionTmp; } forgeInfo = forgeInfoTmp; return(success); } else { ConsoleIO.WriteLineFormatted("§c[错误]§8连接超时."); return(false); } }
/// <summary> /// Perform a DNS lookup for a Minecraft Service using the specified domain name /// </summary> /// <param name="domain">Input domain name, updated with target host if any, else left untouched</param> /// <param name="port">Updated with target port if any, else left untouched</param> /// <returns>TRUE if a Minecraft Service was found.</returns> public static bool MinecraftServiceLookup(ref string domain, ref ushort port) { bool foundService = false; string domainVal = domain; ushort portVal = port; if (!String.IsNullOrEmpty(domain) && domain.Any(c => char.IsLetter(c))) { AutoTimeout.Perform(() => { try { Translations.WriteLine("mcc.resolve", domainVal); Heijden.DNS.Response response = new Heijden.DNS.Resolver().Query("_minecraft._tcp." + domainVal, Heijden.DNS.QType.SRV); Heijden.DNS.RecordSRV[] srvRecords = response.RecordsSRV; if (srvRecords != null && srvRecords.Any()) { //Order SRV records by priority and weight, then randomly Heijden.DNS.RecordSRV result = srvRecords .OrderBy(record => record.PRIORITY) .ThenByDescending(record => record.WEIGHT) .ThenBy(record => Guid.NewGuid()) .First(); string target = result.TARGET.Trim('.'); ConsoleIO.WriteLineFormatted(Translations.Get("mcc.found", target, result.PORT, domainVal)); domainVal = target; portVal = result.PORT; foundService = true; } } catch (Exception e) { ConsoleIO.WriteLineFormatted(Translations.Get("mcc.not_found", domainVal, e.GetType().FullName, e.Message)); } }, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30)); } domain = domainVal; port = portVal; return(foundService); }
/// <summary> /// Manual HTTPS request since we must directly use a TcpClient because of the proxy. /// This method connects to the server, enables SSL, do the request and read the response. /// </summary> /// <param name="host">Host to connect to</param> /// <param name="endpoint">Endpoint for making the request</param> /// <param name="request">Request payload</param> /// <param name="result">Request result</param> /// <returns>HTTP Status code</returns> private static int doHTTPSPost(string host, string endpoint, string request, ref string result) { string postResult = null; int statusCode = 520; AutoTimeout.Perform(() => { TcpClient client = ProxyHandler.newTcpClient(host, 443); SslStream stream = new SslStream(client.GetStream()); stream.AuthenticateAsClient(host); List <String> http_request = new List <string>(); http_request.Add("POST " + endpoint + " HTTP/1.1"); http_request.Add("Host: " + host); http_request.Add("User-Agent: MCC/" + Program.Version); http_request.Add("Content-Type: application/json"); http_request.Add("Content-Length: " + Encoding.ASCII.GetBytes(request).Length); http_request.Add("Connection: close"); http_request.Add(""); http_request.Add(request); stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", http_request.ToArray()))); System.IO.StreamReader sr = new System.IO.StreamReader(stream); string raw_result = sr.ReadToEnd(); if (raw_result.StartsWith("HTTP/1.1")) { postResult = raw_result.Substring(raw_result.IndexOf("\r\n\r\n") + 4); statusCode = Settings.str2int(raw_result.Split(' ')[1]); } else { statusCode = 520; //Web server is returning an unknown error } }, 15000); result = postResult; return(statusCode); }
/// <summary> /// Retrieve information about a Minecraft server /// </summary> /// <param name="serverIP">Server IP to ping</param> /// <param name="serverPort">Server Port to ping</param> /// <param name="protocolversion">Will contain protocol version, if ping successful</param> /// <returns>TRUE if ping was successful</returns> public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, ref ForgeInfo forgeInfo) { bool success = false; int protocolversionTmp = 0; ForgeInfo forgeInfoTmp = null; if (AutoTimeout.Perform(() => { try { if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp) || Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp)) { success = true; } else { ConsoleIO.WriteLineFormatted("§8Unexpected response from the server (is that a Minecraft server?)"); } } catch (Exception e) { //ConsoleIO.WriteLineFormatted("ProtocolHandler:82"); //ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message)); } }, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30))) { protocolversion = protocolversionTmp; forgeInfo = forgeInfoTmp; return(success); } else { //ConsoleIO.WriteLineFormatted("§8A timeout occured while attempting to connect to this IP."); return(false); } }
/// <summary> /// Manual HTTPS request since we must directly use a TcpClient because of the proxy. /// This method connects to the server, enables SSL, do the request and read the response. /// </summary> /// <param name="headers">Request headers and optional body (POST)</param> /// <param name="host">Host to connect to</param> /// <param name="result">Request result</param> /// <returns>HTTP Status code</returns> private static int DoHTTPSRequest(List <string> headers, string host, ref string result) { string postResult = null; int statusCode = 520; Exception exception = null; AutoTimeout.Perform(() => { try { if (Settings.DebugMessages) { ConsoleIO.WriteLineFormatted("§8Performing request to " + host); } TcpClient client = ProxyHandler.newTcpClient(host, 443, true); SslStream stream = new SslStream(client.GetStream()); stream.AuthenticateAsClient(host); if (Settings.DebugMessages) { foreach (string line in headers) { ConsoleIO.WriteLineFormatted("§8> " + line); } } stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", headers.ToArray()))); System.IO.StreamReader sr = new System.IO.StreamReader(stream); string raw_result = sr.ReadToEnd(); if (Settings.DebugMessages) { ConsoleIO.WriteLine(""); foreach (string line in raw_result.Split('\n')) { ConsoleIO.WriteLineFormatted("§8< " + line); } } if (raw_result.StartsWith("HTTP/1.1")) { postResult = raw_result.Substring(raw_result.IndexOf("\r\n\r\n") + 4); statusCode = Settings.str2int(raw_result.Split(' ')[1]); } else { statusCode = 520; //Web server is returning an unknown error } } catch (Exception e) { if (!(e is System.Threading.ThreadAbortException)) { exception = e; } } }, TimeSpan.FromSeconds(30)); result = postResult; if (exception != null) { throw exception; } return(statusCode); }
/// <summary> /// Calculate a path from the start location to the destination location /// </summary> /// <remarks> /// Based on the A* pathfinding algorithm described on Wikipedia /// </remarks> /// <see href="https://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode"/> /// <param name="start">Start location</param> /// <param name="goal">Destination location</param> /// <param name="allowUnsafe">Allow possible but unsafe locations</param> /// <returns>A list of locations, or null if calculation failed</returns> public static Queue <Location> CalculatePath(World world, Location start, Location goal, bool allowUnsafe = false) { Queue <Location> result = null; AutoTimeout.Perform(() => { HashSet <Location> ClosedSet = new HashSet <Location>(); // The set of locations already evaluated. HashSet <Location> OpenSet = new HashSet <Location>(new[] { start }); // The set of tentative nodes to be evaluated, initially containing the start node Dictionary <Location, Location> Came_From = new Dictionary <Location, Location>(); // The map of navigated nodes. Dictionary <Location, int> g_score = new Dictionary <Location, int>(); //:= map with default value of Infinity g_score[start] = 0; // Cost from start along best known path. // Estimated total cost from start to goal through y. Dictionary <Location, int> f_score = new Dictionary <Location, int>(); //:= map with default value of Infinity f_score[start] = (int)start.DistanceSquared(goal); //heuristic_cost_estimate(start, goal) while (OpenSet.Count > 0) { Location current = //the node in OpenSet having the lowest f_score[] value OpenSet.Select(location => f_score.ContainsKey(location) ? new KeyValuePair <Location, int>(location, f_score[location]) : new KeyValuePair <Location, int>(location, int.MaxValue)) .OrderBy(pair => pair.Value).First().Key; if (current == goal) { //reconstruct_path(Came_From, goal) List <Location> total_path = new List <Location>(new[] { current }); while (Came_From.ContainsKey(current)) { current = Came_From[current]; total_path.Add(current); } total_path.Reverse(); result = new Queue <Location>(total_path); } OpenSet.Remove(current); ClosedSet.Add(current); foreach (Location neighbor in GetAvailableMoves(world, current, allowUnsafe)) { if (ClosedSet.Contains(neighbor)) { continue; // Ignore the neighbor which is already evaluated. } int tentative_g_score = g_score[current] + (int)current.DistanceSquared(neighbor); //dist_between(current,neighbor) // length of this path. if (!OpenSet.Contains(neighbor)) // Discover a new node { OpenSet.Add(neighbor); } else if (tentative_g_score >= g_score[neighbor]) { continue; // This is not a better path. } // This path is the best until now. Record it! Came_From[neighbor] = current; g_score[neighbor] = tentative_g_score; f_score[neighbor] = g_score[neighbor] + (int)neighbor.DistanceSquared(goal); //heuristic_cost_estimate(neighbor, goal) } } }, TimeSpan.FromSeconds(5)); return(result); }