diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 0ef5d9ec..bc276c9e 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -135,7 +135,6 @@ - diff --git a/MinecraftClient/Protocol/Handlers/Protocol17.cs b/MinecraftClient/Protocol/Handlers/Protocol17.cs deleted file mode 100644 index 63580074..00000000 --- a/MinecraftClient/Protocol/Handlers/Protocol17.cs +++ /dev/null @@ -1,589 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Net.Sockets; -using System.Threading; -using MinecraftClient.Crypto; -using MinecraftClient.Proxy; -using System.Security.Cryptography; - -namespace MinecraftClient.Protocol.Handlers -{ - /// - /// Implementation for Minecraft 1.7.X Protocol - /// - - class Protocol17Handler : IMinecraftCom - { - IMinecraftComHandler handler; - private bool autocomplete_received = false; - private string autocomplete_result = ""; - private bool encrypted = false; - private int protocolversion; - private Thread netRead; - Crypto.IAesStream s; - TcpClient c; - - public Protocol17Handler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler) - { - ConsoleIO.SetAutoCompleteEngine(this); - ChatParser.InitTranslations(); - this.c = Client; - this.protocolversion = ProtocolVersion; - this.handler = Handler; - } - - private Protocol17Handler(TcpClient Client) - { - this.c = Client; - } - - /// - /// Separate thread. Network reading loop. - /// - - private void Updater() - { - try - { - do - { - Thread.Sleep(100); - } - while (Update()); - } - catch (System.IO.IOException) { } - catch (SocketException) { } - catch (ObjectDisposedException) { } - - handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, ""); - } - - /// - /// Read and data from the network. Should be called on a separate thread. - /// - /// - - private bool Update() - { - handler.OnUpdate(); - if (c.Client == null || !c.Connected) { return false; } - int id = 0, size = 0; - try - { - while (c.Client.Available > 0) - { - size = readNextVarInt(); //Packet size - id = readNextVarInt(); //Packet ID - - switch (id) - { - case 0x00: - byte[] keepalive = new byte[4] { 0, 0, 0, 0 }; - Receive(keepalive, 0, 4, SocketFlags.None); - byte[] keepalive_packet = concatBytes(getVarInt(0x00), keepalive); - byte[] keepalive_tosend = concatBytes(getVarInt(keepalive_packet.Length), keepalive_packet); - Send(keepalive_tosend); - break; - case 0x02: - handler.OnTextReceived(ChatParser.ParseText(readNextString())); - break; - case 0x38: - int name_len = readNextVarInt(); - string name = readNextString(name_len); - bool online = readNextBool(); - short ping = readNextShort(); - readData(size - getVarInt(id).Length - getVarInt(name.Length).Length - name_len - 3); //Skip extradata - Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); - if (online) - { - handler.OnPlayerJoin(FakeUUID, name); - } - else handler.OnPlayerLeave(FakeUUID); - break; - case 0x3A: - int autocomplete_count = readNextVarInt(); - string tab_list = ""; - for (int i = 0; i < autocomplete_count; i++) - { - autocomplete_result = readNextString(); - if (autocomplete_result != "") - tab_list = tab_list + autocomplete_result + " "; - } - autocomplete_received = true; - tab_list = tab_list.Trim(); - if (tab_list.Length > 0) - ConsoleIO.WriteLineFormatted("§8" + tab_list, false); - break; - case 0x40: - handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString())); - return false; - default: - readData(size - getVarInt(id).Length); //Skip packet - break; - } - } - } - catch (SocketException) { return false; } - return true; - } - - /// - /// Start the updating thread. Should be called after login success. - /// - - private void StartUpdating() - { - netRead = new Thread(new ThreadStart(Updater)); - netRead.Name = "ProtocolPacketHandler"; - netRead.Start(); - } - - /// - /// Disconnect from the server, cancel network reading. - /// - - public void Dispose() - { - try - { - if (netRead != null) - { - netRead.Abort(); - c.Close(); - } - } - catch { } - } - - /// - /// Read some data and discard the result - /// - /// Amount of bytes to read - - private void readData(int offset) - { - if (offset > 0) - { - try - { - byte[] cache = new byte[offset]; - Receive(cache, 0, offset, SocketFlags.None); - } - catch (OutOfMemoryException) { } - } - } - - /// - /// Read a string from the network - /// - /// String length - /// The string - - private string readNextString(int length = -1) - { - if (length < 0) - length = readNextVarInt(); - if (length > 0) - { - byte[] cache = new byte[length]; - Receive(cache, 0, length, SocketFlags.None); - return Encoding.UTF8.GetString(cache); - } - else return ""; - } - - /// - /// Read a uuid from the network - /// - /// Cache of bytes to read from - /// The uuid - - private Guid readNextUUID() - { - byte[] cache = new byte[16]; - Receive(cache, 0, 16, SocketFlags.None); - return new Guid(cache); - } - - /// - /// Read a short from the network - /// - /// - - private short readNextShort() - { - byte[] tmp = new byte[2]; - Receive(tmp, 0, 2, SocketFlags.None); - Array.Reverse(tmp); - return BitConverter.ToInt16(tmp, 0); - } - - /// - /// Read a boolean from the network - /// - /// - - private bool readNextBool() - { - byte[] tmp = new byte[1]; - Receive(tmp, 0, 1, SocketFlags.None); - return tmp[0] != 0x00; - } - - /// - /// Read a byte array from the network - /// - /// The byte array - - private byte[] readNextByteArray() - { - byte[] tmp = new byte[2]; - Receive(tmp, 0, 2, SocketFlags.None); - Array.Reverse(tmp); - short len = BitConverter.ToInt16(tmp, 0); - byte[] data = new byte[len]; - Receive(data, 0, len, SocketFlags.None); - return data; - } - - /// - /// Read an integer from the network - /// - /// The integer - - private int readNextVarInt() - { - int i = 0; - int j = 0; - int k = 0; - byte[] tmp = new byte[1]; - while (true) - { - Receive(tmp, 0, 1, SocketFlags.None); - k = tmp[0]; - i |= (k & 0x7F) << j++ * 7; - if (j > 5) throw new OverflowException("VarInt too big"); - if ((k & 0x80) != 128) break; - } - return i; - } - - /// - /// Build an integer for sending over the network - /// - /// Integer to encode - /// Byte array for this integer - - private static byte[] getVarInt(int paramInt) - { - List bytes = new List(); - while ((paramInt & -128) != 0) - { - bytes.Add((byte)(paramInt & 127 | 128)); - paramInt = (int)(((uint)paramInt) >> 7); - } - bytes.Add((byte)paramInt); - return bytes.ToArray(); - } - - /// - /// Easily append several byte arrays - /// - /// Bytes to append - /// Array containing all the data - - private static byte[] concatBytes(params byte[][] bytes) - { - List result = new List(); - foreach (byte[] array in bytes) - result.AddRange(array); - return result.ToArray(); - } - - /// - /// C-like atoi function for parsing an int from string - /// - /// String to parse - /// Int parsed - - private static int atoi(string str) - { - return int.Parse(new string(str.Trim().TakeWhile(char.IsDigit).ToArray())); - } - - /// - /// Network reading method. Read bytes from the socket or encrypted socket. - /// - - private void Receive(byte[] buffer, int start, int offset, SocketFlags f) - { - int read = 0; - while (read < offset) - { - if (encrypted) - { - read += s.Read(buffer, start + read, offset - read); - } - else read += c.Client.Receive(buffer, start + read, offset - read, f); - } - } - - /// - /// Network sending method. Send bytes using the socket or encrypted socket. - /// - /// - - private void Send(byte[] buffer) - { - if (encrypted) - { - s.Write(buffer, 0, buffer.Length); - } - else c.Client.Send(buffer); - } - - /// - /// Do the Minecraft login. - /// - /// True if login successful - - public bool Login() - { - byte[] packet_id = getVarInt(0); - byte[] protocol_version = getVarInt(protocolversion); - byte[] server_adress_val = Encoding.UTF8.GetBytes(handler.GetServerHost()); - byte[] server_adress_len = getVarInt(server_adress_val.Length); - byte[] server_port = BitConverter.GetBytes((ushort)handler.GetServerPort()); Array.Reverse(server_port); - byte[] next_state = getVarInt(2); - byte[] handshake_packet = concatBytes(packet_id, protocol_version, server_adress_len, server_adress_val, server_port, next_state); - byte[] handshake_packet_tosend = concatBytes(getVarInt(handshake_packet.Length), handshake_packet); - - Send(handshake_packet_tosend); - - byte[] username_val = Encoding.UTF8.GetBytes(handler.GetUsername()); - byte[] username_len = getVarInt(username_val.Length); - byte[] login_packet = concatBytes(packet_id, username_len, username_val); - byte[] login_packet_tosend = concatBytes(getVarInt(login_packet.Length), login_packet); - - Send(login_packet_tosend); - - readNextVarInt(); //Packet size - int pid = readNextVarInt(); //Packet ID - if (pid == 0x00) //Login rejected - { - handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString())); - return false; - } - else if (pid == 0x01) //Encryption request - { - string serverID = readNextString(); - byte[] Serverkey = readNextByteArray(); - byte[] token = readNextByteArray(); - return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, Serverkey); - } - else if (pid == 0x02) //Login successful - { - ConsoleIO.WriteLineFormatted("§8Server is in offline mode."); - StartUpdating(); - return true; //No need to check session or start encryption - } - else return false; - } - - /// - /// Start network encryption. Automatically called by Login() if the server requests encryption. - /// - /// True if encryption was successful - - private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, byte[] serverKey) - { - System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverKey); - byte[] secretKey = CryptoHandler.GenerateAESPrivateKey(); - - ConsoleIO.WriteLineFormatted("§8Crypto keys & hash generated."); - - if (serverIDhash != "-") - { - Console.WriteLine("Checking Session..."); - if (!ProtocolHandler.SessionCheck(uuid, sessionID, CryptoHandler.getServerHash(serverIDhash, serverKey, secretKey))) - { - handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, "Failed to check session."); - return false; - } - } - - //Encrypt the data - byte[] key_enc = RSAService.Encrypt(secretKey, false); - byte[] token_enc = RSAService.Encrypt(token, false); - byte[] key_len = BitConverter.GetBytes((short)key_enc.Length); Array.Reverse(key_len); - byte[] token_len = BitConverter.GetBytes((short)token_enc.Length); Array.Reverse(token_len); - - //Encryption Response packet - byte[] packet_id = getVarInt(0x01); - byte[] encryption_response = concatBytes(packet_id, key_len, key_enc, token_len, token_enc); - byte[] encryption_response_tosend = concatBytes(getVarInt(encryption_response.Length), encryption_response); - Send(encryption_response_tosend); - - //Start client-side encryption - s = CryptoHandler.getAesStream(c.GetStream(), secretKey); - encrypted = true; - - //Read and skip the next packet - int received_packet_size = readNextVarInt(); - int received_packet_id = readNextVarInt(); - bool encryption_success = (received_packet_id == 0x02); - if (received_packet_id == 0) { handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString())); } - else readData(received_packet_size - getVarInt(received_packet_id).Length); - if (encryption_success) { StartUpdating(); } - return encryption_success; - } - - /// - /// Send a chat message to the server - /// - /// Message - /// True if properly sent - - public bool SendChatMessage(string message) - { - if (String.IsNullOrEmpty(message)) - return true; - try - { - byte[] packet_id = getVarInt(0x01); - byte[] message_val = Encoding.UTF8.GetBytes(message); - byte[] message_len = getVarInt(message_val.Length); - byte[] message_packet = concatBytes(packet_id, message_len, message_val); - byte[] message_packet_tosend = concatBytes(getVarInt(message_packet.Length), message_packet); - Send(message_packet_tosend); - return true; - } - catch (SocketException) { return false; } - catch (System.IO.IOException) { return false; } - } - - /// - /// Send a respawn packet to the server - /// - /// Message - /// True if properly sent - - public bool SendRespawnPacket() - { - try - { - byte[] packet_id = getVarInt(0x16); - byte[] action_id = new byte[] { 0 }; - byte[] respawn_packet = concatBytes(getVarInt(packet_id.Length + 1), packet_id, action_id); - Send(respawn_packet); - return true; - } - catch (SocketException) { return false; } - } - - /// - /// Disconnect from the server - /// - - public void Disconnect() - { - try - { - c.Close(); - } - catch (SocketException) { } - catch (System.IO.IOException) { } - catch (NullReferenceException) { } - catch (ObjectDisposedException) { } - } - - /// - /// Autocomplete text while typing username or command - /// - /// Text behind cursor - /// Completed text - - public string AutoComplete(string BehindCursor) - { - if (String.IsNullOrEmpty(BehindCursor)) - return ""; - - byte[] packet_id = getVarInt(0x14); - byte[] tocomplete_val = Encoding.UTF8.GetBytes(BehindCursor); - byte[] tocomplete_len = getVarInt(tocomplete_val.Length); - byte[] tabcomplete_packet = concatBytes(packet_id, tocomplete_len, tocomplete_val); - byte[] tabcomplete_packet_tosend = concatBytes(getVarInt(tabcomplete_packet.Length), tabcomplete_packet); - - autocomplete_received = false; - autocomplete_result = BehindCursor; - Send(tabcomplete_packet_tosend); - - int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms) - while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; } - return autocomplete_result; - } - - /// - /// Ping a Minecraft server to get information about the server - /// - /// True if ping was successful - - public static bool doPing(string host, int port, ref int protocolversion) - { - string version = ""; - TcpClient tcp = ProxyHandler.newTcpClient(host, port); - tcp.ReceiveBufferSize = 1024 * 1024; - - byte[] packet_id = getVarInt(0); - byte[] protocol_version = getVarInt(4); - byte[] server_adress_val = Encoding.UTF8.GetBytes(host); - byte[] server_adress_len = getVarInt(server_adress_val.Length); - byte[] server_port = BitConverter.GetBytes((ushort)port); Array.Reverse(server_port); - byte[] next_state = getVarInt(1); - byte[] packet = concatBytes(packet_id, protocol_version, server_adress_len, server_adress_val, server_port, next_state); - byte[] tosend = concatBytes(getVarInt(packet.Length), packet); - - tcp.Client.Send(tosend, SocketFlags.None); - - byte[] status_request = getVarInt(0); - byte[] request_packet = concatBytes(getVarInt(status_request.Length), status_request); - - tcp.Client.Send(request_packet, SocketFlags.None); - - Protocol17Handler ComTmp = new Protocol17Handler(tcp); - if (ComTmp.readNextVarInt() > 0) //Read Response length - { - if (ComTmp.readNextVarInt() == 0x00) //Read Packet ID - { - string result = ComTmp.readNextString(); //Get the Json data - if (!String.IsNullOrEmpty(result) && result.StartsWith("{") && result.EndsWith("}")) - { - Json.JSONData jsonData = Json.ParseJson(result); - if (jsonData.Type == Json.JSONData.DataType.Object && jsonData.Properties.ContainsKey("version")) - { - jsonData = jsonData.Properties["version"]; - - //Retrieve display name of the Minecraft version - if (jsonData.Properties.ContainsKey("name")) - version = jsonData.Properties["name"].StringValue; - - //Retrieve protocol version number for handling this server - if (jsonData.Properties.ContainsKey("protocol")) - protocolversion = atoi(jsonData.Properties["protocol"].StringValue); - - //Automatic fix for BungeeCord 1.8 not properly reporting protocol version - if (protocolversion < 47 && version.Split(' ').Contains("1.8")) - protocolversion = ProtocolHandler.MCVer2ProtocolVersion("1.8.0"); - - ConsoleIO.WriteLineFormatted("§8Server version : " + version + " (protocol v" + protocolversion + ")."); - return true; - } - } - } - } - return false; - } - } -} diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 509bfb92..4e663ae2 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -6,24 +6,28 @@ using System.Net.Sockets; using System.Threading; using MinecraftClient.Crypto; using MinecraftClient.Proxy; +using System.Security.Cryptography; namespace MinecraftClient.Protocol.Handlers { /// - /// Implementation for Minecraft 1.8.X Protocol + /// Implementation for Minecraft 1.7.X and 1.8.X Protocols /// class Protocol18Handler : IMinecraftCom { - IMinecraftComHandler handler; + private const int MC18Version = 47; + private int compression_treshold = 0; private bool autocomplete_received = false; private string autocomplete_result = ""; private bool login_phase = true; private bool encrypted = false; private int protocolversion; - private Thread netRead; - Crypto.IAesStream s; + + IMinecraftComHandler handler; + Thread netRead; + IAesStream s; TcpClient c; public Protocol18Handler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler) @@ -95,7 +99,9 @@ namespace MinecraftClient.Protocol.Handlers int size = readNextVarIntRAW(); //Packet size packetData = readDataRAW(size); //Packet contents - if (compression_treshold > 0) //Handle packet decompression + //Handle packet decompression + if (protocolversion >= MC18Version + && compression_treshold > 0) { int size_uncompressed = readNextVarInt(ref packetData); if (size_uncompressed != 0) // != 0 means compressed, let's decompress @@ -114,12 +120,20 @@ namespace MinecraftClient.Protocol.Handlers private bool handlePacket(int packetID, byte[] packetData) { + if (new[] { 0x00, 0x02, 0x38, 0x3A, 0x40 }.Contains(packetID)) + ConsoleIO.WriteLineFormatted("§aP 0x" + packetID.ToString("x2")); //Process + else if (packetID <= 64) + ConsoleIO.WriteLineFormatted("§fS 0x" + packetID.ToString("x2")); //Skip + else + ConsoleIO.WriteLineFormatted("§c? 0x" + packetID.ToString("x2")); //Invalid + if (login_phase) { switch (packetID) //Packet IDs are different while logging in { case 0x03: - compression_treshold = readNextVarInt(ref packetData); + if (protocolversion >= MC18Version) + compression_treshold = readNextVarInt(ref packetData); break; default: return false; //Ignored packet @@ -130,31 +144,44 @@ namespace MinecraftClient.Protocol.Handlers switch (packetID) { case 0x00: //Keep-Alive - SendPacket(0x00, getVarInt(readNextVarInt(ref packetData))); + SendPacket(0x00, packetData); break; case 0x02: //Chat message handler.OnTextReceived(ChatParser.ParseText(readNextString(ref packetData))); break; case 0x38: //Player List update - int action = readNextVarInt(ref packetData); - int numActions = readNextVarInt(ref packetData); - for (int i = 0; i < numActions; i++) + if (protocolversion >= MC18Version) { - Guid uuid = readNextUUID(ref packetData); - switch (action) + int action = readNextVarInt(ref packetData); + int numActions = readNextVarInt(ref packetData); + for (int i = 0; i < numActions; i++) { - case 0x00: //Player Join - string name = readNextString(ref packetData); - handler.OnPlayerJoin(uuid, name); - break; - case 0x04: //Player Leave - handler.OnPlayerLeave(uuid); - break; - default: - //Unknown player list item type - break; + Guid uuid = readNextUUID(ref packetData); + switch (action) + { + case 0x00: //Player Join + string name = readNextString(ref packetData); + handler.OnPlayerJoin(uuid, name); + break; + case 0x04: //Player Leave + handler.OnPlayerLeave(uuid); + break; + default: + //Unknown player list item type + break; + } } } + else //MC 1.7.X does not provide UUID in tab-list updates + { + string name = readNextString(ref packetData); + bool online = readNextBool(ref packetData); + short ping = readNextShort(ref packetData); + Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); + if (online) + handler.OnPlayerJoin(FakeUUID, name); + else handler.OnPlayerLeave(FakeUUID); + } break; case 0x3A: //Tab-Complete Result int autocomplete_count = readNextVarInt(ref packetData); @@ -174,7 +201,8 @@ namespace MinecraftClient.Protocol.Handlers handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString(ref packetData))); return false; case 0x46: //Network Compression Treshold Info - compression_treshold = readNextVarInt(ref packetData); + if (protocolversion >= MC18Version) + compression_treshold = readNextVarInt(ref packetData); break; default: return false; //Ignored packet @@ -239,18 +267,11 @@ namespace MinecraftClient.Protocol.Handlers /// Cache of bytes to read from /// The data read from the cache as an array - private byte[] readData(int offset, ref byte[] cache) + private static byte[] readData(int offset, ref byte[] cache) { - List read = new List(); - List list = new List(cache); - while (offset > 0 && list.Count > 0) - { - read.Add(list[0]); - list.RemoveAt(0); - offset--; - } - cache = list.ToArray(); - return read.ToArray(); + byte[] result = cache.Take(offset).ToArray(); + cache = cache.Skip(offset).ToArray(); + return result; } /// @@ -259,7 +280,7 @@ namespace MinecraftClient.Protocol.Handlers /// Cache of bytes to read from /// The string - private string readNextString(ref byte[] cache) + private static string readNextString(ref byte[] cache) { int length = readNextVarInt(ref cache); if (length > 0) @@ -269,13 +290,35 @@ namespace MinecraftClient.Protocol.Handlers else return ""; } + /// + /// Read a boolean from a cache of bytes and remove it from the cache + /// + /// The boolean value + + private static bool readNextBool(ref byte[] cache) + { + return readData(1, ref cache)[0] != 0x00; + } + + /// + /// Read a short integer from a cache of bytes and remove it from the cache + /// + /// The short integer value + + private static short readNextShort(ref byte[] cache) + { + byte[] rawValue = readData(2, ref cache); + Array.Reverse(rawValue); //Endianness + return BitConverter.ToInt16(rawValue, 0); + } + /// /// Read a uuid from a cache of bytes and remove it from the cache /// /// Cache of bytes to read from /// The uuid - private Guid readNextUUID(ref byte[] cache) + private static Guid readNextUUID(ref byte[] cache) { return new Guid(readData(16, ref cache)); } @@ -288,7 +331,9 @@ namespace MinecraftClient.Protocol.Handlers private byte[] readNextByteArray(ref byte[] cache) { - int len = readNextVarInt(ref cache); + int len = protocolversion >= MC18Version + ? readNextVarInt(ref cache) + : readNextShort(ref cache); return readData(len, ref cache); } @@ -320,7 +365,7 @@ namespace MinecraftClient.Protocol.Handlers /// Cache of bytes to read from /// The integer - private int readNextVarInt(ref byte[] cache) + private static int readNextVarInt(ref byte[] cache) { int i = 0; int j = 0; @@ -355,6 +400,23 @@ namespace MinecraftClient.Protocol.Handlers return bytes.ToArray(); } + /// + /// Get byte array with length information prepended to it + /// + /// Array to process + /// Array ready to send + + private byte[] getArray(byte[] array) + { + if (protocolversion < MC18Version) + { + byte[] length = BitConverter.GetBytes((short)array.Length); + Array.Reverse(length); + return concatBytes(length, array); + } + else return concatBytes(getVarInt(array.Length), array); + } + /// /// Easily append several byte arrays /// @@ -514,13 +576,11 @@ namespace MinecraftClient.Protocol.Handlers } //Encrypt the data - byte[] key_enc = RSAService.Encrypt(secretKey, false); - byte[] token_enc = RSAService.Encrypt(token, false); - byte[] key_len = getVarInt(key_enc.Length); - byte[] token_len = getVarInt(token_enc.Length); + byte[] key_enc = getArray(RSAService.Encrypt(secretKey, false)); + byte[] token_enc = getArray(RSAService.Encrypt(token, false)); //Encryption Response packet - SendPacket(0x01, concatBytes(key_len, key_enc, token_len, token_enc)); + SendPacket(0x01, concatBytes(key_enc, token_enc)); //Start client-side encryption s = CryptoHandler.getAesStream(c.GetStream(), secretKey); @@ -614,8 +674,10 @@ namespace MinecraftClient.Protocol.Handlers byte[] tocomplete_val = Encoding.UTF8.GetBytes(BehindCursor); byte[] tocomplete_len = getVarInt(tocomplete_val.Length); - byte[] has_position = new byte[] { 0x00 }; //false, no position sent - byte[] tabcomplete_packet = concatBytes(tocomplete_len, tocomplete_val, has_position); + byte[] has_position = new byte[] { 0x00 }; + byte[] tabcomplete_packet = protocolversion >= MC18Version + ? concatBytes(tocomplete_len, tocomplete_val, has_position) + : concatBytes(tocomplete_len, tocomplete_val); autocomplete_received = false; autocomplete_result = BehindCursor; @@ -625,5 +687,68 @@ namespace MinecraftClient.Protocol.Handlers while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; } return autocomplete_result; } + + /// + /// Ping a Minecraft server to get information about the server + /// + /// True if ping was successful + + public static bool doPing(string host, int port, ref int protocolversion) + { + string version = ""; + TcpClient tcp = ProxyHandler.newTcpClient(host, port); + tcp.ReceiveBufferSize = 1024 * 1024; + + byte[] packet_id = getVarInt(0); + byte[] protocol_version = getVarInt(4); + byte[] server_adress_val = Encoding.UTF8.GetBytes(host); + byte[] server_adress_len = getVarInt(server_adress_val.Length); + byte[] server_port = BitConverter.GetBytes((ushort)port); Array.Reverse(server_port); + byte[] next_state = getVarInt(1); + byte[] packet = concatBytes(packet_id, protocol_version, server_adress_len, server_adress_val, server_port, next_state); + byte[] tosend = concatBytes(getVarInt(packet.Length), packet); + + tcp.Client.Send(tosend, SocketFlags.None); + + byte[] status_request = getVarInt(0); + byte[] request_packet = concatBytes(getVarInt(status_request.Length), status_request); + + tcp.Client.Send(request_packet, SocketFlags.None); + + Protocol18Handler ComTmp = new Protocol18Handler(tcp); + int packetLength = ComTmp.readNextVarIntRAW(); + if (packetLength > 0) //Read Response length + { + byte[] packetData = ComTmp.readDataRAW(packetLength); + if (readNextVarInt(ref packetData) == 0x00) //Read Packet ID + { + string result = readNextString(ref packetData); //Get the Json data + if (!String.IsNullOrEmpty(result) && result.StartsWith("{") && result.EndsWith("}")) + { + Json.JSONData jsonData = Json.ParseJson(result); + if (jsonData.Type == Json.JSONData.DataType.Object && jsonData.Properties.ContainsKey("version")) + { + jsonData = jsonData.Properties["version"]; + + //Retrieve display name of the Minecraft version + if (jsonData.Properties.ContainsKey("name")) + version = jsonData.Properties["name"].StringValue; + + //Retrieve protocol version number for handling this server + if (jsonData.Properties.ContainsKey("protocol")) + protocolversion = atoi(jsonData.Properties["protocol"].StringValue); + + //Automatic fix for BungeeCord 1.8 reporting itself as 1.7... + if (protocolversion < 47 && version.Split(' ').Contains("1.8")) + protocolversion = ProtocolHandler.MCVer2ProtocolVersion("1.8.0"); + + ConsoleIO.WriteLineFormatted("§8Server version : " + version + " (protocol v" + protocolversion + ")."); + return true; + } + } + } + } + return false; + } } } diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs index a6c2e981..11a0873f 100644 --- a/MinecraftClient/Protocol/ProtocolHandler.cs +++ b/MinecraftClient/Protocol/ProtocolHandler.cs @@ -32,7 +32,7 @@ namespace MinecraftClient.Protocol try { if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp) - || Protocol17Handler.doPing(serverIP, serverPort, ref protocolversionTmp)) + || Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp)) { success = true; } @@ -67,10 +67,7 @@ namespace MinecraftClient.Protocol int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 }; if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1) return new Protocol16Handler(Client, ProtocolVersion, Handler); - int[] supportedVersions_Protocol17 = { 4, 5 }; - if (Array.IndexOf(supportedVersions_Protocol17, ProtocolVersion) > -1) - return new Protocol17Handler(Client, ProtocolVersion, Handler); - int[] supportedVersions_Protocol18 = { 47 }; + int[] supportedVersions_Protocol18 = { 4, 5, 47 }; if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1) return new Protocol18Handler(Client, ProtocolVersion, Handler); throw new NotSupportedException("The protocol version no." + ProtocolVersion + " is not supported.");