mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-11-07 17:36:07 +00:00
Move 1.7 handling into 1.8 handler
Minecraft 1.7 handler was pretty similar to 1.8 handler and lacking some features such as packet prefetching.
This commit is contained in:
parent
729960d4a3
commit
3a760240e4
4 changed files with 172 additions and 640 deletions
|
|
@ -135,7 +135,6 @@
|
||||||
<Compile Include="Protocol\Handlers\Protocol16.cs" />
|
<Compile Include="Protocol\Handlers\Protocol16.cs" />
|
||||||
<Compile Include="Protocol\IMinecraftCom.cs" />
|
<Compile Include="Protocol\IMinecraftCom.cs" />
|
||||||
<Compile Include="Protocol\IMinecraftComHandler.cs" />
|
<Compile Include="Protocol\IMinecraftComHandler.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Protocol17.cs" />
|
|
||||||
<Compile Include="Protocol\ProtocolHandler.cs" />
|
<Compile Include="Protocol\ProtocolHandler.cs" />
|
||||||
<Compile Include="Proxy\ProxyHandler.cs" />
|
<Compile Include="Proxy\ProxyHandler.cs" />
|
||||||
<Compile Include="Proxy\Handlers\EventArgs\CreateConnectionAsyncCompletedEventArgs.cs" />
|
<Compile Include="Proxy\Handlers\EventArgs\CreateConnectionAsyncCompletedEventArgs.cs" />
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Implementation for Minecraft 1.7.X Protocol
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Separate thread. Network reading loop.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void Updater()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
while (Update());
|
|
||||||
}
|
|
||||||
catch (System.IO.IOException) { }
|
|
||||||
catch (SocketException) { }
|
|
||||||
catch (ObjectDisposedException) { }
|
|
||||||
|
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read and data from the network. Should be called on a separate thread.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start the updating thread. Should be called after login success.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void StartUpdating()
|
|
||||||
{
|
|
||||||
netRead = new Thread(new ThreadStart(Updater));
|
|
||||||
netRead.Name = "ProtocolPacketHandler";
|
|
||||||
netRead.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disconnect from the server, cancel network reading.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (netRead != null)
|
|
||||||
{
|
|
||||||
netRead.Abort();
|
|
||||||
c.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read some data and discard the result
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Amount of bytes to read</param>
|
|
||||||
|
|
||||||
private void readData(int offset)
|
|
||||||
{
|
|
||||||
if (offset > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
byte[] cache = new byte[offset];
|
|
||||||
Receive(cache, 0, offset, SocketFlags.None);
|
|
||||||
}
|
|
||||||
catch (OutOfMemoryException) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a string from the network
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">String length</param>
|
|
||||||
/// <returns>The string</returns>
|
|
||||||
|
|
||||||
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 "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a uuid from the network
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
|
||||||
/// <returns>The uuid</returns>
|
|
||||||
|
|
||||||
private Guid readNextUUID()
|
|
||||||
{
|
|
||||||
byte[] cache = new byte[16];
|
|
||||||
Receive(cache, 0, 16, SocketFlags.None);
|
|
||||||
return new Guid(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a short from the network
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
|
|
||||||
private short readNextShort()
|
|
||||||
{
|
|
||||||
byte[] tmp = new byte[2];
|
|
||||||
Receive(tmp, 0, 2, SocketFlags.None);
|
|
||||||
Array.Reverse(tmp);
|
|
||||||
return BitConverter.ToInt16(tmp, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a boolean from the network
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
|
|
||||||
private bool readNextBool()
|
|
||||||
{
|
|
||||||
byte[] tmp = new byte[1];
|
|
||||||
Receive(tmp, 0, 1, SocketFlags.None);
|
|
||||||
return tmp[0] != 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a byte array from the network
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The byte array</returns>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read an integer from the network
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The integer</returns>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Build an integer for sending over the network
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="paramInt">Integer to encode</param>
|
|
||||||
/// <returns>Byte array for this integer</returns>
|
|
||||||
|
|
||||||
private static byte[] getVarInt(int paramInt)
|
|
||||||
{
|
|
||||||
List<byte> bytes = new List<byte>();
|
|
||||||
while ((paramInt & -128) != 0)
|
|
||||||
{
|
|
||||||
bytes.Add((byte)(paramInt & 127 | 128));
|
|
||||||
paramInt = (int)(((uint)paramInt) >> 7);
|
|
||||||
}
|
|
||||||
bytes.Add((byte)paramInt);
|
|
||||||
return bytes.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Easily append several byte arrays
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">Bytes to append</param>
|
|
||||||
/// <returns>Array containing all the data</returns>
|
|
||||||
|
|
||||||
private static byte[] concatBytes(params byte[][] bytes)
|
|
||||||
{
|
|
||||||
List<byte> result = new List<byte>();
|
|
||||||
foreach (byte[] array in bytes)
|
|
||||||
result.AddRange(array);
|
|
||||||
return result.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// C-like atoi function for parsing an int from string
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="str">String to parse</param>
|
|
||||||
/// <returns>Int parsed</returns>
|
|
||||||
|
|
||||||
private static int atoi(string str)
|
|
||||||
{
|
|
||||||
return int.Parse(new string(str.Trim().TakeWhile(char.IsDigit).ToArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Network reading method. Read bytes from the socket or encrypted socket.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Network sending method. Send bytes using the socket or encrypted socket.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer"></param>
|
|
||||||
|
|
||||||
private void Send(byte[] buffer)
|
|
||||||
{
|
|
||||||
if (encrypted)
|
|
||||||
{
|
|
||||||
s.Write(buffer, 0, buffer.Length);
|
|
||||||
}
|
|
||||||
else c.Client.Send(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Do the Minecraft login.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if login successful</returns>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start network encryption. Automatically called by Login() if the server requests encryption.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if encryption was successful</returns>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send a chat message to the server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">Message</param>
|
|
||||||
/// <returns>True if properly sent</returns>
|
|
||||||
|
|
||||||
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; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send a respawn packet to the server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">Message</param>
|
|
||||||
/// <returns>True if properly sent</returns>
|
|
||||||
|
|
||||||
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; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disconnect from the server
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public void Disconnect()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
c.Close();
|
|
||||||
}
|
|
||||||
catch (SocketException) { }
|
|
||||||
catch (System.IO.IOException) { }
|
|
||||||
catch (NullReferenceException) { }
|
|
||||||
catch (ObjectDisposedException) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Autocomplete text while typing username or command
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="BehindCursor">Text behind cursor</param>
|
|
||||||
/// <returns>Completed text</returns>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ping a Minecraft server to get information about the server
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if ping was successful</returns>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,24 +6,28 @@ using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using MinecraftClient.Crypto;
|
using MinecraftClient.Crypto;
|
||||||
using MinecraftClient.Proxy;
|
using MinecraftClient.Proxy;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol.Handlers
|
namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implementation for Minecraft 1.8.X Protocol
|
/// Implementation for Minecraft 1.7.X and 1.8.X Protocols
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
class Protocol18Handler : IMinecraftCom
|
class Protocol18Handler : IMinecraftCom
|
||||||
{
|
{
|
||||||
IMinecraftComHandler handler;
|
private const int MC18Version = 47;
|
||||||
|
|
||||||
private int compression_treshold = 0;
|
private int compression_treshold = 0;
|
||||||
private bool autocomplete_received = false;
|
private bool autocomplete_received = false;
|
||||||
private string autocomplete_result = "";
|
private string autocomplete_result = "";
|
||||||
private bool login_phase = true;
|
private bool login_phase = true;
|
||||||
private bool encrypted = false;
|
private bool encrypted = false;
|
||||||
private int protocolversion;
|
private int protocolversion;
|
||||||
private Thread netRead;
|
|
||||||
Crypto.IAesStream s;
|
IMinecraftComHandler handler;
|
||||||
|
Thread netRead;
|
||||||
|
IAesStream s;
|
||||||
TcpClient c;
|
TcpClient c;
|
||||||
|
|
||||||
public Protocol18Handler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler)
|
public Protocol18Handler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler)
|
||||||
|
|
@ -95,7 +99,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
int size = readNextVarIntRAW(); //Packet size
|
int size = readNextVarIntRAW(); //Packet size
|
||||||
packetData = readDataRAW(size); //Packet contents
|
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);
|
int size_uncompressed = readNextVarInt(ref packetData);
|
||||||
if (size_uncompressed != 0) // != 0 means compressed, let's decompress
|
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)
|
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)
|
if (login_phase)
|
||||||
{
|
{
|
||||||
switch (packetID) //Packet IDs are different while logging in
|
switch (packetID) //Packet IDs are different while logging in
|
||||||
{
|
{
|
||||||
case 0x03:
|
case 0x03:
|
||||||
compression_treshold = readNextVarInt(ref packetData);
|
if (protocolversion >= MC18Version)
|
||||||
|
compression_treshold = readNextVarInt(ref packetData);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false; //Ignored packet
|
return false; //Ignored packet
|
||||||
|
|
@ -130,31 +144,44 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
switch (packetID)
|
switch (packetID)
|
||||||
{
|
{
|
||||||
case 0x00: //Keep-Alive
|
case 0x00: //Keep-Alive
|
||||||
SendPacket(0x00, getVarInt(readNextVarInt(ref packetData)));
|
SendPacket(0x00, packetData);
|
||||||
break;
|
break;
|
||||||
case 0x02: //Chat message
|
case 0x02: //Chat message
|
||||||
handler.OnTextReceived(ChatParser.ParseText(readNextString(ref packetData)));
|
handler.OnTextReceived(ChatParser.ParseText(readNextString(ref packetData)));
|
||||||
break;
|
break;
|
||||||
case 0x38: //Player List update
|
case 0x38: //Player List update
|
||||||
int action = readNextVarInt(ref packetData);
|
if (protocolversion >= MC18Version)
|
||||||
int numActions = readNextVarInt(ref packetData);
|
|
||||||
for (int i = 0; i < numActions; i++)
|
|
||||||
{
|
{
|
||||||
Guid uuid = readNextUUID(ref packetData);
|
int action = readNextVarInt(ref packetData);
|
||||||
switch (action)
|
int numActions = readNextVarInt(ref packetData);
|
||||||
|
for (int i = 0; i < numActions; i++)
|
||||||
{
|
{
|
||||||
case 0x00: //Player Join
|
Guid uuid = readNextUUID(ref packetData);
|
||||||
string name = readNextString(ref packetData);
|
switch (action)
|
||||||
handler.OnPlayerJoin(uuid, name);
|
{
|
||||||
break;
|
case 0x00: //Player Join
|
||||||
case 0x04: //Player Leave
|
string name = readNextString(ref packetData);
|
||||||
handler.OnPlayerLeave(uuid);
|
handler.OnPlayerJoin(uuid, name);
|
||||||
break;
|
break;
|
||||||
default:
|
case 0x04: //Player Leave
|
||||||
//Unknown player list item type
|
handler.OnPlayerLeave(uuid);
|
||||||
break;
|
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;
|
break;
|
||||||
case 0x3A: //Tab-Complete Result
|
case 0x3A: //Tab-Complete Result
|
||||||
int autocomplete_count = readNextVarInt(ref packetData);
|
int autocomplete_count = readNextVarInt(ref packetData);
|
||||||
|
|
@ -174,7 +201,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString(ref packetData)));
|
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString(ref packetData)));
|
||||||
return false;
|
return false;
|
||||||
case 0x46: //Network Compression Treshold Info
|
case 0x46: //Network Compression Treshold Info
|
||||||
compression_treshold = readNextVarInt(ref packetData);
|
if (protocolversion >= MC18Version)
|
||||||
|
compression_treshold = readNextVarInt(ref packetData);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false; //Ignored packet
|
return false; //Ignored packet
|
||||||
|
|
@ -239,18 +267,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The data read from the cache as an array</returns>
|
/// <returns>The data read from the cache as an array</returns>
|
||||||
|
|
||||||
private byte[] readData(int offset, ref byte[] cache)
|
private static byte[] readData(int offset, ref byte[] cache)
|
||||||
{
|
{
|
||||||
List<byte> read = new List<byte>();
|
byte[] result = cache.Take(offset).ToArray();
|
||||||
List<byte> list = new List<byte>(cache);
|
cache = cache.Skip(offset).ToArray();
|
||||||
while (offset > 0 && list.Count > 0)
|
return result;
|
||||||
{
|
|
||||||
read.Add(list[0]);
|
|
||||||
list.RemoveAt(0);
|
|
||||||
offset--;
|
|
||||||
}
|
|
||||||
cache = list.ToArray();
|
|
||||||
return read.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -259,7 +280,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The string</returns>
|
/// <returns>The string</returns>
|
||||||
|
|
||||||
private string readNextString(ref byte[] cache)
|
private static string readNextString(ref byte[] cache)
|
||||||
{
|
{
|
||||||
int length = readNextVarInt(ref cache);
|
int length = readNextVarInt(ref cache);
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
|
|
@ -269,13 +290,35 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
else return "";
|
else return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a boolean from a cache of bytes and remove it from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The boolean value</returns>
|
||||||
|
|
||||||
|
private static bool readNextBool(ref byte[] cache)
|
||||||
|
{
|
||||||
|
return readData(1, ref cache)[0] != 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a short integer from a cache of bytes and remove it from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The short integer value</returns>
|
||||||
|
|
||||||
|
private static short readNextShort(ref byte[] cache)
|
||||||
|
{
|
||||||
|
byte[] rawValue = readData(2, ref cache);
|
||||||
|
Array.Reverse(rawValue); //Endianness
|
||||||
|
return BitConverter.ToInt16(rawValue, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a uuid from a cache of bytes and remove it from the cache
|
/// Read a uuid from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The uuid</returns>
|
/// <returns>The uuid</returns>
|
||||||
|
|
||||||
private Guid readNextUUID(ref byte[] cache)
|
private static Guid readNextUUID(ref byte[] cache)
|
||||||
{
|
{
|
||||||
return new Guid(readData(16, ref cache));
|
return new Guid(readData(16, ref cache));
|
||||||
}
|
}
|
||||||
|
|
@ -288,7 +331,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
private byte[] readNextByteArray(ref byte[] cache)
|
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);
|
return readData(len, ref cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -320,7 +365,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The integer</returns>
|
/// <returns>The integer</returns>
|
||||||
|
|
||||||
private int readNextVarInt(ref byte[] cache)
|
private static int readNextVarInt(ref byte[] cache)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
|
@ -355,6 +400,23 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return bytes.ToArray();
|
return bytes.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get byte array with length information prepended to it
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">Array to process</param>
|
||||||
|
/// <returns>Array ready to send</returns>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Easily append several byte arrays
|
/// Easily append several byte arrays
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -514,13 +576,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
//Encrypt the data
|
//Encrypt the data
|
||||||
byte[] key_enc = RSAService.Encrypt(secretKey, false);
|
byte[] key_enc = getArray(RSAService.Encrypt(secretKey, false));
|
||||||
byte[] token_enc = RSAService.Encrypt(token, false);
|
byte[] token_enc = getArray(RSAService.Encrypt(token, false));
|
||||||
byte[] key_len = getVarInt(key_enc.Length);
|
|
||||||
byte[] token_len = getVarInt(token_enc.Length);
|
|
||||||
|
|
||||||
//Encryption Response packet
|
//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
|
//Start client-side encryption
|
||||||
s = CryptoHandler.getAesStream(c.GetStream(), secretKey);
|
s = CryptoHandler.getAesStream(c.GetStream(), secretKey);
|
||||||
|
|
@ -614,8 +674,10 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
byte[] tocomplete_val = Encoding.UTF8.GetBytes(BehindCursor);
|
byte[] tocomplete_val = Encoding.UTF8.GetBytes(BehindCursor);
|
||||||
byte[] tocomplete_len = getVarInt(tocomplete_val.Length);
|
byte[] tocomplete_len = getVarInt(tocomplete_val.Length);
|
||||||
byte[] has_position = new byte[] { 0x00 }; //false, no position sent
|
byte[] has_position = new byte[] { 0x00 };
|
||||||
byte[] tabcomplete_packet = concatBytes(tocomplete_len, tocomplete_val, has_position);
|
byte[] tabcomplete_packet = protocolversion >= MC18Version
|
||||||
|
? concatBytes(tocomplete_len, tocomplete_val, has_position)
|
||||||
|
: concatBytes(tocomplete_len, tocomplete_val);
|
||||||
|
|
||||||
autocomplete_received = false;
|
autocomplete_received = false;
|
||||||
autocomplete_result = BehindCursor;
|
autocomplete_result = BehindCursor;
|
||||||
|
|
@ -625,5 +687,68 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; }
|
while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; }
|
||||||
return autocomplete_result;
|
return autocomplete_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ping a Minecraft server to get information about the server
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if ping was successful</returns>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace MinecraftClient.Protocol
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp)
|
if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp)
|
||||||
|| Protocol17Handler.doPing(serverIP, serverPort, ref protocolversionTmp))
|
|| Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp))
|
||||||
{
|
{
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
|
@ -67,10 +67,7 @@ namespace MinecraftClient.Protocol
|
||||||
int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 };
|
int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 };
|
||||||
if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1)
|
if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1)
|
||||||
return new Protocol16Handler(Client, ProtocolVersion, Handler);
|
return new Protocol16Handler(Client, ProtocolVersion, Handler);
|
||||||
int[] supportedVersions_Protocol17 = { 4, 5 };
|
int[] supportedVersions_Protocol18 = { 4, 5, 47 };
|
||||||
if (Array.IndexOf(supportedVersions_Protocol17, ProtocolVersion) > -1)
|
|
||||||
return new Protocol17Handler(Client, ProtocolVersion, Handler);
|
|
||||||
int[] supportedVersions_Protocol18 = { 47 };
|
|
||||||
if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1)
|
if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1)
|
||||||
return new Protocol18Handler(Client, ProtocolVersion, Handler);
|
return new Protocol18Handler(Client, ProtocolVersion, Handler);
|
||||||
throw new NotSupportedException("The protocol version no." + ProtocolVersion + " is not supported.");
|
throw new NotSupportedException("The protocol version no." + ProtocolVersion + " is not supported.");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue