From d2cbc9f1c38bd90c3ae9e8ebfed8ec1bb28a1b79 Mon Sep 17 00:00:00 2001 From: ORelio Date: Mon, 29 Apr 2019 22:30:43 +0200 Subject: [PATCH] Split Protocol18 protocol into several classes - Protocol18: Handles Vanilla packets - Protocol18Forge: Handles Forge packets - DataTypes: Handles packet field encoding/decoding - SocketWrapper: Handles encryption and basic R/W operations This commit should not change anything for end users, only devs. See also #704 for discussions about this. --- MinecraftClient/MinecraftClient.csproj | 6 + .../Protocol/Handlers/DataTypes.cs | 414 +++++ .../Protocol/Handlers/PacketIncomingType.cs | 31 + .../Protocol/Handlers/PacketOutgoingType.cs | 24 + .../Protocol/Handlers/Protocol18.cs | 1381 +++-------------- .../Protocol/Handlers/Protocol18Forge.cs | 246 +++ .../Handlers/Protocol18PacketTypes.cs | 261 ++++ .../Protocol/Handlers/SocketWrapper.cs | 116 ++ 8 files changed, 1317 insertions(+), 1162 deletions(-) create mode 100644 MinecraftClient/Protocol/Handlers/DataTypes.cs create mode 100644 MinecraftClient/Protocol/Handlers/PacketIncomingType.cs create mode 100644 MinecraftClient/Protocol/Handlers/PacketOutgoingType.cs create mode 100644 MinecraftClient/Protocol/Handlers/Protocol18Forge.cs create mode 100644 MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs create mode 100644 MinecraftClient/Protocol/Handlers/SocketWrapper.cs diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 5e40a5f6..c90c4066 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -102,6 +102,12 @@ + + + + + + diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs new file mode 100644 index 00000000..37859d59 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -0,0 +1,414 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Sockets; +using MinecraftClient.Mapping; +using MinecraftClient.Crypto; + +namespace MinecraftClient.Protocol.Handlers +{ + /// + /// Handle data types encoding / decoding + /// + class DataTypes + { + /// + /// Protocol version for adjusting data types + /// + private int protocolversion; + + /// + /// Initialize a new DataTypes instance + /// + /// Protocol version + public DataTypes(int protocol) + { + this.protocolversion = protocol; + } + + /// + /// Read some data from a cache of bytes and remove it from the cache + /// + /// Amount of bytes to read + /// Cache of bytes to read from + /// The data read from the cache as an array + public byte[] ReadData(int offset, List cache) + { + byte[] result = cache.Take(offset).ToArray(); + cache.RemoveRange(0, offset); + return result; + } + + /// + /// Read a string from a cache of bytes and remove it from the cache + /// + /// Cache of bytes to read from + /// The string + public string ReadNextString(List cache) + { + int length = ReadNextVarInt(cache); + if (length > 0) + { + return Encoding.UTF8.GetString(ReadData(length, cache)); + } + else return ""; + } + + /// + /// Read a boolean from a cache of bytes and remove it from the cache + /// + /// The boolean value + public bool ReadNextBool(List cache) + { + return ReadNextByte(cache) != 0x00; + } + + /// + /// Read a short integer from a cache of bytes and remove it from the cache + /// + /// The short integer value + public short ReadNextShort(List cache) + { + byte[] rawValue = ReadData(2, cache); + Array.Reverse(rawValue); //Endianness + return BitConverter.ToInt16(rawValue, 0); + } + + /// + /// Read an integer from a cache of bytes and remove it from the cache + /// + /// The integer value + public int ReadNextInt(List cache) + { + byte[] rawValue = ReadData(4, cache); + Array.Reverse(rawValue); //Endianness + return BitConverter.ToInt32(rawValue, 0); + } + + /// + /// Read an unsigned short integer from a cache of bytes and remove it from the cache + /// + /// The unsigned short integer value + public ushort ReadNextUShort(List cache) + { + byte[] rawValue = ReadData(2, cache); + Array.Reverse(rawValue); //Endianness + return BitConverter.ToUInt16(rawValue, 0); + } + + /// + /// Read an unsigned long integer from a cache of bytes and remove it from the cache + /// + /// The unsigned long integer value + public ulong ReadNextULong(List cache) + { + byte[] rawValue = ReadData(8, cache); + Array.Reverse(rawValue); //Endianness + return BitConverter.ToUInt64(rawValue, 0); + } + + /// + /// Read a Location encoded as an ulong field and remove it from the cache + /// + /// The Location value + public Location ReadNextLocation(List cache) + { + ulong locEncoded = ReadNextULong(cache); + int x, y, z; + if (protocolversion >= Protocol18Handler.MC114Version) + { + x = (int)(locEncoded >> 38); + y = (int)(locEncoded & 0xFFF); + z = (int)(locEncoded << 26 >> 38); + } + else + { + x = (int)(locEncoded >> 38); + y = (int)((locEncoded >> 26) & 0xFFF); + z = (int)(locEncoded << 38 >> 38); + } + if (x >= 33554432) + x -= 67108864; + if (y >= 2048) + y -= 4096; + if (z >= 33554432) + z -= 67108864; + return new Location(x, y, z); + } + + /// + /// Read several little endian unsigned short integers at once from a cache of bytes and remove them from the cache + /// + /// The unsigned short integer value + public ushort[] ReadNextUShortsLittleEndian(int amount, List cache) + { + byte[] rawValues = ReadData(2 * amount, cache); + ushort[] result = new ushort[amount]; + for (int i = 0; i < amount; i++) + result[i] = BitConverter.ToUInt16(rawValues, i * 2); + return result; + } + + /// + /// Read a uuid from a cache of bytes and remove it from the cache + /// + /// Cache of bytes to read from + /// The uuid + public Guid ReadNextUUID(List cache) + { + byte[] javaUUID = ReadData(16, cache); + Guid guid; + if (BitConverter.IsLittleEndian) + { + // Convert big-endian Java UUID to little-endian .NET GUID + byte[] netGUID = new byte[16]; + for (int i = 8; i < 16; i++) + netGUID[i] = javaUUID[i]; + netGUID[3] = javaUUID[0]; + netGUID[2] = javaUUID[1]; + netGUID[1] = javaUUID[2]; + netGUID[0] = javaUUID[3]; + netGUID[5] = javaUUID[4]; + netGUID[4] = javaUUID[5]; + netGUID[6] = javaUUID[7]; + netGUID[7] = javaUUID[6]; + guid = new Guid(netGUID); + } + else + { + guid = new Guid(javaUUID); + } + return guid; + } + + /// + /// Read a byte array from a cache of bytes and remove it from the cache + /// + /// Cache of bytes to read from + /// The byte array + public byte[] ReadNextByteArray(List cache) + { + int len = protocolversion >= Protocol18Handler.MC18Version + ? ReadNextVarInt(cache) + : ReadNextShort(cache); + return ReadData(len, cache); + } + + /// + /// Reads a length-prefixed array of unsigned long integers and removes it from the cache + /// + /// The unsigned long integer values + public ulong[] ReadNextULongArray(List cache) + { + int len = ReadNextVarInt(cache); + ulong[] result = new ulong[len]; + for (int i = 0; i < len; i++) + result[i] = ReadNextULong(cache); + return result; + } + + /// + /// Read a double from a cache of bytes and remove it from the cache + /// + /// The double value + public double ReadNextDouble(List cache) + { + byte[] rawValue = ReadData(8, cache); + Array.Reverse(rawValue); //Endianness + return BitConverter.ToDouble(rawValue, 0); + } + + /// + /// Read a float from a cache of bytes and remove it from the cache + /// + /// The float value + public float ReadNextFloat(List cache) + { + byte[] rawValue = ReadData(4, cache); + Array.Reverse(rawValue); //Endianness + return BitConverter.ToSingle(rawValue, 0); + } + + /// + /// Read an integer from the network + /// + /// The integer + public int ReadNextVarIntRAW(SocketWrapper socket) + { + int i = 0; + int j = 0; + int k = 0; + while (true) + { + k = socket.ReadDataRAW(1)[0]; + i |= (k & 0x7F) << j++ * 7; + if (j > 5) throw new OverflowException("VarInt too big"); + if ((k & 0x80) != 128) break; + } + return i; + } + + /// + /// Read an integer from a cache of bytes and remove it from the cache + /// + /// Cache of bytes to read from + /// The integer + public int ReadNextVarInt(List cache) + { + int i = 0; + int j = 0; + int k = 0; + while (true) + { + k = ReadNextByte(cache); + i |= (k & 0x7F) << j++ * 7; + if (j > 5) throw new OverflowException("VarInt too big"); + if ((k & 0x80) != 128) break; + } + return i; + } + + /// + /// Read an "extended short", which is actually an int of some kind, from the cache of bytes. + /// This is only done with forge. It looks like it's a normal short, except that if the high + /// bit is set, it has an extra byte. + /// + /// Cache of bytes to read from + /// The int + public int ReadNextVarShort(List cache) + { + ushort low = ReadNextUShort(cache); + byte high = 0; + if ((low & 0x8000) != 0) + { + low &= 0x7FFF; + high = ReadNextByte(cache); + } + return ((high & 0xFF) << 15) | low; + } + + /// + /// Read a single byte from a cache of bytes and remove it from the cache + /// + /// The byte that was read + public byte ReadNextByte(List cache) + { + byte result = cache[0]; + cache.RemoveAt(0); + return result; + } + + /// + /// Build an integer for sending over the network + /// + /// Integer to encode + /// Byte array for this integer + public 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(); + } + + /// + /// Get byte array representing a double + /// + /// Double to process + /// Array ready to send + public byte[] GetDouble(double number) + { + byte[] theDouble = BitConverter.GetBytes(number); + Array.Reverse(theDouble); //Endianness + return theDouble; + } + + /// + /// Get byte array representing a float + /// + /// Floalt to process + /// Array ready to send + public byte[] GetFloat(float number) + { + byte[] theFloat = BitConverter.GetBytes(number); + Array.Reverse(theFloat); //Endianness + return theFloat; + } + + + /// + /// Get byte array with length information prepended to it + /// + /// Array to process + /// Array ready to send + public byte[] GetArray(byte[] array) + { + if (protocolversion < Protocol18Handler.MC18Version) + { + byte[] length = BitConverter.GetBytes((short)array.Length); + Array.Reverse(length); + return ConcatBytes(length, array); + } + else return ConcatBytes(GetVarInt(array.Length), array); + } + + /// + /// Get a byte array from the given string for sending over the network, with length information prepended. + /// + /// String to process + /// Array ready to send + public byte[] GetString(string text) + { + byte[] bytes = Encoding.UTF8.GetBytes(text); + + return ConcatBytes(GetVarInt(bytes.Length), bytes); + } + + /// + /// Get a byte array representing the given location encoded as an unsigned short + /// + /// + /// A modulo will be applied if the location is outside the following ranges: + /// X: -33,554,432 to +33,554,431 + /// Y: -2,048 to +2,047 + /// Z: -33,554,432 to +33,554,431 + /// + /// Location representation as ulong + public byte[] GetLocation(Location location) + { + if (protocolversion >= Protocol18Handler.MC114Version) + { + return BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Z) & 0x3FFFFFF) << 12) | (((ulong)location.Y) & 0xFFF)); + } + else return BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Y) & 0xFFF) << 26) | (((ulong)location.Z) & 0x3FFFFFF)); + } + + /// + /// Easily append several byte arrays + /// + /// Bytes to append + /// Array containing all the data + public 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 + public int Atoi(string str) + { + return int.Parse(new string(str.Trim().TakeWhile(char.IsDigit).ToArray())); + } + } +} diff --git a/MinecraftClient/Protocol/Handlers/PacketIncomingType.cs b/MinecraftClient/Protocol/Handlers/PacketIncomingType.cs new file mode 100644 index 00000000..263d38a1 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketIncomingType.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Protocol.Handlers +{ + /// + /// Abstract incoming packet numbering + /// + enum PacketIncomingType + { + KeepAlive, + JoinGame, + ChatMessage, + Respawn, + PlayerPositionAndLook, + ChunkData, + MultiBlockChange, + BlockChange, + MapChunkBulk, + UnloadChunk, + PlayerListUpdate, + TabCompleteResult, + PluginMessage, + KickPacket, + NetworkCompressionTreshold, + ResourcePackSend, + UnknownPacket + } +} diff --git a/MinecraftClient/Protocol/Handlers/PacketOutgoingType.cs b/MinecraftClient/Protocol/Handlers/PacketOutgoingType.cs new file mode 100644 index 00000000..be863d5b --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketOutgoingType.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Protocol.Handlers +{ + /// + /// Abstract outgoing packet numbering + /// + enum PacketOutgoingType + { + KeepAlive, + ResourcePackStatus, + ChatMessage, + ClientStatus, + ClientSettings, + PluginMessage, + TabComplete, + PlayerPosition, + PlayerPositionAndLook, + TeleportConfirm + } +} diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 548764ed..42f0a759 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -7,9 +7,9 @@ using System.Threading; using MinecraftClient.Crypto; using MinecraftClient.Proxy; using System.Security.Cryptography; -using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Mapping; using MinecraftClient.Mapping.BlockPalettes; +using MinecraftClient.Protocol.Handlers.Forge; namespace MinecraftClient.Protocol.Handlers { @@ -20,64 +20,55 @@ namespace MinecraftClient.Protocol.Handlers /// Typical update steps for implementing protocol changes for a new Minecraft version: /// - Perform a diff between latest supported version in MCC and new stable version to support on https://wiki.vg/Protocol /// - If there are any changes in packets implemented by MCC, add MCXXXVersion field below and implement new packet layouts - /// - If packet IDs were changed, also update getPacketIncomingType() and getPacketOutgoingID() + /// - If packet IDs were changed, also update getPacketIncomingType() and getPacketOutgoingID() inside Protocol18PacketTypes.cs /// class Protocol18Handler : IMinecraftCom { - private const int MC18Version = 47; - private const int MC19Version = 107; - private const int MC191Version = 108; - private const int MC110Version = 210; - private const int MC1112Version = 316; - private const int MC112Version = 335; - private const int MC1121Version = 338; - private const int MC1122Version = 340; - private const int MC113Version = 393; - private const int MC114Version = 477; + internal const int MC18Version = 47; + internal const int MC19Version = 107; + internal const int MC191Version = 108; + internal const int MC110Version = 210; + internal const int MC1112Version = 316; + internal const int MC112Version = 335; + internal const int MC1121Version = 338; + internal const int MC1122Version = 340; + internal const int MC113Version = 393; + internal const int MC114Version = 477; private int compression_treshold = 0; private bool autocomplete_received = false; private int autocomplete_transaction_id = 0; private readonly List autocomplete_result = new List(); private bool login_phase = true; - private bool encrypted = false; private int protocolversion; + private int currentDimension; - // Server forge info -- may be null. - private ForgeInfo forgeInfo; - private FMLHandshakeClientState fmlHandshakeState = FMLHandshakeClientState.START; - + Protocol18Forge pForge; IMinecraftComHandler handler; + SocketWrapper socketWrapper; + DataTypes dataTypes; Thread netRead; - IAesStream s; - TcpClient c; - int currentDimension; - - public Protocol18Handler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler, ForgeInfo ForgeInfo) + public Protocol18Handler(TcpClient Client, int protocolVersion, IMinecraftComHandler handler, ForgeInfo forgeInfo) { ConsoleIO.SetAutoCompleteEngine(this); ChatParser.InitTranslations(); - this.c = Client; - this.protocolversion = ProtocolVersion; - this.handler = Handler; - this.forgeInfo = ForgeInfo; + this.socketWrapper = new SocketWrapper(Client); + this.dataTypes = new DataTypes(protocolVersion); + this.protocolversion = protocolVersion; + this.handler = handler; + this.pForge = new Protocol18Forge(forgeInfo, protocolVersion, dataTypes, this, handler); if (protocolversion >= MC113Version) Block.Palette = new Palette113(); else Block.Palette = new Palette112(); - if (Handler.GetTerrainEnabled() && protocolversion >= MC114Version) + if (handler.GetTerrainEnabled() && protocolversion >= MC114Version) { ConsoleIO.WriteLineFormatted("§8Terrain & Movements currently not handled for that MC version."); - Handler.SetTerrainEnabled(false); + handler.SetTerrainEnabled(false); } } - private Protocol18Handler(TcpClient Client) - { - this.c = Client; - } - /// /// Separate thread. Network reading loop. /// @@ -105,15 +96,16 @@ namespace MinecraftClient.Protocol.Handlers private bool Update() { handler.OnUpdate(); - if (c.Client == null || !c.Connected) { return false; } + if (!socketWrapper.IsConnected()) + return false; try { - while (c.Client.Available > 0) + while (socketWrapper.HasDataAvailable()) { int packetID = 0; List packetData = new List(); - readNextPacket(ref packetID, packetData); - handlePacket(packetID, new List(packetData)); + ReadNextPacket(ref packetID, packetData); + HandlePacket(packetID, new List(packetData)); } } catch (SocketException) { return false; } @@ -126,17 +118,17 @@ namespace MinecraftClient.Protocol.Handlers /// /// will contain packet ID /// will contain raw packet Data - private void readNextPacket(ref int packetID, List packetData) + internal void ReadNextPacket(ref int packetID, List packetData) { packetData.Clear(); - int size = readNextVarIntRAW(); //Packet size - packetData.AddRange(readDataRAW(size)); //Packet contents + int size = dataTypes.ReadNextVarIntRAW(socketWrapper); //Packet size + packetData.AddRange(socketWrapper.ReadDataRAW(size)); //Packet contents //Handle packet decompression if (protocolversion >= MC18Version && compression_treshold > 0) { - int sizeUncompressed = readNextVarInt(packetData); + int sizeUncompressed = dataTypes.ReadNextVarInt(packetData); if (sizeUncompressed != 0) // != 0 means compressed, let's decompress { byte[] toDecompress = packetData.ToArray(); @@ -146,296 +138,7 @@ namespace MinecraftClient.Protocol.Handlers } } - packetID = readNextVarInt(packetData); //Packet ID - } - - /// - /// Abstract incoming packet numbering - /// - private enum PacketIncomingType - { - KeepAlive, - JoinGame, - ChatMessage, - Respawn, - PlayerPositionAndLook, - ChunkData, - MultiBlockChange, - BlockChange, - MapChunkBulk, - UnloadChunk, - PlayerListUpdate, - TabCompleteResult, - PluginMessage, - KickPacket, - NetworkCompressionTreshold, - ResourcePackSend, - UnknownPacket - } - - /// - /// Get abstract numbering of the specified packet ID - /// - /// Packet ID - /// Protocol version - /// Abstract numbering - private PacketIncomingType getPacketIncomingType(int packetID, int protocol) - { - if (protocol <= MC18Version) // MC 1.7 and 1.8 - { - switch (packetID) - { - case 0x00: return PacketIncomingType.KeepAlive; - case 0x01: return PacketIncomingType.JoinGame; - case 0x02: return PacketIncomingType.ChatMessage; - case 0x07: return PacketIncomingType.Respawn; - case 0x08: return PacketIncomingType.PlayerPositionAndLook; - case 0x21: return PacketIncomingType.ChunkData; - case 0x22: return PacketIncomingType.MultiBlockChange; - case 0x23: return PacketIncomingType.BlockChange; - case 0x26: return PacketIncomingType.MapChunkBulk; - //UnloadChunk does not exists prior to 1.9 - case 0x38: return PacketIncomingType.PlayerListUpdate; - case 0x3A: return PacketIncomingType.TabCompleteResult; - case 0x3F: return PacketIncomingType.PluginMessage; - case 0x40: return PacketIncomingType.KickPacket; - case 0x46: return PacketIncomingType.NetworkCompressionTreshold; - case 0x48: return PacketIncomingType.ResourcePackSend; - default: return PacketIncomingType.UnknownPacket; - } - } - else if (protocol <= MC1112Version) // MC 1.9, 1.10 and 1.11 - { - switch (packetID) - { - case 0x1F: return PacketIncomingType.KeepAlive; - case 0x23: return PacketIncomingType.JoinGame; - case 0x0F: return PacketIncomingType.ChatMessage; - case 0x33: return PacketIncomingType.Respawn; - case 0x2E: return PacketIncomingType.PlayerPositionAndLook; - case 0x20: return PacketIncomingType.ChunkData; - case 0x10: return PacketIncomingType.MultiBlockChange; - case 0x0B: return PacketIncomingType.BlockChange; - //MapChunkBulk removed in 1.9 - case 0x1D: return PacketIncomingType.UnloadChunk; - case 0x2D: return PacketIncomingType.PlayerListUpdate; - case 0x0E: return PacketIncomingType.TabCompleteResult; - case 0x18: return PacketIncomingType.PluginMessage; - case 0x1A: return PacketIncomingType.KickPacket; - //NetworkCompressionTreshold removed in 1.9 - case 0x32: return PacketIncomingType.ResourcePackSend; - default: return PacketIncomingType.UnknownPacket; - } - } - else if (protocol <= MC112Version) // MC 1.12.0 - { - switch (packetID) - { - case 0x1F: return PacketIncomingType.KeepAlive; - case 0x23: return PacketIncomingType.JoinGame; - case 0x0F: return PacketIncomingType.ChatMessage; - case 0x34: return PacketIncomingType.Respawn; - case 0x2E: return PacketIncomingType.PlayerPositionAndLook; - case 0x20: return PacketIncomingType.ChunkData; - case 0x10: return PacketIncomingType.MultiBlockChange; - case 0x0B: return PacketIncomingType.BlockChange; - case 0x1D: return PacketIncomingType.UnloadChunk; - case 0x2D: return PacketIncomingType.PlayerListUpdate; - case 0x0E: return PacketIncomingType.TabCompleteResult; - case 0x18: return PacketIncomingType.PluginMessage; - case 0x1A: return PacketIncomingType.KickPacket; - case 0x33: return PacketIncomingType.ResourcePackSend; - default: return PacketIncomingType.UnknownPacket; - } - } - else if (protocol <= MC1122Version) // MC 1.12.2 - { - switch (packetID) - { - case 0x1F: return PacketIncomingType.KeepAlive; - case 0x23: return PacketIncomingType.JoinGame; - case 0x0F: return PacketIncomingType.ChatMessage; - case 0x35: return PacketIncomingType.Respawn; - case 0x2F: return PacketIncomingType.PlayerPositionAndLook; - case 0x20: return PacketIncomingType.ChunkData; - case 0x10: return PacketIncomingType.MultiBlockChange; - case 0x0B: return PacketIncomingType.BlockChange; - case 0x1D: return PacketIncomingType.UnloadChunk; - case 0x2E: return PacketIncomingType.PlayerListUpdate; - case 0x0E: return PacketIncomingType.TabCompleteResult; - case 0x18: return PacketIncomingType.PluginMessage; - case 0x1A: return PacketIncomingType.KickPacket; - case 0x34: return PacketIncomingType.ResourcePackSend; - default: return PacketIncomingType.UnknownPacket; - } - } - else if (protocolversion < MC114Version) // MC 1.13 to 1.13.2 - { - switch (packetID) - { - case 0x21: return PacketIncomingType.KeepAlive; - case 0x25: return PacketIncomingType.JoinGame; - case 0x0E: return PacketIncomingType.ChatMessage; - case 0x38: return PacketIncomingType.Respawn; - case 0x32: return PacketIncomingType.PlayerPositionAndLook; - case 0x22: return PacketIncomingType.ChunkData; - case 0x0F: return PacketIncomingType.MultiBlockChange; - case 0x0B: return PacketIncomingType.BlockChange; - case 0x1F: return PacketIncomingType.UnloadChunk; - case 0x30: return PacketIncomingType.PlayerListUpdate; - case 0x10: return PacketIncomingType.TabCompleteResult; - case 0x19: return PacketIncomingType.PluginMessage; - case 0x1B: return PacketIncomingType.KickPacket; - case 0x37: return PacketIncomingType.ResourcePackSend; - default: return PacketIncomingType.UnknownPacket; - } - } - else // MC 1.14 - { - switch (packetID) - { - case 0x20: return PacketIncomingType.KeepAlive; - case 0x25: return PacketIncomingType.JoinGame; - case 0x0E: return PacketIncomingType.ChatMessage; - case 0x3A: return PacketIncomingType.Respawn; - case 0x35: return PacketIncomingType.PlayerPositionAndLook; - case 0x21: return PacketIncomingType.ChunkData; - case 0x0F: return PacketIncomingType.MultiBlockChange; - case 0x0B: return PacketIncomingType.BlockChange; - case 0x1D: return PacketIncomingType.UnloadChunk; - case 0x33: return PacketIncomingType.PlayerListUpdate; - case 0x10: return PacketIncomingType.TabCompleteResult; - case 0x18: return PacketIncomingType.PluginMessage; - case 0x1A: return PacketIncomingType.KickPacket; - case 0x39: return PacketIncomingType.ResourcePackSend; - default: return PacketIncomingType.UnknownPacket; - } - } - } - - /// - /// Abstract outgoing packet numbering - /// - private enum PacketOutgoingType - { - KeepAlive, - ResourcePackStatus, - ChatMessage, - ClientStatus, - ClientSettings, - PluginMessage, - TabComplete, - PlayerPosition, - PlayerPositionAndLook, - TeleportConfirm - } - - /// - /// Get packet ID of the specified outgoing packet - /// - /// Abstract packet numbering - /// Protocol version - /// Packet ID - private int getPacketOutgoingID(PacketOutgoingType packet, int protocol) - { - if (protocol <= MC18Version) // MC 1.7 and 1.8 - { - switch (packet) - { - case PacketOutgoingType.KeepAlive: return 0x00; - case PacketOutgoingType.ResourcePackStatus: return 0x19; - case PacketOutgoingType.ChatMessage: return 0x01; - case PacketOutgoingType.ClientStatus: return 0x16; - case PacketOutgoingType.ClientSettings: return 0x15; - case PacketOutgoingType.PluginMessage: return 0x17; - case PacketOutgoingType.TabComplete: return 0x14; - case PacketOutgoingType.PlayerPosition: return 0x04; - case PacketOutgoingType.PlayerPositionAndLook: return 0x06; - case PacketOutgoingType.TeleportConfirm: throw new InvalidOperationException("Teleport confirm is not supported in protocol " + protocol); - } - } - else if (protocol <= MC1112Version) // MC 1.9, 1,10 and 1.11 - { - switch (packet) - { - case PacketOutgoingType.KeepAlive: return 0x0B; - case PacketOutgoingType.ResourcePackStatus: return 0x16; - case PacketOutgoingType.ChatMessage: return 0x02; - case PacketOutgoingType.ClientStatus: return 0x03; - case PacketOutgoingType.ClientSettings: return 0x04; - case PacketOutgoingType.PluginMessage: return 0x09; - case PacketOutgoingType.TabComplete: return 0x01; - case PacketOutgoingType.PlayerPosition: return 0x0C; - case PacketOutgoingType.PlayerPositionAndLook: return 0x0D; - case PacketOutgoingType.TeleportConfirm: return 0x00; - } - } - else if (protocol <= MC112Version) // MC 1.12 - { - switch (packet) - { - case PacketOutgoingType.KeepAlive: return 0x0C; - case PacketOutgoingType.ResourcePackStatus: return 0x18; - case PacketOutgoingType.ChatMessage: return 0x03; - case PacketOutgoingType.ClientStatus: return 0x04; - case PacketOutgoingType.ClientSettings: return 0x05; - case PacketOutgoingType.PluginMessage: return 0x0A; - case PacketOutgoingType.TabComplete: return 0x02; - case PacketOutgoingType.PlayerPosition: return 0x0E; - case PacketOutgoingType.PlayerPositionAndLook: return 0x0F; - case PacketOutgoingType.TeleportConfirm: return 0x00; - } - } - else if (protocol <= MC1122Version) // 1.12.2 - { - switch (packet) - { - case PacketOutgoingType.KeepAlive: return 0x0B; - case PacketOutgoingType.ResourcePackStatus: return 0x18; - case PacketOutgoingType.ChatMessage: return 0x02; - case PacketOutgoingType.ClientStatus: return 0x03; - case PacketOutgoingType.ClientSettings: return 0x04; - case PacketOutgoingType.PluginMessage: return 0x09; - case PacketOutgoingType.TabComplete: return 0x01; - case PacketOutgoingType.PlayerPosition: return 0x0D; - case PacketOutgoingType.PlayerPositionAndLook: return 0x0E; - case PacketOutgoingType.TeleportConfirm: return 0x00; - } - } - else if (protocol < MC114Version) // MC 1.13 to 1.13.2 - { - switch (packet) - { - case PacketOutgoingType.KeepAlive: return 0x0E; - case PacketOutgoingType.ResourcePackStatus: return 0x1D; - case PacketOutgoingType.ChatMessage: return 0x02; - case PacketOutgoingType.ClientStatus: return 0x03; - case PacketOutgoingType.ClientSettings: return 0x04; - case PacketOutgoingType.PluginMessage: return 0x0A; - case PacketOutgoingType.TabComplete: return 0x05; - case PacketOutgoingType.PlayerPosition: return 0x10; - case PacketOutgoingType.PlayerPositionAndLook: return 0x11; - case PacketOutgoingType.TeleportConfirm: return 0x00; - } - } - else // MC 1.14 - { - switch (packet) - { - case PacketOutgoingType.KeepAlive: return 0x0F; - case PacketOutgoingType.ResourcePackStatus: return 0x1F; - case PacketOutgoingType.ChatMessage: return 0x03; - case PacketOutgoingType.ClientStatus: return 0x04; - case PacketOutgoingType.ClientSettings: return 0x05; - case PacketOutgoingType.PluginMessage: return 0x0B; - case PacketOutgoingType.TabComplete: return 0x06; - case PacketOutgoingType.PlayerPosition: return 0x11; - case PacketOutgoingType.PlayerPositionAndLook: return 0x12; - case PacketOutgoingType.TeleportConfirm: return 0x00; - } - } - - throw new System.ComponentModel.InvalidEnumArgumentException("Unknown PacketOutgoingType (protocol=" + protocol + ")", (int)packet, typeof(PacketOutgoingType)); + packetID = dataTypes.ReadNextVarInt(packetData); //Packet ID } /// @@ -444,7 +147,7 @@ namespace MinecraftClient.Protocol.Handlers /// Packet ID /// Packet contents /// TRUE if the packet was processed, FALSE if ignored or unknown - private bool handlePacket(int packetID, List packetData) + internal bool HandlePacket(int packetID, List packetData) { try { @@ -454,41 +157,41 @@ namespace MinecraftClient.Protocol.Handlers { case 0x03: if (protocolversion >= MC18Version) - compression_treshold = readNextVarInt(packetData); + compression_treshold = dataTypes.ReadNextVarInt(packetData); break; default: return false; //Ignored packet } } // Regular in-game packets - switch (getPacketIncomingType(packetID, protocolversion)) + switch (Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion)) { case PacketIncomingType.KeepAlive: SendPacket(PacketOutgoingType.KeepAlive, packetData); break; case PacketIncomingType.JoinGame: handler.OnGameJoined(); - readNextInt(packetData); - readNextByte(packetData); + dataTypes.ReadNextInt(packetData); + dataTypes.ReadNextByte(packetData); if (protocolversion >= MC191Version) - this.currentDimension = readNextInt(packetData); + this.currentDimension = dataTypes.ReadNextInt(packetData); else - this.currentDimension = (sbyte)readNextByte(packetData); + this.currentDimension = (sbyte)dataTypes.ReadNextByte(packetData); if (protocolversion < MC114Version) - readNextByte(packetData); // Difficulty - 1.13 and below - readNextByte(packetData); - readNextString(packetData); + dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below + dataTypes.ReadNextByte(packetData); + dataTypes.ReadNextString(packetData); if (protocolversion >= MC114Version) - readNextVarInt(packetData); // View distance - 1.14 and above + dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above if (protocolversion >= MC18Version) - readNextBool(packetData); // Reduced debug info - 1.8 and above + dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above break; case PacketIncomingType.ChatMessage: - string message = readNextString(packetData); + string message = dataTypes.ReadNextString(packetData); try { //Hide system messages or xp bar messages? - byte messageType = readNextByte(packetData); + byte messageType = dataTypes.ReadNextByte(packetData); if ((messageType == 1 && !Settings.DisplaySystemMessages) || (messageType == 2 && !Settings.DisplayXPBarMessages)) break; @@ -497,22 +200,22 @@ namespace MinecraftClient.Protocol.Handlers handler.OnTextReceived(message, true); break; case PacketIncomingType.Respawn: - this.currentDimension = readNextInt(packetData); + this.currentDimension = dataTypes.ReadNextInt(packetData); if (protocolversion < MC114Version) - readNextByte(packetData); // Difficulty - 1.13 and below - readNextByte(packetData); - readNextString(packetData); + dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below + dataTypes.ReadNextByte(packetData); + dataTypes.ReadNextString(packetData); handler.OnRespawn(); break; case PacketIncomingType.PlayerPositionAndLook: if (handler.GetTerrainEnabled()) { - double x = readNextDouble(packetData); - double y = readNextDouble(packetData); - double z = readNextDouble(packetData); - float yaw = readNextFloat(packetData); - float pitch = readNextFloat(packetData); - byte locMask = readNextByte(packetData); + double x = dataTypes.ReadNextDouble(packetData); + double y = dataTypes.ReadNextDouble(packetData); + double z = dataTypes.ReadNextDouble(packetData); + float yaw = dataTypes.ReadNextFloat(packetData); + float pitch = dataTypes.ReadNextFloat(packetData); + byte locMask = dataTypes.ReadNextByte(packetData); if (protocolversion >= MC18Version) { @@ -527,25 +230,25 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion >= MC19Version) { - int teleportID = readNextVarInt(packetData); + int teleportID = dataTypes.ReadNextVarInt(packetData); // Teleport confirm packet - SendPacket(PacketOutgoingType.TeleportConfirm, getVarInt(teleportID)); + SendPacket(PacketOutgoingType.TeleportConfirm, dataTypes.GetVarInt(teleportID)); } break; case PacketIncomingType.ChunkData: if (handler.GetTerrainEnabled()) { - int chunkX = readNextInt(packetData); - int chunkZ = readNextInt(packetData); - bool chunksContinuous = readNextBool(packetData); + int chunkX = dataTypes.ReadNextInt(packetData); + int chunkZ = dataTypes.ReadNextInt(packetData); + bool chunksContinuous = dataTypes.ReadNextBool(packetData); ushort chunkMask = protocolversion >= MC19Version - ? (ushort)readNextVarInt(packetData) - : readNextUShort(packetData); + ? (ushort)dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextUShort(packetData); if (protocolversion < MC18Version) { - ushort addBitmap = readNextUShort(packetData); - int compressedDataSize = readNextInt(packetData); - byte[] compressed = readData(compressedDataSize, packetData); + ushort addBitmap = dataTypes.ReadNextUShort(packetData); + int compressedDataSize = dataTypes.ReadNextInt(packetData); + byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); byte[] decompressed = ZlibUtils.Decompress(compressed); ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, new List(decompressed)); } @@ -553,9 +256,9 @@ namespace MinecraftClient.Protocol.Handlers { //TODO skip NBT Heightmaps field for 1.14 //if (protocolversion >= MC114Version) - // readNextNBT(packetData); + // dataTypes.ReadNextNBT(packetData); //TODO update Material.cs for 1.14 - int dataSize = readNextVarInt(packetData); + int dataSize = dataTypes.ReadNextVarInt(packetData); ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, packetData); } } @@ -563,11 +266,11 @@ namespace MinecraftClient.Protocol.Handlers case PacketIncomingType.MultiBlockChange: if (handler.GetTerrainEnabled()) { - int chunkX = readNextInt(packetData); - int chunkZ = readNextInt(packetData); + int chunkX = dataTypes.ReadNextInt(packetData); + int chunkZ = dataTypes.ReadNextInt(packetData); int recordCount = protocolversion < MC18Version - ? (int)readNextShort(packetData) - : readNextVarInt(packetData); + ? (int)dataTypes.ReadNextShort(packetData) + : dataTypes.ReadNextVarInt(packetData); for (int i = 0; i < recordCount; i++) { @@ -577,15 +280,15 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion < MC18Version) { - blockIdMeta = readNextUShort(packetData); - blockY = (ushort)readNextByte(packetData); - locationXZ = readNextByte(packetData); + blockIdMeta = dataTypes.ReadNextUShort(packetData); + blockY = (ushort)dataTypes.ReadNextByte(packetData); + locationXZ = dataTypes.ReadNextByte(packetData); } else { - locationXZ = readNextByte(packetData); - blockY = (ushort)readNextByte(packetData); - blockIdMeta = (ushort)readNextVarInt(packetData); + locationXZ = dataTypes.ReadNextByte(packetData); + blockY = (ushort)dataTypes.ReadNextByte(packetData); + blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData); } int blockX = locationXZ >> 4; @@ -600,14 +303,14 @@ namespace MinecraftClient.Protocol.Handlers { if (protocolversion < MC18Version) { - int blockX = readNextInt(packetData); - int blockY = readNextByte(packetData); - int blockZ = readNextInt(packetData); - short blockId = (short)readNextVarInt(packetData); - byte blockMeta = readNextByte(packetData); + int blockX = dataTypes.ReadNextInt(packetData); + int blockY = dataTypes.ReadNextByte(packetData); + int blockZ = dataTypes.ReadNextInt(packetData); + short blockId = (short)dataTypes.ReadNextVarInt(packetData); + byte blockMeta = dataTypes.ReadNextByte(packetData); handler.GetWorld().SetBlock(new Location(blockX, blockY, blockZ), new Block(blockId, blockMeta)); } - else handler.GetWorld().SetBlock(readNextLocation(packetData), new Block((ushort)readNextVarInt(packetData))); + else handler.GetWorld().SetBlock(dataTypes.ReadNextLocation(packetData), new Block((ushort)dataTypes.ReadNextVarInt(packetData))); } break; case PacketIncomingType.MapChunkBulk: @@ -620,17 +323,17 @@ namespace MinecraftClient.Protocol.Handlers //Read global fields if (protocolversion < MC18Version) { - chunkCount = readNextShort(packetData); - int compressedDataSize = readNextInt(packetData); - hasSkyLight = readNextBool(packetData); - byte[] compressed = readData(compressedDataSize, packetData); + chunkCount = dataTypes.ReadNextShort(packetData); + int compressedDataSize = dataTypes.ReadNextInt(packetData); + hasSkyLight = dataTypes.ReadNextBool(packetData); + byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); byte[] decompressed = ZlibUtils.Decompress(compressed); chunkData = new List(decompressed); } else { - hasSkyLight = readNextBool(packetData); - chunkCount = readNextVarInt(packetData); + hasSkyLight = dataTypes.ReadNextBool(packetData); + chunkCount = dataTypes.ReadNextVarInt(packetData); } //Read chunk records @@ -640,11 +343,11 @@ namespace MinecraftClient.Protocol.Handlers ushort[] addBitmaps = new ushort[chunkCount]; for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) { - chunkXs[chunkColumnNo] = readNextInt(packetData); - chunkZs[chunkColumnNo] = readNextInt(packetData); - chunkMasks[chunkColumnNo] = readNextUShort(packetData); + chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); + chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); + chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData); addBitmaps[chunkColumnNo] = protocolversion < MC18Version - ? readNextUShort(packetData) + ? dataTypes.ReadNextUShort(packetData) : (ushort)0; } @@ -656,44 +359,44 @@ namespace MinecraftClient.Protocol.Handlers case PacketIncomingType.UnloadChunk: if (protocolversion >= MC19Version && handler.GetTerrainEnabled()) { - int chunkX = readNextInt(packetData); - int chunkZ = readNextInt(packetData); + int chunkX = dataTypes.ReadNextInt(packetData); + int chunkZ = dataTypes.ReadNextInt(packetData); handler.GetWorld()[chunkX, chunkZ] = null; } break; case PacketIncomingType.PlayerListUpdate: if (protocolversion >= MC18Version) { - int action = readNextVarInt(packetData); - int numActions = readNextVarInt(packetData); + int action = dataTypes.ReadNextVarInt(packetData); + int numActions = dataTypes.ReadNextVarInt(packetData); for (int i = 0; i < numActions; i++) { - Guid uuid = readNextUUID(packetData); + Guid uuid = dataTypes.ReadNextUUID(packetData); switch (action) { case 0x00: //Player Join - string name = readNextString(packetData); - int propNum = readNextVarInt(packetData); + string name = dataTypes.ReadNextString(packetData); + int propNum = dataTypes.ReadNextVarInt(packetData); for (int p = 0; p < propNum; p++) { - string key = readNextString(packetData); - string val = readNextString(packetData); - if (readNextBool(packetData)) - readNextString(packetData); + string key = dataTypes.ReadNextString(packetData); + string val = dataTypes.ReadNextString(packetData); + if (dataTypes.ReadNextBool(packetData)) + dataTypes.ReadNextString(packetData); } - readNextVarInt(packetData); - readNextVarInt(packetData); - if (readNextBool(packetData)) - readNextString(packetData); + dataTypes.ReadNextVarInt(packetData); + dataTypes.ReadNextVarInt(packetData); + if (dataTypes.ReadNextBool(packetData)) + dataTypes.ReadNextString(packetData); handler.OnPlayerJoin(uuid, name); break; case 0x01: //Update gamemode case 0x02: //Update latency - readNextVarInt(packetData); + dataTypes.ReadNextVarInt(packetData); break; case 0x03: //Update display name - if (readNextBool(packetData)) - readNextString(packetData); + if (dataTypes.ReadNextBool(packetData)) + dataTypes.ReadNextString(packetData); break; case 0x04: //Player Leave handler.OnPlayerLeave(uuid); @@ -706,9 +409,9 @@ namespace MinecraftClient.Protocol.Handlers } else //MC 1.7.X does not provide UUID in tab-list updates { - string name = readNextString(packetData); - bool online = readNextBool(packetData); - short ping = readNextShort(packetData); + string name = dataTypes.ReadNextString(packetData); + bool online = dataTypes.ReadNextBool(packetData); + short ping = dataTypes.ReadNextShort(packetData); Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); if (online) handler.OnPlayerJoin(FakeUUID, name); @@ -718,191 +421,50 @@ namespace MinecraftClient.Protocol.Handlers case PacketIncomingType.TabCompleteResult: if (protocolversion >= MC113Version) { - autocomplete_transaction_id = readNextVarInt(packetData); - readNextVarInt(packetData); // Start of text to replace - readNextVarInt(packetData); // Length of text to replace + autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData); + dataTypes.ReadNextVarInt(packetData); // Start of text to replace + dataTypes.ReadNextVarInt(packetData); // Length of text to replace } - int autocomplete_count = readNextVarInt(packetData); + int autocomplete_count = dataTypes.ReadNextVarInt(packetData); autocomplete_result.Clear(); for (int i = 0; i < autocomplete_count; i++) { - autocomplete_result.Add(readNextString(packetData)); + autocomplete_result.Add(dataTypes.ReadNextString(packetData)); if (protocolversion >= MC113Version) { // Skip optional tooltip for each tab-complete result - if (readNextBool(packetData)) - readNextString(packetData); + if (dataTypes.ReadNextBool(packetData)) + dataTypes.ReadNextString(packetData); } } autocomplete_received = true; break; case PacketIncomingType.PluginMessage: - String channel = readNextString(packetData); + String channel = dataTypes.ReadNextString(packetData); + // Length is unneeded as the whole remaining packetData is the entire payload of the packet. if (protocolversion < MC18Version) - { - if (forgeInfo == null) - { - // 1.7 and lower prefix plugin channel packets with the length. - // We can skip it, though. - readNextShort(packetData); - } - else - { - // Forge does something even weirder with the length. - readNextVarShort(packetData); - } - } - - // The remaining data in the array is the entire payload of the packet. + pForge.ReadNextVarShort(packetData); handler.OnPluginChannelMessage(channel, packetData.ToArray()); - - #region Forge Login - if (forgeInfo != null && fmlHandshakeState != FMLHandshakeClientState.DONE) - { - if (channel == "FML|HS") - { - FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)readNextByte(packetData); - - if (discriminator == FMLHandshakeDiscriminator.HandshakeReset) - { - fmlHandshakeState = FMLHandshakeClientState.START; - return true; - } - - switch (fmlHandshakeState) - { - case FMLHandshakeClientState.START: - if (discriminator != FMLHandshakeDiscriminator.ServerHello) - return false; - - // Send the plugin channel registration. - // REGISTER is somewhat special in that it doesn't actually include length information, - // and is also \0-separated. - // Also, yes, "FML" is there twice. Don't ask me why, but that's the way forge does it. - string[] channels = { "FML|HS", "FML", "FML|MP", "FML", "FORGE" }; - SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels))); - - byte fmlProtocolVersion = readNextByte(packetData); - - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion); - - if (fmlProtocolVersion >= 1) - this.currentDimension = readNextInt(packetData); - - // Tell the server we're running the same version. - SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion }); - - // Then tell the server that we're running the same mods. - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Sending falsified mod list to server..."); - byte[][] mods = new byte[forgeInfo.Mods.Count][]; - for (int i = 0; i < forgeInfo.Mods.Count; i++) - { - ForgeInfo.ForgeMod mod = forgeInfo.Mods[i]; - mods[i] = concatBytes(getString(mod.ModID), getString(mod.Version)); - } - SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList, - concatBytes(getVarInt(forgeInfo.Mods.Count), concatBytes(mods))); - - fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERDATA; - - return true; - case FMLHandshakeClientState.WAITINGSERVERDATA: - if (discriminator != FMLHandshakeDiscriminator.ModList) - return false; - - Thread.Sleep(2000); - - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Accepting server mod list..."); - // Tell the server that yes, we are OK with the mods it has - // even though we don't actually care what mods it has. - - SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, - new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA }); - - fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERCOMPLETE; - return false; - case FMLHandshakeClientState.WAITINGSERVERCOMPLETE: - // The server now will tell us a bunch of registry information. - // We need to read it all, though, until it says that there is no more. - if (discriminator != FMLHandshakeDiscriminator.RegistryData) - return false; - - if (protocolversion < MC18Version) - { - // 1.7.10 and below have one registry - // with blocks and items. - int registrySize = readNextVarInt(packetData); - - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Received registry with " + registrySize + " entries"); - - fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; - } - else - { - // 1.8+ has more than one registry. - - bool hasNextRegistry = readNextBool(packetData); - string registryName = readNextString(packetData); - int registrySize = readNextVarInt(packetData); - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Received registry " + registryName + " with " + registrySize + " entries"); - if (!hasNextRegistry) - fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; - } - - return false; - case FMLHandshakeClientState.PENDINGCOMPLETE: - // The server will ask us to accept the registries. - // Just say yes. - if (discriminator != FMLHandshakeDiscriminator.HandshakeAck) - return false; - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Accepting server registries..."); - SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, - new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE }); - fmlHandshakeState = FMLHandshakeClientState.COMPLETE; - - return true; - case FMLHandshakeClientState.COMPLETE: - // One final "OK". On the actual forge source, a packet is sent from - // the client to the client saying that the connection was complete, but - // we don't need to do that. - - SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, - new byte[] { (byte)FMLHandshakeClientState.COMPLETE }); - if (Settings.DebugMessages) - ConsoleIO.WriteLine("Forge server connection complete!"); - fmlHandshakeState = FMLHandshakeClientState.DONE; - return true; - } - } - } - #endregion - - return false; + return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); case PacketIncomingType.KickPacket: - handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString(packetData))); + handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); return false; case PacketIncomingType.NetworkCompressionTreshold: if (protocolversion >= MC18Version && protocolversion < MC19Version) - compression_treshold = readNextVarInt(packetData); + compression_treshold = dataTypes.ReadNextVarInt(packetData); break; case PacketIncomingType.ResourcePackSend: - string url = readNextString(packetData); - string hash = readNextString(packetData); + string url = dataTypes.ReadNextString(packetData); + string hash = dataTypes.ReadNextString(packetData); //Send back "accepted" and "successfully loaded" responses for plugins making use of resource pack mandatory byte[] responseHeader = new byte[0]; if (protocolversion < MC110Version) //MC 1.10 does not include resource pack hash in responses - responseHeader = concatBytes(getVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); - SendPacket(PacketOutgoingType.ResourcePackStatus, concatBytes(responseHeader, getVarInt(3))); //Accepted pack - SendPacket(PacketOutgoingType.ResourcePackStatus, concatBytes(responseHeader, getVarInt(0))); //Successfully loaded + responseHeader = dataTypes.ConcatBytes(dataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); + SendPacket(PacketOutgoingType.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, dataTypes.GetVarInt(3))); //Accepted pack + SendPacket(PacketOutgoingType.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, dataTypes.GetVarInt(0))); //Successfully loaded break; default: return false; //Ignored packet @@ -913,7 +475,7 @@ namespace MinecraftClient.Protocol.Handlers { throw new System.IO.InvalidDataException( String.Format("Failed to process incoming packet of type {0}. (PacketID: {1}, Protocol: {2}, LoginPhase: {3}, InnerException: {4}).", - getPacketIncomingType(packetID, protocolversion), + Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion), packetID, protocolversion, login_phase, @@ -942,7 +504,7 @@ namespace MinecraftClient.Protocol.Handlers { if ((chunkMask & (1 << chunkY)) != 0) { - byte bitsPerBlock = readNextByte(cache); + byte bitsPerBlock = dataTypes.ReadNextByte(cache); bool usePalette = (bitsPerBlock <= 8); // Vanilla Minecraft will use at least 4 bits per block @@ -953,19 +515,19 @@ namespace MinecraftClient.Protocol.Handlers // is not used, MC 1.13+ does not send the field at all in this case int paletteLength = 0; // Assume zero when length is absent if (usePalette || protocolversion < MC113Version) - paletteLength = readNextVarInt(cache); + paletteLength = dataTypes.ReadNextVarInt(cache); int[] palette = new int[paletteLength]; for (int i = 0; i < paletteLength; i++) { - palette[i] = readNextVarInt(cache); + palette[i] = dataTypes.ReadNextVarInt(cache); } // Bit mask covering bitsPerBlock bits // EG, if bitsPerBlock = 5, valueMask = 00011111 in binary uint valueMask = (uint)((1 << bitsPerBlock) - 1); - ulong[] dataArray = readNextULongArray(cache); + ulong[] dataArray = dataTypes.ReadNextULongArray(cache); Chunk chunk = new Chunk(); @@ -1014,12 +576,12 @@ namespace MinecraftClient.Protocol.Handlers handler.GetWorld()[chunkX, chunkZ][chunkY] = chunk; //Skip block light - readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache); + dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache); //Skip sky light if (this.currentDimension == 0) // Sky light is not sent in the nether or the end - readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache); + dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache); } } @@ -1044,7 +606,7 @@ namespace MinecraftClient.Protocol.Handlers Chunk chunk = new Chunk(); //Read chunk data, all at once for performance reasons, and build the chunk object - Queue queue = new Queue(readNextUShortsLittleEndian(Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ, cache)); + Queue queue = new Queue(dataTypes.ReadNextUShortsLittleEndian(Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ, cache)); for (int blockY = 0; blockY < Chunk.SizeY; blockY++) for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++) for (int blockX = 0; blockX < Chunk.SizeX; blockX++) @@ -1063,17 +625,17 @@ namespace MinecraftClient.Protocol.Handlers if ((chunkMask & (1 << chunkY)) != 0) { //Skip block light - readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache); + dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache); //Skip sky light if (hasSkyLight) - readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache); + dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache); } } //Skip biome metadata if (chunksContinuous) - readData(Chunk.SizeX * Chunk.SizeZ, cache); + dataTypes.ReadData(Chunk.SizeX * Chunk.SizeZ, cache); } } else @@ -1098,9 +660,9 @@ namespace MinecraftClient.Protocol.Handlers } //Read chunk data, unpacking 4-bit values into 8-bit values for block metadata - Queue blockTypes = new Queue(readData(Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount, cache)); + Queue blockTypes = new Queue(dataTypes.ReadData(Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount, cache)); Queue blockMeta = new Queue(); - foreach (byte packed in readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache)) + foreach (byte packed in dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache)) { byte hig = (byte)(packed >> 4); byte low = (byte)(packed & (byte)0x0F); @@ -1109,12 +671,12 @@ namespace MinecraftClient.Protocol.Handlers } //Skip data we don't need - readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache); //Block light + dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache); //Block light if (hasSkyLight) - readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache); //Sky light - readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * addDataSectionCount) / 2, cache); //BlockAdd + dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache); //Sky light + dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * addDataSectionCount) / 2, cache); //BlockAdd if (chunksContinuous) - readData(Chunk.SizeX * Chunk.SizeZ, cache); //Biomes + dataTypes.ReadData(Chunk.SizeX * Chunk.SizeZ, cache); //Biomes //Load chunk data for (int chunkY = 0; chunkY < ChunkColumn.ColumnSize; chunkY++) @@ -1157,454 +719,12 @@ namespace MinecraftClient.Protocol.Handlers if (netRead != null) { netRead.Abort(); - c.Close(); + socketWrapper.Disconnect(); } } catch { } } - /// - /// Read some data directly from the network - /// - /// Amount of bytes to read - /// The data read from the network as an array - private byte[] readDataRAW(int offset) - { - if (offset > 0) - { - try - { - byte[] cache = new byte[offset]; - Receive(cache, 0, offset, SocketFlags.None); - return cache; - } - catch (OutOfMemoryException) { } - } - return new byte[] { }; - } - - /// - /// Read some data from a cache of bytes and remove it from the cache - /// - /// Amount of bytes to read - /// Cache of bytes to read from - /// The data read from the cache as an array - private static byte[] readData(int offset, List cache) - { - byte[] result = cache.Take(offset).ToArray(); - cache.RemoveRange(0, offset); - return result; - } - - /// - /// Read a string from a cache of bytes and remove it from the cache - /// - /// Cache of bytes to read from - /// The string - private static string readNextString(List cache) - { - int length = readNextVarInt(cache); - if (length > 0) - { - return Encoding.UTF8.GetString(readData(length, cache)); - } - else return ""; - } - - /// - /// Read a boolean from a cache of bytes and remove it from the cache - /// - /// The boolean value - private static bool readNextBool(List cache) - { - return readNextByte(cache) != 0x00; - } - - /// - /// Read a short integer from a cache of bytes and remove it from the cache - /// - /// The short integer value - private static short readNextShort(List cache) - { - byte[] rawValue = readData(2, cache); - Array.Reverse(rawValue); //Endianness - return BitConverter.ToInt16(rawValue, 0); - } - - /// - /// Read an integer from a cache of bytes and remove it from the cache - /// - /// The integer value - private static int readNextInt(List cache) - { - byte[] rawValue = readData(4, cache); - Array.Reverse(rawValue); //Endianness - return BitConverter.ToInt32(rawValue, 0); - } - - /// - /// Read an unsigned short integer from a cache of bytes and remove it from the cache - /// - /// The unsigned short integer value - private static ushort readNextUShort(List cache) - { - byte[] rawValue = readData(2, cache); - Array.Reverse(rawValue); //Endianness - return BitConverter.ToUInt16(rawValue, 0); - } - - /// - /// Read an unsigned long integer from a cache of bytes and remove it from the cache - /// - /// The unsigned long integer value - private static ulong readNextULong(List cache) - { - byte[] rawValue = readData(8, cache); - Array.Reverse(rawValue); //Endianness - return BitConverter.ToUInt64(rawValue, 0); - } - - /// - /// Read a Location encoded as an ulong field and remove it from the cache - /// - /// The Location value - private Location readNextLocation(List cache) - { - ulong locEncoded = readNextULong(cache); - int x, y, z; - if (protocolversion >= MC114Version) - { - x = (int)(locEncoded >> 38); - y = (int)(locEncoded & 0xFFF); - z = (int)(locEncoded << 26 >> 38); - } - else - { - x = (int)(locEncoded >> 38); - y = (int)((locEncoded >> 26) & 0xFFF); - z = (int)(locEncoded << 38 >> 38); - } - if (x >= 33554432) - x -= 67108864; - if (y >= 2048) - y -= 4096; - if (z >= 33554432) - z -= 67108864; - return new Location(x, y, z); - } - - /// - /// Read several little endian unsigned short integers at once from a cache of bytes and remove them from the cache - /// - /// The unsigned short integer value - private static ushort[] readNextUShortsLittleEndian(int amount, List cache) - { - byte[] rawValues = readData(2 * amount, cache); - ushort[] result = new ushort[amount]; - for (int i = 0; i < amount; i++) - result[i] = BitConverter.ToUInt16(rawValues, i * 2); - return result; - } - - /// - /// Read a uuid from a cache of bytes and remove it from the cache - /// - /// Cache of bytes to read from - /// The uuid - private static Guid readNextUUID(List cache) - { - byte[] javaUUID = readData(16, cache); - Guid guid; - if (BitConverter.IsLittleEndian) - { - guid = ToLittleEndian(javaUUID); - } - else - { - guid = new Guid(javaUUID); - } - return guid; - } - - /// - /// convert a Java big-endian Guid to a .NET little-endian Guid. - /// - /// GUID in little endian. - private static Guid ToLittleEndian(byte[] java) - { - byte[] net = new byte[16]; - for (int i = 8; i < 16; i++) - { - net[i] = java[i]; - } - net[3] = java[0]; - net[2] = java[1]; - net[1] = java[2]; - net[0] = java[3]; - net[5] = java[4]; - net[4] = java[5]; - net[6] = java[7]; - net[7] = java[6]; - return new Guid(net); - } - - /// - /// Read a byte array from a cache of bytes and remove it from the cache - /// - /// Cache of bytes to read from - /// The byte array - private byte[] readNextByteArray(List cache) - { - int len = protocolversion >= MC18Version - ? readNextVarInt(cache) - : readNextShort(cache); - return readData(len, cache); - } - - /// - /// Reads a length-prefixed array of unsigned long integers and removes it from the cache - /// - /// The unsigned long integer values - private static ulong[] readNextULongArray(List cache) - { - int len = readNextVarInt(cache); - ulong[] result = new ulong[len]; - for (int i = 0; i < len; i++) - result[i] = readNextULong(cache); - return result; - } - - /// - /// Read a double from a cache of bytes and remove it from the cache - /// - /// The double value - private static double readNextDouble(List cache) - { - byte[] rawValue = readData(8, cache); - Array.Reverse(rawValue); //Endianness - return BitConverter.ToDouble(rawValue, 0); - } - - /// - /// Read a float from a cache of bytes and remove it from the cache - /// - /// The float value - private static float readNextFloat(List cache) - { - byte[] rawValue = readData(4, cache); - Array.Reverse(rawValue); //Endianness - return BitConverter.ToSingle(rawValue, 0); - } - - /// - /// Read an integer from the network - /// - /// The integer - private int readNextVarIntRAW() - { - 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; - } - - /// - /// Read an integer from a cache of bytes and remove it from the cache - /// - /// Cache of bytes to read from - /// The integer - private static int readNextVarInt(List cache) - { - int i = 0; - int j = 0; - int k = 0; - while (true) - { - k = readNextByte(cache); - i |= (k & 0x7F) << j++ * 7; - if (j > 5) throw new OverflowException("VarInt too big"); - if ((k & 0x80) != 128) break; - } - return i; - } - - /// - /// Read an "extended short", which is actually an int of some kind, from the cache of bytes. - /// This is only done with forge. It looks like it's a normal short, except that if the high - /// bit is set, it has an extra byte. - /// - /// Cache of bytes to read from - /// The int - private static int readNextVarShort(List cache) - { - ushort low = readNextUShort(cache); - byte high = 0; - if ((low & 0x8000) != 0) - { - low &= 0x7FFF; - high = readNextByte(cache); - } - return ((high & 0xFF) << 15) | low; - } - - /// - /// Read a single byte from a cache of bytes and remove it from the cache - /// - /// The byte that was read - private static byte readNextByte(List cache) - { - byte result = cache[0]; - cache.RemoveAt(0); - return result; - } - - /// - /// 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(); - } - - /// - /// Get byte array representing a double - /// - /// Double to process - /// Array ready to send - private byte[] getDouble(double number) - { - byte[] theDouble = BitConverter.GetBytes(number); - Array.Reverse(theDouble); //Endianness - return theDouble; - } - - /// - /// Get byte array representing a float - /// - /// Floalt to process - /// Array ready to send - private byte[] getFloat(float number) - { - byte[] theFloat = BitConverter.GetBytes(number); - Array.Reverse(theFloat); //Endianness - return theFloat; - } - - - /// - /// 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); - } - - /// - /// Get a byte array from the given string for sending over the network, with length information prepended. - /// - /// String to process - /// Array ready to send - private static byte[] getString(string text) - { - byte[] bytes = Encoding.UTF8.GetBytes(text); - - return concatBytes(getVarInt(bytes.Length), bytes); - } - - /// - /// Get a byte array representing the given location encoded as an unsigned short - /// - /// - /// A modulo will be applied if the location is outside the following ranges: - /// X: -33,554,432 to +33,554,431 - /// Y: -2,048 to +2,047 - /// Z: -33,554,432 to +33,554,431 - /// - /// Location representation as ulong - private byte[] GetLocation(Location location) - { - if (protocolversion >= MC114Version) - { - return BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Z) & 0x3FFFFFF) << 12) | (((ulong)location.Y) & 0xFFF)); - } - else return BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Y) & 0xFFF) << 26) | (((ulong)location.Z) & 0x3FFFFFF)); - } - - /// - /// 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); - } - } - - /// - /// Send a forge plugin channel packet ("FML|HS"). Compression and encryption will be handled automatically - /// - /// Discriminator to use. - /// packet Data - private void SendForgeHandshakePacket(FMLHandshakeDiscriminator discriminator, byte[] data) - { - SendPluginChannelPacket("FML|HS", concatBytes(new byte[] { (byte)discriminator }, data)); - } - /// /// Send a packet to the server. Packet ID, compression, and encryption will be handled automatically. /// @@ -1612,7 +732,7 @@ namespace MinecraftClient.Protocol.Handlers /// packet Data private void SendPacket(PacketOutgoingType packet, IEnumerable packetData) { - SendPacket(getPacketOutgoingID(packet, protocolversion), packetData); + SendPacket(Protocol18PacketTypes.GetPacketOutgoingID(packet, protocolversion), packetData); } /// @@ -1623,36 +743,23 @@ namespace MinecraftClient.Protocol.Handlers private void SendPacket(int packetID, IEnumerable packetData) { //The inner packet - byte[] the_packet = concatBytes(getVarInt(packetID), packetData.ToArray()); + byte[] the_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(packetID), packetData.ToArray()); if (compression_treshold > 0) //Compression enabled? { if (the_packet.Length >= compression_treshold) //Packet long enough for compressing? { byte[] compressed_packet = ZlibUtils.Compress(the_packet); - the_packet = concatBytes(getVarInt(the_packet.Length), compressed_packet); + the_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), compressed_packet); } else { - byte[] uncompressed_length = getVarInt(0); //Not compressed (short packet) - the_packet = concatBytes(uncompressed_length, the_packet); + byte[] uncompressed_length = dataTypes.GetVarInt(0); //Not compressed (short packet) + the_packet = dataTypes.ConcatBytes(uncompressed_length, the_packet); } } - SendRAW(concatBytes(getVarInt(the_packet.Length), the_packet)); - } - - /// - /// Send raw data to the server. Encryption will be handled automatically. - /// - /// data to send - private void SendRAW(byte[] buffer) - { - if (encrypted) - { - s.Write(buffer, 0, buffer.Length); - } - else c.Client.Send(buffer); + socketWrapper.SendDataRAW(dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), the_packet)); } /// @@ -1661,15 +768,15 @@ namespace MinecraftClient.Protocol.Handlers /// True if login successful public bool Login() { - byte[] protocol_version = getVarInt(protocolversion); - string server_address = handler.GetServerHost() + (forgeInfo != null ? "\0FML\0" : ""); + byte[] protocol_version = dataTypes.GetVarInt(protocolversion); + string server_address = pForge.GetServerAddress(handler.GetServerHost()); byte[] server_port = BitConverter.GetBytes((ushort)handler.GetServerPort()); Array.Reverse(server_port); - byte[] next_state = getVarInt(2); - byte[] handshake_packet = concatBytes(protocol_version, getString(server_address), server_port, next_state); + byte[] next_state = dataTypes.GetVarInt(2); + byte[] handshake_packet = dataTypes.ConcatBytes(protocol_version, dataTypes.GetString(server_address), server_port, next_state); SendPacket(0x00, handshake_packet); - byte[] login_packet = getString(handler.GetUsername()); + byte[] login_packet = dataTypes.GetString(handler.GetUsername()); SendPacket(0x00, login_packet); @@ -1677,17 +784,17 @@ namespace MinecraftClient.Protocol.Handlers List packetData = new List(); while (true) { - readNextPacket(ref packetID, packetData); + ReadNextPacket(ref packetID, packetData); if (packetID == 0x00) //Login rejected { - handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(packetData))); + handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); return false; } else if (packetID == 0x01) //Encryption request { - string serverID = readNextString(packetData); - byte[] Serverkey = readNextByteArray(packetData); - byte[] token = readNextByteArray(packetData); + string serverID = dataTypes.ReadNextString(packetData); + byte[] Serverkey = dataTypes.ReadNextByteArray(packetData); + byte[] token = dataTypes.ReadNextByteArray(packetData); return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, Serverkey); } else if (packetID == 0x02) //Login successful @@ -1695,49 +802,16 @@ namespace MinecraftClient.Protocol.Handlers ConsoleIO.WriteLineFormatted("§8Server is in offline mode."); login_phase = false; - if (forgeInfo != null) - { - // Do the forge handshake. - if (!CompleteForgeHandshake()) - { - return false; - } - } + if (!pForge.CompleteForgeHandshake()) + return false; StartUpdating(); return true; //No need to check session or start encryption } - else handlePacket(packetID, packetData); + else HandlePacket(packetID, packetData); } } - /// - /// Completes the Minecraft Forge handshake. - /// - /// Whether the handshake was successful. - private bool CompleteForgeHandshake() - { - int packetID = -1; - List packetData = new List(); - - while (fmlHandshakeState != FMLHandshakeClientState.DONE) - { - readNextPacket(ref packetID, packetData); - - if (packetID == 0x40) // Disconnect - { - handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(packetData))); - return false; - } - else - { - handlePacket(packetID, packetData); - } - } - - return true; - } - /// /// Start network encryption. Automatically called by Login() if the server requests encryption. /// @@ -1761,44 +835,37 @@ namespace MinecraftClient.Protocol.Handlers } //Encrypt the data - byte[] key_enc = getArray(RSAService.Encrypt(secretKey, false)); - byte[] token_enc = getArray(RSAService.Encrypt(token, false)); + byte[] key_enc = dataTypes.GetArray(RSAService.Encrypt(secretKey, false)); + byte[] token_enc = dataTypes.GetArray(RSAService.Encrypt(token, false)); //Encryption Response packet - SendPacket(0x01, concatBytes(key_enc, token_enc)); + SendPacket(0x01, dataTypes.ConcatBytes(key_enc, token_enc)); //Start client-side encryption - s = CryptoHandler.getAesStream(c.GetStream(), secretKey); - encrypted = true; + socketWrapper.SwitchToEncrypted(secretKey); //Process the next packet int packetID = -1; List packetData = new List(); while (true) { - readNextPacket(ref packetID, packetData); + ReadNextPacket(ref packetID, packetData); if (packetID == 0x00) //Login rejected { - handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(packetData))); + handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); return false; } else if (packetID == 0x02) //Login successful { login_phase = false; - if (forgeInfo != null) - { - // Do the forge handshake. - if (!CompleteForgeHandshake()) - { - return false; - } - } + if (!pForge.CompleteForgeHandshake()) + return false; StartUpdating(); return true; } - else handlePacket(packetID, packetData); + else HandlePacket(packetID, packetData); } } @@ -1824,7 +891,7 @@ namespace MinecraftClient.Protocol.Handlers return true; try { - byte[] message_packet = getString(message); + byte[] message_packet = dataTypes.GetString(message); SendPacket(PacketOutgoingType.ChatMessage, message_packet); return true; } @@ -1859,11 +926,11 @@ namespace MinecraftClient.Protocol.Handlers // https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14132#Plugin_Channels if (protocolversion >= MC113Version) { - return SendPluginChannelPacket("minecraft:brand", getString(brandInfo)); + return SendPluginChannelPacket("minecraft:brand", dataTypes.GetString(brandInfo)); } else { - return SendPluginChannelPacket("MC|Brand", getString(brandInfo)); + return SendPluginChannelPacket("MC|Brand", dataTypes.GetString(brandInfo)); } } @@ -1883,10 +950,10 @@ namespace MinecraftClient.Protocol.Handlers try { List fields = new List(); - fields.AddRange(getString(language)); + fields.AddRange(dataTypes.GetString(language)); fields.Add(viewDistance); fields.AddRange(protocolversion >= MC19Version - ? getVarInt(chatMode) + ? dataTypes.GetVarInt(chatMode) : new byte[] { chatMode }); fields.Add(chatColors ? (byte)1 : (byte)0); if (protocolversion < MC18Version) @@ -1896,7 +963,7 @@ namespace MinecraftClient.Protocol.Handlers } else fields.Add(skinParts); if (protocolversion >= MC19Version) - fields.AddRange(getVarInt(mainHand)); + fields.AddRange(dataTypes.GetVarInt(mainHand)); SendPacket(PacketOutgoingType.ClientSettings, fields); } catch (SocketException) { } @@ -1920,19 +987,19 @@ namespace MinecraftClient.Protocol.Handlers if (yaw.HasValue && pitch.HasValue) { - yawpitch = concatBytes(getFloat(yaw.Value), getFloat(pitch.Value)); + yawpitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), dataTypes.GetFloat(pitch.Value)); packetType = PacketOutgoingType.PlayerPositionAndLook; } try { - SendPacket(packetType, concatBytes( - getDouble(location.X), - getDouble(location.Y), + SendPacket(packetType, dataTypes.ConcatBytes( + dataTypes.GetDouble(location.X), + dataTypes.GetDouble(location.Y), protocolversion < MC18Version - ? getDouble(location.Y + 1.62) + ? dataTypes.GetDouble(location.Y + 1.62) : new byte[0], - getDouble(location.Z), + dataTypes.GetDouble(location.Z), yawpitch, new byte[] { onGround ? (byte)1 : (byte)0 })); return true; @@ -1958,11 +1025,11 @@ namespace MinecraftClient.Protocol.Handlers byte[] length = BitConverter.GetBytes((short)data.Length); Array.Reverse(length); - SendPacket(PacketOutgoingType.PluginMessage, concatBytes(getString(channel), length, data)); + SendPacket(PacketOutgoingType.PluginMessage, dataTypes.ConcatBytes(dataTypes.GetString(channel), length, data)); } else { - SendPacket(PacketOutgoingType.PluginMessage, concatBytes(getString(channel), data)); + SendPacket(PacketOutgoingType.PluginMessage, dataTypes.ConcatBytes(dataTypes.GetString(channel), data)); } return true; @@ -1976,14 +1043,7 @@ namespace MinecraftClient.Protocol.Handlers /// public void Disconnect() { - try - { - c.Close(); - } - catch (SocketException) { } - catch (System.IO.IOException) { } - catch (NullReferenceException) { } - catch (ObjectDisposedException) { } + socketWrapper.Disconnect(); } /// @@ -1996,7 +1056,7 @@ namespace MinecraftClient.Protocol.Handlers if (String.IsNullOrEmpty(BehindCursor)) return new string[] { }; - byte[] transaction_id = getVarInt(autocomplete_transaction_id); + byte[] transaction_id = dataTypes.GetVarInt(autocomplete_transaction_id); byte[] assume_command = new byte[] { 0x00 }; byte[] has_position = new byte[] { 0x00 }; @@ -2006,24 +1066,24 @@ namespace MinecraftClient.Protocol.Handlers { if (protocolversion >= MC113Version) { - tabcomplete_packet = concatBytes(tabcomplete_packet, transaction_id); - tabcomplete_packet = concatBytes(tabcomplete_packet, getString(BehindCursor)); + tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, transaction_id); + tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor)); } else { - tabcomplete_packet = concatBytes(tabcomplete_packet, getString(BehindCursor)); + tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor)); if (protocolversion >= MC19Version) { - tabcomplete_packet = concatBytes(tabcomplete_packet, assume_command); + tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, assume_command); } - tabcomplete_packet = concatBytes(tabcomplete_packet, has_position); + tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, has_position); } } else { - tabcomplete_packet = concatBytes(getString(BehindCursor)); + tabcomplete_packet = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor)); } autocomplete_received = false; @@ -2047,29 +1107,30 @@ namespace MinecraftClient.Protocol.Handlers string version = ""; TcpClient tcp = ProxyHandler.newTcpClient(host, port); tcp.ReceiveBufferSize = 1024 * 1024; + SocketWrapper socketWrapper = new SocketWrapper(tcp); + DataTypes dataTypes = new DataTypes(MC18Version); - byte[] packet_id = getVarInt(0); - byte[] protocol_version = getVarInt(-1); + byte[] packet_id = dataTypes.GetVarInt(0); + byte[] protocol_version = dataTypes.GetVarInt(-1); byte[] server_port = BitConverter.GetBytes((ushort)port); Array.Reverse(server_port); - byte[] next_state = getVarInt(1); - byte[] packet = concatBytes(packet_id, protocol_version, getString(host), server_port, next_state); - byte[] tosend = concatBytes(getVarInt(packet.Length), packet); + byte[] next_state = dataTypes.GetVarInt(1); + byte[] packet = dataTypes.ConcatBytes(packet_id, protocol_version, dataTypes.GetString(host), server_port, next_state); + byte[] tosend = dataTypes.ConcatBytes(dataTypes.GetVarInt(packet.Length), packet); - tcp.Client.Send(tosend, SocketFlags.None); + socketWrapper.SendDataRAW(tosend); - byte[] status_request = getVarInt(0); - byte[] request_packet = concatBytes(getVarInt(status_request.Length), status_request); + byte[] status_request = dataTypes.GetVarInt(0); + byte[] request_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(status_request.Length), status_request); - tcp.Client.Send(request_packet, SocketFlags.None); + socketWrapper.SendDataRAW(request_packet); - Protocol18Handler ComTmp = new Protocol18Handler(tcp); - int packetLength = ComTmp.readNextVarIntRAW(); + int packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper); if (packetLength > 0) //Read Response length { - List packetData = new List(ComTmp.readDataRAW(packetLength)); - if (readNextVarInt(packetData) == 0x00) //Read Packet ID + List packetData = new List(socketWrapper.ReadDataRAW(packetLength)); + if (dataTypes.ReadNextVarInt(packetData) == 0x00) //Read Packet ID { - string result = readNextString(packetData); //Get the Json data + string result = dataTypes.ReadNextString(packetData); //Get the Json data if (!String.IsNullOrEmpty(result) && result.StartsWith("{") && result.EndsWith("}")) { @@ -2084,11 +1145,7 @@ namespace MinecraftClient.Protocol.Handlers //Retrieve protocol version number for handling this server if (versionData.Properties.ContainsKey("protocol")) - protocolversion = atoi(versionData.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"); + protocolversion = dataTypes.Atoi(versionData.Properties["protocol"].StringValue); // Check for forge on the server. if (jsonData.Properties.ContainsKey("modinfo") && jsonData.Properties["modinfo"].Type == Json.JSONData.DataType.Object) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs new file mode 100644 index 00000000..2400731a --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MinecraftClient.Protocol.Handlers.Forge; +using System.Threading; + +namespace MinecraftClient.Protocol.Handlers +{ + /// + /// Handler for the Minecraft Forge protocol + /// + class Protocol18Forge + { + private int protocolversion; + private DataTypes dataTypes; + private Protocol18Handler protocol18; + private IMinecraftComHandler mcHandler; + + private ForgeInfo forgeInfo; + private FMLHandshakeClientState fmlHandshakeState = FMLHandshakeClientState.START; + private bool ForgeEnabled() { return forgeInfo != null; } + + /// + /// Initialize a new Forge protocol handler + /// + /// Forge Server Information + /// Minecraft protocol version + /// Minecraft data types handler + public Protocol18Forge(ForgeInfo forgeInfo, int protocolVersion, DataTypes dataTypes, Protocol18Handler protocol18, IMinecraftComHandler mcHandler) + { + this.forgeInfo = forgeInfo; + this.protocolversion = protocolVersion; + this.dataTypes = dataTypes; + this.protocol18 = protocol18; + this.mcHandler = mcHandler; + } + + /// + /// Get Forge-Tagged server address + /// + /// Server Address + /// Forge-Tagged server address + public string GetServerAddress(string serverAddress) + { + if (ForgeEnabled()) + return serverAddress + "\0FML\0"; + return serverAddress; + } + + /// + /// Completes the Minecraft Forge handshake. + /// + /// Whether the handshake was successful. + public bool CompleteForgeHandshake() + { + if (ForgeEnabled()) + { + int packetID = -1; + List packetData = new List(); + + while (fmlHandshakeState != FMLHandshakeClientState.DONE) + { + protocol18.ReadNextPacket(ref packetID, packetData); + + if (packetID == 0x40) // Disconnect + { + mcHandler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); + return false; + } + else + { + // Send back regular packet to the vanilla protocol handler + protocol18.HandlePacket(packetID, packetData); + } + } + } + return true; + } + + /// + /// Read Forge VarShort field + /// + /// Packet data to read from + /// Length from packet data + public int ReadNextVarShort(List packetData) + { + if (ForgeEnabled()) + { + // Forge special VarShort field. + return (int)dataTypes.ReadNextVarShort(packetData); + } + else + { + // Vanilla regular Short field + return (int)dataTypes.ReadNextShort(packetData); + } + } + + /// + /// Handle Forge plugin messages + /// + /// Plugin message channel + /// Plugin message data + /// Current world dimension + /// TRUE if the plugin message was recognized and handled + public bool HandlePluginMessage(string channel, List packetData, ref int currentDimension) + { + if (ForgeEnabled() && fmlHandshakeState != FMLHandshakeClientState.DONE) + { + if (channel == "FML|HS") + { + FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)dataTypes.ReadNextByte(packetData); + + if (discriminator == FMLHandshakeDiscriminator.HandshakeReset) + { + fmlHandshakeState = FMLHandshakeClientState.START; + return true; + } + + switch (fmlHandshakeState) + { + case FMLHandshakeClientState.START: + if (discriminator != FMLHandshakeDiscriminator.ServerHello) + return false; + + // Send the plugin channel registration. + // REGISTER is somewhat special in that it doesn't actually include length information, + // and is also \0-separated. + // Also, yes, "FML" is there twice. Don't ask me why, but that's the way forge does it. + string[] channels = { "FML|HS", "FML", "FML|MP", "FML", "FORGE" }; + protocol18.SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels))); + + byte fmlProtocolVersion = dataTypes.ReadNextByte(packetData); + + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion); + + if (fmlProtocolVersion >= 1) + currentDimension = dataTypes.ReadNextInt(packetData); + + // Tell the server we're running the same version. + SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion }); + + // Then tell the server that we're running the same mods. + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Sending falsified mod list to server..."); + byte[][] mods = new byte[forgeInfo.Mods.Count][]; + for (int i = 0; i < forgeInfo.Mods.Count; i++) + { + ForgeInfo.ForgeMod mod = forgeInfo.Mods[i]; + mods[i] = dataTypes.ConcatBytes(dataTypes.GetString(mod.ModID), dataTypes.GetString(mod.Version)); + } + SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList, + dataTypes.ConcatBytes(dataTypes.GetVarInt(forgeInfo.Mods.Count), dataTypes.ConcatBytes(mods))); + + fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERDATA; + + return true; + case FMLHandshakeClientState.WAITINGSERVERDATA: + if (discriminator != FMLHandshakeDiscriminator.ModList) + return false; + + Thread.Sleep(2000); + + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Accepting server mod list..."); + // Tell the server that yes, we are OK with the mods it has + // even though we don't actually care what mods it has. + + SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, + new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA }); + + fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERCOMPLETE; + return false; + case FMLHandshakeClientState.WAITINGSERVERCOMPLETE: + // The server now will tell us a bunch of registry information. + // We need to read it all, though, until it says that there is no more. + if (discriminator != FMLHandshakeDiscriminator.RegistryData) + return false; + + if (protocolversion < Protocol18Handler.MC18Version) + { + // 1.7.10 and below have one registry + // with blocks and items. + int registrySize = dataTypes.ReadNextVarInt(packetData); + + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Received registry with " + registrySize + " entries"); + + fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; + } + else + { + // 1.8+ has more than one registry. + + bool hasNextRegistry = dataTypes.ReadNextBool(packetData); + string registryName = dataTypes.ReadNextString(packetData); + int registrySize = dataTypes.ReadNextVarInt(packetData); + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Received registry " + registryName + " with " + registrySize + " entries"); + if (!hasNextRegistry) + fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; + } + + return false; + case FMLHandshakeClientState.PENDINGCOMPLETE: + // The server will ask us to accept the registries. + // Just say yes. + if (discriminator != FMLHandshakeDiscriminator.HandshakeAck) + return false; + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Accepting server registries..."); + SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, + new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE }); + fmlHandshakeState = FMLHandshakeClientState.COMPLETE; + + return true; + case FMLHandshakeClientState.COMPLETE: + // One final "OK". On the actual forge source, a packet is sent from + // the client to the client saying that the connection was complete, but + // we don't need to do that. + + SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, + new byte[] { (byte)FMLHandshakeClientState.COMPLETE }); + if (Settings.DebugMessages) + ConsoleIO.WriteLine("Forge server connection complete!"); + fmlHandshakeState = FMLHandshakeClientState.DONE; + return true; + } + } + } + return false; + } + + /// + /// Send a forge plugin channel packet ("FML|HS"). Compression and encryption will be handled automatically + /// + /// Discriminator to use. + /// packet Data + private void SendForgeHandshakePacket(FMLHandshakeDiscriminator discriminator, byte[] data) + { + protocol18.SendPluginChannelPacket("FML|HS", dataTypes.ConcatBytes(new byte[] { (byte)discriminator }, data)); + } + } +} diff --git a/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs b/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs new file mode 100644 index 00000000..c35feb20 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Protocol.Handlers +{ + /// + /// Contains packet ID mappings for Protocol18 + /// + class Protocol18PacketTypes + { + /// + /// Get abstract numbering of the specified packet ID + /// + /// Packet ID + /// Protocol version + /// Abstract numbering + public static PacketIncomingType GetPacketIncomingType(int packetID, int protocol) + { + if (protocol <= Protocol18Handler.MC18Version) // MC 1.7 and 1.8 + { + switch (packetID) + { + case 0x00: return PacketIncomingType.KeepAlive; + case 0x01: return PacketIncomingType.JoinGame; + case 0x02: return PacketIncomingType.ChatMessage; + case 0x07: return PacketIncomingType.Respawn; + case 0x08: return PacketIncomingType.PlayerPositionAndLook; + case 0x21: return PacketIncomingType.ChunkData; + case 0x22: return PacketIncomingType.MultiBlockChange; + case 0x23: return PacketIncomingType.BlockChange; + case 0x26: return PacketIncomingType.MapChunkBulk; + //UnloadChunk does not exists prior to 1.9 + case 0x38: return PacketIncomingType.PlayerListUpdate; + case 0x3A: return PacketIncomingType.TabCompleteResult; + case 0x3F: return PacketIncomingType.PluginMessage; + case 0x40: return PacketIncomingType.KickPacket; + case 0x46: return PacketIncomingType.NetworkCompressionTreshold; + case 0x48: return PacketIncomingType.ResourcePackSend; + default: return PacketIncomingType.UnknownPacket; + } + } + else if (protocol <= Protocol18Handler.MC1112Version) // MC 1.9, 1.10 and 1.11 + { + switch (packetID) + { + case 0x1F: return PacketIncomingType.KeepAlive; + case 0x23: return PacketIncomingType.JoinGame; + case 0x0F: return PacketIncomingType.ChatMessage; + case 0x33: return PacketIncomingType.Respawn; + case 0x2E: return PacketIncomingType.PlayerPositionAndLook; + case 0x20: return PacketIncomingType.ChunkData; + case 0x10: return PacketIncomingType.MultiBlockChange; + case 0x0B: return PacketIncomingType.BlockChange; + //MapChunkBulk removed in 1.9 + case 0x1D: return PacketIncomingType.UnloadChunk; + case 0x2D: return PacketIncomingType.PlayerListUpdate; + case 0x0E: return PacketIncomingType.TabCompleteResult; + case 0x18: return PacketIncomingType.PluginMessage; + case 0x1A: return PacketIncomingType.KickPacket; + //NetworkCompressionTreshold removed in 1.9 + case 0x32: return PacketIncomingType.ResourcePackSend; + default: return PacketIncomingType.UnknownPacket; + } + } + else if (protocol <= Protocol18Handler.MC112Version) // MC 1.12.0 + { + switch (packetID) + { + case 0x1F: return PacketIncomingType.KeepAlive; + case 0x23: return PacketIncomingType.JoinGame; + case 0x0F: return PacketIncomingType.ChatMessage; + case 0x34: return PacketIncomingType.Respawn; + case 0x2E: return PacketIncomingType.PlayerPositionAndLook; + case 0x20: return PacketIncomingType.ChunkData; + case 0x10: return PacketIncomingType.MultiBlockChange; + case 0x0B: return PacketIncomingType.BlockChange; + case 0x1D: return PacketIncomingType.UnloadChunk; + case 0x2D: return PacketIncomingType.PlayerListUpdate; + case 0x0E: return PacketIncomingType.TabCompleteResult; + case 0x18: return PacketIncomingType.PluginMessage; + case 0x1A: return PacketIncomingType.KickPacket; + case 0x33: return PacketIncomingType.ResourcePackSend; + default: return PacketIncomingType.UnknownPacket; + } + } + else if (protocol <= Protocol18Handler.MC1122Version) // MC 1.12.2 + { + switch (packetID) + { + case 0x1F: return PacketIncomingType.KeepAlive; + case 0x23: return PacketIncomingType.JoinGame; + case 0x0F: return PacketIncomingType.ChatMessage; + case 0x35: return PacketIncomingType.Respawn; + case 0x2F: return PacketIncomingType.PlayerPositionAndLook; + case 0x20: return PacketIncomingType.ChunkData; + case 0x10: return PacketIncomingType.MultiBlockChange; + case 0x0B: return PacketIncomingType.BlockChange; + case 0x1D: return PacketIncomingType.UnloadChunk; + case 0x2E: return PacketIncomingType.PlayerListUpdate; + case 0x0E: return PacketIncomingType.TabCompleteResult; + case 0x18: return PacketIncomingType.PluginMessage; + case 0x1A: return PacketIncomingType.KickPacket; + case 0x34: return PacketIncomingType.ResourcePackSend; + default: return PacketIncomingType.UnknownPacket; + } + } + else if (protocol < Protocol18Handler.MC114Version) // MC 1.13 to 1.13.2 + { + switch (packetID) + { + case 0x21: return PacketIncomingType.KeepAlive; + case 0x25: return PacketIncomingType.JoinGame; + case 0x0E: return PacketIncomingType.ChatMessage; + case 0x38: return PacketIncomingType.Respawn; + case 0x32: return PacketIncomingType.PlayerPositionAndLook; + case 0x22: return PacketIncomingType.ChunkData; + case 0x0F: return PacketIncomingType.MultiBlockChange; + case 0x0B: return PacketIncomingType.BlockChange; + case 0x1F: return PacketIncomingType.UnloadChunk; + case 0x30: return PacketIncomingType.PlayerListUpdate; + case 0x10: return PacketIncomingType.TabCompleteResult; + case 0x19: return PacketIncomingType.PluginMessage; + case 0x1B: return PacketIncomingType.KickPacket; + case 0x37: return PacketIncomingType.ResourcePackSend; + default: return PacketIncomingType.UnknownPacket; + } + } + else // MC 1.14 + { + switch (packetID) + { + case 0x20: return PacketIncomingType.KeepAlive; + case 0x25: return PacketIncomingType.JoinGame; + case 0x0E: return PacketIncomingType.ChatMessage; + case 0x3A: return PacketIncomingType.Respawn; + case 0x35: return PacketIncomingType.PlayerPositionAndLook; + case 0x21: return PacketIncomingType.ChunkData; + case 0x0F: return PacketIncomingType.MultiBlockChange; + case 0x0B: return PacketIncomingType.BlockChange; + case 0x1D: return PacketIncomingType.UnloadChunk; + case 0x33: return PacketIncomingType.PlayerListUpdate; + case 0x10: return PacketIncomingType.TabCompleteResult; + case 0x18: return PacketIncomingType.PluginMessage; + case 0x1A: return PacketIncomingType.KickPacket; + case 0x39: return PacketIncomingType.ResourcePackSend; + default: return PacketIncomingType.UnknownPacket; + } + } + } + + /// + /// Get packet ID of the specified outgoing packet + /// + /// Abstract packet numbering + /// Protocol version + /// Packet ID + public static int GetPacketOutgoingID(PacketOutgoingType packet, int protocol) + { + if (protocol <= Protocol18Handler.MC18Version) // MC 1.7 and 1.8 + { + switch (packet) + { + case PacketOutgoingType.KeepAlive: return 0x00; + case PacketOutgoingType.ResourcePackStatus: return 0x19; + case PacketOutgoingType.ChatMessage: return 0x01; + case PacketOutgoingType.ClientStatus: return 0x16; + case PacketOutgoingType.ClientSettings: return 0x15; + case PacketOutgoingType.PluginMessage: return 0x17; + case PacketOutgoingType.TabComplete: return 0x14; + case PacketOutgoingType.PlayerPosition: return 0x04; + case PacketOutgoingType.PlayerPositionAndLook: return 0x06; + case PacketOutgoingType.TeleportConfirm: throw new InvalidOperationException("Teleport confirm is not supported in protocol " + protocol); + } + } + else if (protocol <= Protocol18Handler.MC1112Version) // MC 1.9, 1,10 and 1.11 + { + switch (packet) + { + case PacketOutgoingType.KeepAlive: return 0x0B; + case PacketOutgoingType.ResourcePackStatus: return 0x16; + case PacketOutgoingType.ChatMessage: return 0x02; + case PacketOutgoingType.ClientStatus: return 0x03; + case PacketOutgoingType.ClientSettings: return 0x04; + case PacketOutgoingType.PluginMessage: return 0x09; + case PacketOutgoingType.TabComplete: return 0x01; + case PacketOutgoingType.PlayerPosition: return 0x0C; + case PacketOutgoingType.PlayerPositionAndLook: return 0x0D; + case PacketOutgoingType.TeleportConfirm: return 0x00; + } + } + else if (protocol <= Protocol18Handler.MC112Version) // MC 1.12 + { + switch (packet) + { + case PacketOutgoingType.KeepAlive: return 0x0C; + case PacketOutgoingType.ResourcePackStatus: return 0x18; + case PacketOutgoingType.ChatMessage: return 0x03; + case PacketOutgoingType.ClientStatus: return 0x04; + case PacketOutgoingType.ClientSettings: return 0x05; + case PacketOutgoingType.PluginMessage: return 0x0A; + case PacketOutgoingType.TabComplete: return 0x02; + case PacketOutgoingType.PlayerPosition: return 0x0E; + case PacketOutgoingType.PlayerPositionAndLook: return 0x0F; + case PacketOutgoingType.TeleportConfirm: return 0x00; + } + } + else if (protocol <= Protocol18Handler.MC1122Version) // 1.12.2 + { + switch (packet) + { + case PacketOutgoingType.KeepAlive: return 0x0B; + case PacketOutgoingType.ResourcePackStatus: return 0x18; + case PacketOutgoingType.ChatMessage: return 0x02; + case PacketOutgoingType.ClientStatus: return 0x03; + case PacketOutgoingType.ClientSettings: return 0x04; + case PacketOutgoingType.PluginMessage: return 0x09; + case PacketOutgoingType.TabComplete: return 0x01; + case PacketOutgoingType.PlayerPosition: return 0x0D; + case PacketOutgoingType.PlayerPositionAndLook: return 0x0E; + case PacketOutgoingType.TeleportConfirm: return 0x00; + } + } + else if (protocol < Protocol18Handler.MC114Version) // MC 1.13 to 1.13.2 + { + switch (packet) + { + case PacketOutgoingType.KeepAlive: return 0x0E; + case PacketOutgoingType.ResourcePackStatus: return 0x1D; + case PacketOutgoingType.ChatMessage: return 0x02; + case PacketOutgoingType.ClientStatus: return 0x03; + case PacketOutgoingType.ClientSettings: return 0x04; + case PacketOutgoingType.PluginMessage: return 0x0A; + case PacketOutgoingType.TabComplete: return 0x05; + case PacketOutgoingType.PlayerPosition: return 0x10; + case PacketOutgoingType.PlayerPositionAndLook: return 0x11; + case PacketOutgoingType.TeleportConfirm: return 0x00; + } + } + else // MC 1.14 + { + switch (packet) + { + case PacketOutgoingType.KeepAlive: return 0x0F; + case PacketOutgoingType.ResourcePackStatus: return 0x1F; + case PacketOutgoingType.ChatMessage: return 0x03; + case PacketOutgoingType.ClientStatus: return 0x04; + case PacketOutgoingType.ClientSettings: return 0x05; + case PacketOutgoingType.PluginMessage: return 0x0B; + case PacketOutgoingType.TabComplete: return 0x06; + case PacketOutgoingType.PlayerPosition: return 0x11; + case PacketOutgoingType.PlayerPositionAndLook: return 0x12; + case PacketOutgoingType.TeleportConfirm: return 0x00; + } + } + + throw new System.ComponentModel.InvalidEnumArgumentException("Unknown PacketOutgoingType (protocol=" + protocol + ")", (int)packet, typeof(PacketOutgoingType)); + } + } +} diff --git a/MinecraftClient/Protocol/Handlers/SocketWrapper.cs b/MinecraftClient/Protocol/Handlers/SocketWrapper.cs new file mode 100644 index 00000000..cdcce762 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/SocketWrapper.cs @@ -0,0 +1,116 @@ +using System; +using System.Net.Sockets; +using MinecraftClient.Crypto; + +namespace MinecraftClient.Protocol.Handlers +{ + /// + /// Wrapper for handling unencrypted & encrypted socket + /// + class SocketWrapper + { + TcpClient c; + IAesStream s; + bool encrypted = false; + + /// + /// Initialize a new SocketWrapper + /// + /// TcpClient connected to the server + public SocketWrapper(TcpClient client) + { + this.c = client; + } + + /// + /// Check if the socket is still connected + /// + /// TRUE if still connected + /// Silently dropped connection can only be detected by attempting to read/write data + public bool IsConnected() + { + return c.Client != null && c.Connected; + } + + /// + /// Check if the socket has data available to read + /// + /// TRUE if data is available to read + public bool HasDataAvailable() + { + return c.Client.Available > 0; + } + + /// + /// Switch network reading/writing to an encrypted stream + /// + /// AES secret key + public void SwitchToEncrypted(byte[] secretKey) + { + if (encrypted) + throw new InvalidOperationException("Stream is already encrypted!?"); + this.s = CryptoHandler.getAesStream(c.GetStream(), secretKey); + this.encrypted = true; + } + + /// + /// 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); + } + } + + /// + /// Read some data from the server. + /// + /// Amount of bytes to read + /// The data read from the network as an array + public byte[] ReadDataRAW(int length) + { + if (length > 0) + { + byte[] cache = new byte[length]; + Receive(cache, 0, length, SocketFlags.None); + return cache; + } + return new byte[] { }; + } + + /// + /// Send raw data to the server. + /// + /// data to send + public void SendDataRAW(byte[] buffer) + { + if (encrypted) + { + s.Write(buffer, 0, buffer.Length); + } + else c.Client.Send(buffer); + } + + /// + /// Disconnect from the server + /// + public void Disconnect() + { + try + { + c.Close(); + } + catch (SocketException) { } + catch (System.IO.IOException) { } + catch (NullReferenceException) { } + catch (ObjectDisposedException) { } + } + } +}