From 424eab29dd2d4848142d368b6458ddc7e7cae1b1 Mon Sep 17 00:00:00 2001
From: Bas950 <34792838+Bas950@users.noreply.github.com>
Date: Mon, 29 Jun 2020 16:05:12 +0200
Subject: [PATCH] Join Game and Respawn encoding/decoding & added
ReadNextVarIntArray
---
.../Protocol/Handlers/DataTypes.cs | 1733 ++++-----
.../Protocol/Handlers/Protocol18.cs | 3105 +++++++++--------
2 files changed, 2446 insertions(+), 2392 deletions(-)
diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs
index 5a077073..0b7ae768 100644
--- a/MinecraftClient/Protocol/Handlers/DataTypes.cs
+++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs
@@ -10,864 +10,885 @@ using MinecraftClient.Mapping.EntityPalettes;
namespace MinecraftClient.Protocol.Handlers
{
+ ///
+ /// Handle data types encoding / decoding
+ ///
+ class DataTypes
+ {
///
- /// Handle data types encoding / decoding
+ /// Protocol version for adjusting data types
///
- class DataTypes
+ private int protocolversion;
+
+ ///
+ /// Initialize a new DataTypes instance
+ ///
+ /// Protocol version
+ public DataTypes(int protocol)
{
- ///
- /// 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, Queue cache)
- {
- byte[] result = new byte[offset];
- for (int i = 0; i < offset; i++)
- result[i] = cache.Dequeue();
- 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(Queue 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(Queue 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(Queue 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(Queue cache)
- {
- byte[] rawValue = ReadData(4, cache);
- Array.Reverse(rawValue); //Endianness
- return BitConverter.ToInt32(rawValue, 0);
- }
-
- ///
- /// Read a long integer from a cache of bytes and remove it from the cache
- ///
- /// The unsigned long integer value
- public long ReadNextLong(Queue cache)
- {
- byte[] rawValue = ReadData(8, cache);
- Array.Reverse(rawValue); //Endianness
- return BitConverter.ToInt64(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(Queue 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(Queue 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(Queue 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, Queue 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(Queue 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(Queue 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(Queue 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(Queue 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(Queue 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(Queue cache)
- {
- string rawData = BitConverter.ToString(cache.ToArray());
- 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 " + rawData);
- 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(Queue 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(Queue cache)
- {
- byte result = cache.Dequeue();
- return result;
- }
-
- ///
- /// Read an uncompressed Named Binary Tag blob and remove it from the cache
- ///
- public Dictionary ReadNextNbt(Queue cache)
- {
- return ReadNextNbt(cache, true);
- }
-
- ///
- /// Read a single item slot from a cache of bytes and remove it from the cache
- ///
- /// The item that was read or NULL for an empty slot
- public Item ReadNextItemSlot(Queue cache)
- {
- List slotData = new List();
- if (protocolversion > Protocol18Handler.MC113Version)
- {
- // MC 1.13 and greater
- bool itemPresent = ReadNextBool(cache);
- if (itemPresent)
- {
- int itemID = ReadNextVarInt(cache);
- byte itemCount = ReadNextByte(cache);
- Dictionary nbt = ReadNextNbt(cache);
- return new Item(itemID, itemCount, nbt);
- }
- else return null;
- }
- else
- {
- // MC 1.12.2 and lower
- short itemID = ReadNextShort(cache);
- if (itemID == -1)
- return null;
- byte itemCount = ReadNextByte(cache);
- short itemDamage = ReadNextShort(cache);
- Dictionary nbt = ReadNextNbt(cache);
- return new Item(itemID, itemCount, nbt);
- }
- }
-
- ///
- /// Read entity information from a cache of bytes and remove it from the cache
- ///
- /// Mappings for converting entity type Ids to EntityType
- /// TRUE for living entities (layout differs)
- /// Entity information
- public Entity ReadNextEntity(Queue cache, EntityPalette entityPalette, bool living)
- {
- int entityID = ReadNextVarInt(cache);
- Guid entityUUID = Guid.Empty;
-
- if (protocolversion > Protocol18Handler.MC18Version)
- {
- entityUUID = ReadNextUUID(cache);
- }
-
- EntityType entityType = entityPalette.FromId(ReadNextVarInt(cache), living);
- Double entityX = ReadNextDouble(cache);
- Double entityY = ReadNextDouble(cache);
- Double entityZ = ReadNextDouble(cache);
- byte entityYaw = ReadNextByte(cache);
- byte entityPitch = ReadNextByte(cache);
-
- if (living)
- {
- byte entityHeadPitch = ReadNextByte(cache);
- }
- else
- {
- int metadata = ReadNextInt(cache);
- }
-
- short velocityX = ReadNextShort(cache);
- short velocityY = ReadNextShort(cache);
- short velocityZ = ReadNextShort(cache);
-
- return new Entity(entityID, entityType, new Location(entityX, entityY, entityZ));
- }
-
- ///
- /// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal)
- ///
- private Dictionary ReadNextNbt(Queue cache, bool root)
- {
- Dictionary nbtData = new Dictionary();
-
- if (root)
- {
- if (cache.Peek() == 0) // TAG_End
- {
- cache.Dequeue();
- return nbtData;
- }
- if (cache.Peek() != 10) // TAG_Compound
- throw new System.IO.InvalidDataException("Failed to decode NBT: Does not start with TAG_Compound");
- ReadNextByte(cache); // Tag type (TAG_Compound)
-
- // NBT root name
- string rootName = Encoding.ASCII.GetString(ReadData(ReadNextUShort(cache), cache));
- if (!String.IsNullOrEmpty(rootName))
- nbtData[""] = rootName;
- }
-
- while (true)
- {
- int fieldType = ReadNextByte(cache);
-
- if (fieldType == 0) // TAG_End
- return nbtData;
-
- int fieldNameLength = ReadNextUShort(cache);
- string fieldName = Encoding.ASCII.GetString(ReadData(fieldNameLength, cache));
- object fieldValue = ReadNbtField(cache, fieldType);
-
- // This will override previous tags with the same name
- nbtData[fieldName] = fieldValue;
- }
- }
-
- ///
- /// Read a single Named Binary Tag field of the specified type and remove it from the cache
- ///
- private object ReadNbtField(Queue cache, int fieldType)
- {
- switch (fieldType)
- {
- case 1: // TAG_Byte
- return ReadNextByte(cache);
- case 2: // TAG_Short
- return ReadNextShort(cache);
- case 3: // TAG_Int
- return ReadNextInt(cache);
- case 4: // TAG_Long
- return ReadNextLong(cache);
- case 5: // TAG_Float
- return ReadNextFloat(cache);
- case 6: // TAG_Double
- return ReadNextDouble(cache);
- case 7: // TAG_Byte_Array
- return ReadData(ReadNextInt(cache), cache);
- case 8: // TAG_String
- return Encoding.UTF8.GetString(ReadData(ReadNextUShort(cache), cache));
- case 9: // TAG_List
- int listType = ReadNextByte(cache);
- int listLength = ReadNextInt(cache);
- object[] listItems = new object[listLength];
- for (int i = 0; i < listLength; i++)
- listItems[i] = ReadNbtField(cache, listType);
- return listItems;
- case 10: // TAG_Compound
- return ReadNextNbt(cache, false);
- case 11: // TAG_Int_Array
- listType = 3;
- listLength = ReadNextInt(cache);
- listItems = new object[listLength];
- for (int i = 0; i < listLength; i++)
- listItems[i] = ReadNbtField(cache, listType);
- return listItems;
- case 12: // TAG_Long_Array
- listType = 4;
- listLength = ReadNextInt(cache);
- listItems = new object[listLength];
- for (int i = 0; i < listLength; i++)
- listItems[i] = ReadNbtField(cache, listType);
- return listItems;
- default:
- throw new System.IO.InvalidDataException("Failed to decode NBT: Unknown field type " + fieldType);
- }
- }
-
- ///
- /// Build an uncompressed Named Binary Tag blob for sending over the network
- ///
- /// Dictionary to encode as Nbt
- /// Byte array for this NBT tag
- public byte[] GetNbt(Dictionary nbt)
- {
- return GetNbt(nbt, true);
- }
-
- ///
- /// Build an uncompressed Named Binary Tag blob for sending over the network (internal)
- ///
- /// Dictionary to encode as Nbt
- /// TRUE if starting a new NBT tag, FALSE if processing a nested NBT tag
- /// Byte array for this NBT tag
- private byte[] GetNbt(Dictionary nbt, bool root)
- {
- if (nbt == null || nbt.Count == 0)
- return new byte[] { 0 }; // TAG_End
-
- List bytes = new List();
-
- if (root)
- {
- bytes.Add(10); // TAG_Compound
-
- // NBT root name
- string rootName = null;
- if (nbt.ContainsKey(""))
- rootName = nbt[""] as string;
- if (rootName == null)
- rootName = "";
- bytes.AddRange(GetUShort((ushort)rootName.Length));
- bytes.AddRange(Encoding.ASCII.GetBytes(rootName));
- }
-
- foreach (var item in nbt)
- {
- // Skip NBT root name
- if (item.Key == "" && root)
- continue;
-
- byte fieldType;
- byte[] fieldNameLength = GetUShort((ushort)item.Key.Length);
- byte[] fieldName = Encoding.ASCII.GetBytes(item.Key);
- byte[] fieldData = GetNbtField(item.Value, out fieldType);
- bytes.Add(fieldType);
- bytes.AddRange(fieldNameLength);
- bytes.AddRange(fieldName);
- bytes.AddRange(fieldData);
- }
-
- bytes.Add(0); // TAG_End
- return bytes.ToArray();
- }
-
- ///
- /// Convert a single object into its NBT representation (internal)
- ///
- /// Object to convert
- /// Field type for the passed object
- /// Binary data for the passed object
- private byte[] GetNbtField(object obj, out byte fieldType)
- {
- if (obj is byte)
- {
- fieldType = 1; // TAG_Byte
- return new[] { (byte)obj };
- }
- else if (obj is short)
- {
- fieldType = 2; // TAG_Short
- return GetShort((short)obj);
- }
- else if (obj is int)
- {
- fieldType = 3; // TAG_Int
- return GetInt((int)obj);
- }
- else if (obj is long)
- {
- fieldType = 4; // TAG_Long
- return GetLong((long)obj);
- }
- else if (obj is float)
- {
- fieldType = 5; // TAG_Float
- return GetFloat((float)obj);
- }
- else if (obj is double)
- {
- fieldType = 6; // TAG_Double
- return GetDouble((double)obj);
- }
- else if (obj is byte[])
- {
- fieldType = 7; // TAG_Byte_Array
- return (byte[])obj;
- }
- else if (obj is string)
- {
- fieldType = 8; // TAG_String
- byte[] stringBytes = Encoding.UTF8.GetBytes((string)obj);
- return ConcatBytes(GetUShort((ushort)stringBytes.Length), stringBytes);
- }
- else if (obj is object[])
- {
- fieldType = 9; // TAG_List
-
- List list = new List((object[])obj);
- int arrayLengthTotal = list.Count;
-
- // Treat empty list as TAG_Byte, length 0
- if (arrayLengthTotal == 0)
- return ConcatBytes(new[] { (byte)1 }, GetInt(0));
-
- // Encode first list item, retain its type
- byte firstItemType;
- string firstItemTypeString = list[0].GetType().Name;
- byte[] firstItemBytes = GetNbtField(list[0], out firstItemType);
- list.RemoveAt(0);
-
- // Encode further list items, check they have the same type
- byte subsequentItemType;
- List subsequentItemsBytes = new List();
- foreach (object item in list)
- {
- subsequentItemsBytes.AddRange(GetNbtField(item, out subsequentItemType));
- if (subsequentItemType != firstItemType)
- throw new System.IO.InvalidDataException(
- "GetNbt: Cannot encode object[] list with mixed types: " + firstItemTypeString + ", " + item.GetType().Name + " into NBT!");
- }
-
- // Build NBT list: type, length, item array
- return ConcatBytes(new[] { firstItemType }, GetInt(arrayLengthTotal), firstItemBytes, subsequentItemsBytes.ToArray());
- }
- else if (obj is Dictionary)
- {
- fieldType = 10; // TAG_Compound
- return GetNbt((Dictionary)obj, false);
- }
- else if (obj is int[])
- {
- fieldType = 11; // TAG_Int_Array
-
- int[] srcIntList = (int[])obj;
- List encIntList = new List();
- encIntList.AddRange(GetInt(srcIntList.Length));
- foreach (int item in srcIntList)
- encIntList.AddRange(GetInt(item));
- return encIntList.ToArray();
- }
- else if (obj is long[])
- {
- fieldType = 12; // TAG_Long_Array
-
- long[] srcLongList = (long[])obj;
- List encLongList = new List();
- encLongList.AddRange(GetInt(srcLongList.Length));
- foreach (long item in srcLongList)
- encLongList.AddRange(GetLong(item));
- return encLongList.ToArray();
- }
- else
- {
- throw new System.IO.InvalidDataException("GetNbt: Cannot encode data type " + obj.GetType().Name + " into NBT!");
- }
- }
-
- ///
- /// 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 long integer
- ///
- /// Long to process
- /// Array ready to send
- public byte[] GetLong(long number)
- {
- byte[] theLong = BitConverter.GetBytes(number);
- Array.Reverse(theLong);
- return theLong;
- }
-
- ///
- /// Get byte array representing an integer
- ///
- /// Integer to process
- /// Array ready to send
- public byte[] GetInt(int number)
- {
- byte[] theInt = BitConverter.GetBytes(number);
- Array.Reverse(theInt);
- return theInt;
- }
-
- ///
- /// Get byte array representing a short
- ///
- /// Short to process
- /// Array ready to send
- public byte[] GetShort(short number)
- {
- byte[] theShort = BitConverter.GetBytes(number);
- Array.Reverse(theShort);
- return theShort;
- }
-
- ///
- /// Get byte array representing an unsigned short
- ///
- /// Short to process
- /// Array ready to send
- public byte[] GetUShort(ushort number)
- {
- byte[] theShort = BitConverter.GetBytes(number);
- Array.Reverse(theShort);
- return theShort;
- }
-
- ///
- /// 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); //Endianness
- 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 long
- ///
- ///
- /// 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)
- {
- byte[] locationBytes;
- if (protocolversion >= Protocol18Handler.MC114Version)
- {
- locationBytes = BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Z) & 0x3FFFFFF) << 12) | (((ulong)location.Y) & 0xFFF));
- }
- else locationBytes = BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Y) & 0xFFF) << 26) | (((ulong)location.Z) & 0x3FFFFFF));
- Array.Reverse(locationBytes); //Endianness
- return locationBytes;
- }
-
- ///
- /// Get a byte array representing the given item as an item slot
- ///
- /// Item
- /// Item slot representation
- public byte[] GetItemSlot(Item item)
- {
- List slotData = new List();
- if (protocolversion > Protocol18Handler.MC113Version)
- {
- // MC 1.13 and greater
- if (item == null || item.IsEmpty)
- slotData.Add(0); // No item
- else
- {
- slotData.Add(1); // Item is present
- slotData.AddRange(GetVarInt((int)item.Type));
- slotData.Add((byte)item.Count);
- slotData.AddRange(GetNbt(item.NBT));
- }
- }
- else
- {
- // MC 1.12.2 and lower
- if (item == null || item.IsEmpty)
- slotData.AddRange(GetShort(-1));
- else
- {
- slotData.AddRange(GetShort((short)item.Type));
- slotData.Add((byte)item.Count);
- slotData.AddRange(GetNbt(item.NBT));
- }
- }
- return slotData.ToArray();
- }
-
- ///
- /// Get protocol block face from Direction
- ///
- /// Direction
- /// Block face byte enum
- public byte GetBlockFace(Direction direction)
- {
- switch (direction)
- {
- case Direction.Down: return 0;
- case Direction.Up: return 1;
- case Direction.North: return 2;
- case Direction.South: return 3;
- case Direction.West: return 4;
- case Direction.East: return 5;
- default: throw new NotImplementedException("Unknown direction: " + direction.ToString());
- }
- }
-
- ///
- /// 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()));
- }
+ 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, Queue cache)
+ {
+ byte[] result = new byte[offset];
+ for (int i = 0; i < offset; i++)
+ result[i] = cache.Dequeue();
+ 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(Queue 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(Queue 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(Queue 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(Queue cache)
+ {
+ byte[] rawValue = ReadData(4, cache);
+ Array.Reverse(rawValue); //Endianness
+ return BitConverter.ToInt32(rawValue, 0);
+ }
+
+ ///
+ /// Read a long integer from a cache of bytes and remove it from the cache
+ ///
+ /// The unsigned long integer value
+ public long ReadNextLong(Queue cache)
+ {
+ byte[] rawValue = ReadData(8, cache);
+ Array.Reverse(rawValue); //Endianness
+ return BitConverter.ToInt64(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(Queue 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(Queue 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(Queue 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, Queue 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(Queue 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(Queue 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(Queue 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(Queue 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(Queue 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(Queue cache)
+ {
+ string rawData = BitConverter.ToString(cache.ToArray());
+ 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 " + rawData);
+ 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[] ReadNextVarIntArray(Queue cache)
+ {
+ string rawData = BitConverter.ToString(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 " + rawData);
+ 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(Queue 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(Queue cache)
+ {
+ byte result = cache.Dequeue();
+ return result;
+ }
+
+ ///
+ /// Read an uncompressed Named Binary Tag blob and remove it from the cache
+ ///
+ public Dictionary ReadNextNbt(Queue cache)
+ {
+ return ReadNextNbt(cache, true);
+ }
+
+ ///
+ /// Read a single item slot from a cache of bytes and remove it from the cache
+ ///
+ /// The item that was read or NULL for an empty slot
+ public Item ReadNextItemSlot(Queue cache)
+ {
+ List slotData = new List();
+ if (protocolversion > Protocol18Handler.MC113Version)
+ {
+ // MC 1.13 and greater
+ bool itemPresent = ReadNextBool(cache);
+ if (itemPresent)
+ {
+ int itemID = ReadNextVarInt(cache);
+ byte itemCount = ReadNextByte(cache);
+ Dictionary nbt = ReadNextNbt(cache);
+ return new Item(itemID, itemCount, nbt);
+ }
+ else return null;
+ }
+ else
+ {
+ // MC 1.12.2 and lower
+ short itemID = ReadNextShort(cache);
+ if (itemID == -1)
+ return null;
+ byte itemCount = ReadNextByte(cache);
+ short itemDamage = ReadNextShort(cache);
+ Dictionary nbt = ReadNextNbt(cache);
+ return new Item(itemID, itemCount, nbt);
+ }
+ }
+
+ ///
+ /// Read entity information from a cache of bytes and remove it from the cache
+ ///
+ /// Mappings for converting entity type Ids to EntityType
+ /// TRUE for living entities (layout differs)
+ /// Entity information
+ public Entity ReadNextEntity(Queue cache, EntityPalette entityPalette, bool living)
+ {
+ int entityID = ReadNextVarInt(cache);
+ Guid entityUUID = Guid.Empty;
+
+ if (protocolversion > Protocol18Handler.MC18Version)
+ {
+ entityUUID = ReadNextUUID(cache);
+ }
+
+ EntityType entityType = entityPalette.FromId(ReadNextVarInt(cache), living);
+ Double entityX = ReadNextDouble(cache);
+ Double entityY = ReadNextDouble(cache);
+ Double entityZ = ReadNextDouble(cache);
+ byte entityYaw = ReadNextByte(cache);
+ byte entityPitch = ReadNextByte(cache);
+
+ if (living)
+ {
+ byte entityHeadPitch = ReadNextByte(cache);
+ }
+ else
+ {
+ int metadata = ReadNextInt(cache);
+ }
+
+ short velocityX = ReadNextShort(cache);
+ short velocityY = ReadNextShort(cache);
+ short velocityZ = ReadNextShort(cache);
+
+ return new Entity(entityID, entityType, new Location(entityX, entityY, entityZ));
+ }
+
+ ///
+ /// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal)
+ ///
+ private Dictionary ReadNextNbt(Queue cache, bool root)
+ {
+ Dictionary nbtData = new Dictionary();
+
+ if (root)
+ {
+ if (cache.Peek() == 0) // TAG_End
+ {
+ cache.Dequeue();
+ return nbtData;
+ }
+ if (cache.Peek() != 10) // TAG_Compound
+ throw new System.IO.InvalidDataException("Failed to decode NBT: Does not start with TAG_Compound");
+ ReadNextByte(cache); // Tag type (TAG_Compound)
+
+ // NBT root name
+ string rootName = Encoding.ASCII.GetString(ReadData(ReadNextUShort(cache), cache));
+ if (!String.IsNullOrEmpty(rootName))
+ nbtData[""] = rootName;
+ }
+
+ while (true)
+ {
+ int fieldType = ReadNextByte(cache);
+
+ if (fieldType == 0) // TAG_End
+ return nbtData;
+
+ int fieldNameLength = ReadNextUShort(cache);
+ string fieldName = Encoding.ASCII.GetString(ReadData(fieldNameLength, cache));
+ object fieldValue = ReadNbtField(cache, fieldType);
+
+ // This will override previous tags with the same name
+ nbtData[fieldName] = fieldValue;
+ }
+ }
+
+ ///
+ /// Read a single Named Binary Tag field of the specified type and remove it from the cache
+ ///
+ private object ReadNbtField(Queue cache, int fieldType)
+ {
+ switch (fieldType)
+ {
+ case 1: // TAG_Byte
+ return ReadNextByte(cache);
+ case 2: // TAG_Short
+ return ReadNextShort(cache);
+ case 3: // TAG_Int
+ return ReadNextInt(cache);
+ case 4: // TAG_Long
+ return ReadNextLong(cache);
+ case 5: // TAG_Float
+ return ReadNextFloat(cache);
+ case 6: // TAG_Double
+ return ReadNextDouble(cache);
+ case 7: // TAG_Byte_Array
+ return ReadData(ReadNextInt(cache), cache);
+ case 8: // TAG_String
+ return Encoding.UTF8.GetString(ReadData(ReadNextUShort(cache), cache));
+ case 9: // TAG_List
+ int listType = ReadNextByte(cache);
+ int listLength = ReadNextInt(cache);
+ object[] listItems = new object[listLength];
+ for (int i = 0; i < listLength; i++)
+ listItems[i] = ReadNbtField(cache, listType);
+ return listItems;
+ case 10: // TAG_Compound
+ return ReadNextNbt(cache, false);
+ case 11: // TAG_Int_Array
+ listType = 3;
+ listLength = ReadNextInt(cache);
+ listItems = new object[listLength];
+ for (int i = 0; i < listLength; i++)
+ listItems[i] = ReadNbtField(cache, listType);
+ return listItems;
+ case 12: // TAG_Long_Array
+ listType = 4;
+ listLength = ReadNextInt(cache);
+ listItems = new object[listLength];
+ for (int i = 0; i < listLength; i++)
+ listItems[i] = ReadNbtField(cache, listType);
+ return listItems;
+ default:
+ throw new System.IO.InvalidDataException("Failed to decode NBT: Unknown field type " + fieldType);
+ }
+ }
+
+ ///
+ /// Build an uncompressed Named Binary Tag blob for sending over the network
+ ///
+ /// Dictionary to encode as Nbt
+ /// Byte array for this NBT tag
+ public byte[] GetNbt(Dictionary nbt)
+ {
+ return GetNbt(nbt, true);
+ }
+
+ ///
+ /// Build an uncompressed Named Binary Tag blob for sending over the network (internal)
+ ///
+ /// Dictionary to encode as Nbt
+ /// TRUE if starting a new NBT tag, FALSE if processing a nested NBT tag
+ /// Byte array for this NBT tag
+ private byte[] GetNbt(Dictionary nbt, bool root)
+ {
+ if (nbt == null || nbt.Count == 0)
+ return new byte[] { 0 }; // TAG_End
+
+ List bytes = new List();
+
+ if (root)
+ {
+ bytes.Add(10); // TAG_Compound
+
+ // NBT root name
+ string rootName = null;
+ if (nbt.ContainsKey(""))
+ rootName = nbt[""] as string;
+ if (rootName == null)
+ rootName = "";
+ bytes.AddRange(GetUShort((ushort)rootName.Length));
+ bytes.AddRange(Encoding.ASCII.GetBytes(rootName));
+ }
+
+ foreach (var item in nbt)
+ {
+ // Skip NBT root name
+ if (item.Key == "" && root)
+ continue;
+
+ byte fieldType;
+ byte[] fieldNameLength = GetUShort((ushort)item.Key.Length);
+ byte[] fieldName = Encoding.ASCII.GetBytes(item.Key);
+ byte[] fieldData = GetNbtField(item.Value, out fieldType);
+ bytes.Add(fieldType);
+ bytes.AddRange(fieldNameLength);
+ bytes.AddRange(fieldName);
+ bytes.AddRange(fieldData);
+ }
+
+ bytes.Add(0); // TAG_End
+ return bytes.ToArray();
+ }
+
+ ///
+ /// Convert a single object into its NBT representation (internal)
+ ///
+ /// Object to convert
+ /// Field type for the passed object
+ /// Binary data for the passed object
+ private byte[] GetNbtField(object obj, out byte fieldType)
+ {
+ if (obj is byte)
+ {
+ fieldType = 1; // TAG_Byte
+ return new[] { (byte)obj };
+ }
+ else if (obj is short)
+ {
+ fieldType = 2; // TAG_Short
+ return GetShort((short)obj);
+ }
+ else if (obj is int)
+ {
+ fieldType = 3; // TAG_Int
+ return GetInt((int)obj);
+ }
+ else if (obj is long)
+ {
+ fieldType = 4; // TAG_Long
+ return GetLong((long)obj);
+ }
+ else if (obj is float)
+ {
+ fieldType = 5; // TAG_Float
+ return GetFloat((float)obj);
+ }
+ else if (obj is double)
+ {
+ fieldType = 6; // TAG_Double
+ return GetDouble((double)obj);
+ }
+ else if (obj is byte[])
+ {
+ fieldType = 7; // TAG_Byte_Array
+ return (byte[])obj;
+ }
+ else if (obj is string)
+ {
+ fieldType = 8; // TAG_String
+ byte[] stringBytes = Encoding.UTF8.GetBytes((string)obj);
+ return ConcatBytes(GetUShort((ushort)stringBytes.Length), stringBytes);
+ }
+ else if (obj is object[])
+ {
+ fieldType = 9; // TAG_List
+
+ List list = new List((object[])obj);
+ int arrayLengthTotal = list.Count;
+
+ // Treat empty list as TAG_Byte, length 0
+ if (arrayLengthTotal == 0)
+ return ConcatBytes(new[] { (byte)1 }, GetInt(0));
+
+ // Encode first list item, retain its type
+ byte firstItemType;
+ string firstItemTypeString = list[0].GetType().Name;
+ byte[] firstItemBytes = GetNbtField(list[0], out firstItemType);
+ list.RemoveAt(0);
+
+ // Encode further list items, check they have the same type
+ byte subsequentItemType;
+ List subsequentItemsBytes = new List();
+ foreach (object item in list)
+ {
+ subsequentItemsBytes.AddRange(GetNbtField(item, out subsequentItemType));
+ if (subsequentItemType != firstItemType)
+ throw new System.IO.InvalidDataException(
+ "GetNbt: Cannot encode object[] list with mixed types: " + firstItemTypeString + ", " + item.GetType().Name + " into NBT!");
+ }
+
+ // Build NBT list: type, length, item array
+ return ConcatBytes(new[] { firstItemType }, GetInt(arrayLengthTotal), firstItemBytes, subsequentItemsBytes.ToArray());
+ }
+ else if (obj is Dictionary)
+ {
+ fieldType = 10; // TAG_Compound
+ return GetNbt((Dictionary)obj, false);
+ }
+ else if (obj is int[])
+ {
+ fieldType = 11; // TAG_Int_Array
+
+ int[] srcIntList = (int[])obj;
+ List encIntList = new List();
+ encIntList.AddRange(GetInt(srcIntList.Length));
+ foreach (int item in srcIntList)
+ encIntList.AddRange(GetInt(item));
+ return encIntList.ToArray();
+ }
+ else if (obj is long[])
+ {
+ fieldType = 12; // TAG_Long_Array
+
+ long[] srcLongList = (long[])obj;
+ List encLongList = new List();
+ encLongList.AddRange(GetInt(srcLongList.Length));
+ foreach (long item in srcLongList)
+ encLongList.AddRange(GetLong(item));
+ return encLongList.ToArray();
+ }
+ else
+ {
+ throw new System.IO.InvalidDataException("GetNbt: Cannot encode data type " + obj.GetType().Name + " into NBT!");
+ }
+ }
+
+ ///
+ /// 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 long integer
+ ///
+ /// Long to process
+ /// Array ready to send
+ public byte[] GetLong(long number)
+ {
+ byte[] theLong = BitConverter.GetBytes(number);
+ Array.Reverse(theLong);
+ return theLong;
+ }
+
+ ///
+ /// Get byte array representing an integer
+ ///
+ /// Integer to process
+ /// Array ready to send
+ public byte[] GetInt(int number)
+ {
+ byte[] theInt = BitConverter.GetBytes(number);
+ Array.Reverse(theInt);
+ return theInt;
+ }
+
+ ///
+ /// Get byte array representing a short
+ ///
+ /// Short to process
+ /// Array ready to send
+ public byte[] GetShort(short number)
+ {
+ byte[] theShort = BitConverter.GetBytes(number);
+ Array.Reverse(theShort);
+ return theShort;
+ }
+
+ ///
+ /// Get byte array representing an unsigned short
+ ///
+ /// Short to process
+ /// Array ready to send
+ public byte[] GetUShort(ushort number)
+ {
+ byte[] theShort = BitConverter.GetBytes(number);
+ Array.Reverse(theShort);
+ return theShort;
+ }
+
+ ///
+ /// 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); //Endianness
+ 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 long
+ ///
+ ///
+ /// 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)
+ {
+ byte[] locationBytes;
+ if (protocolversion >= Protocol18Handler.MC114Version)
+ {
+ locationBytes = BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Z) & 0x3FFFFFF) << 12) | (((ulong)location.Y) & 0xFFF));
+ }
+ else locationBytes = BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Y) & 0xFFF) << 26) | (((ulong)location.Z) & 0x3FFFFFF));
+ Array.Reverse(locationBytes); //Endianness
+ return locationBytes;
+ }
+
+ ///
+ /// Get a byte array representing the given item as an item slot
+ ///
+ /// Item
+ /// Item slot representation
+ public byte[] GetItemSlot(Item item)
+ {
+ List slotData = new List();
+ if (protocolversion > Protocol18Handler.MC113Version)
+ {
+ // MC 1.13 and greater
+ if (item == null || item.IsEmpty)
+ slotData.Add(0); // No item
+ else
+ {
+ slotData.Add(1); // Item is present
+ slotData.AddRange(GetVarInt((int)item.Type));
+ slotData.Add((byte)item.Count);
+ slotData.AddRange(GetNbt(item.NBT));
+ }
+ }
+ else
+ {
+ // MC 1.12.2 and lower
+ if (item == null || item.IsEmpty)
+ slotData.AddRange(GetShort(-1));
+ else
+ {
+ slotData.AddRange(GetShort((short)item.Type));
+ slotData.Add((byte)item.Count);
+ slotData.AddRange(GetNbt(item.NBT));
+ }
+ }
+ return slotData.ToArray();
+ }
+
+ ///
+ /// Get protocol block face from Direction
+ ///
+ /// Direction
+ /// Block face byte enum
+ public byte GetBlockFace(Direction direction)
+ {
+ switch (direction)
+ {
+ case Direction.Down: return 0;
+ case Direction.Up: return 1;
+ case Direction.North: return 2;
+ case Direction.South: return 3;
+ case Direction.West: return 4;
+ case Direction.East: return 5;
+ default: throw new NotImplementedException("Unknown direction: " + direction.ToString());
+ }
+ }
+
+ ///
+ /// 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/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs
index f4a043ba..d9909966 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs
@@ -15,1585 +15,1618 @@ using MinecraftClient.Inventory;
namespace MinecraftClient.Protocol.Handlers
{
- ///
- /// Implementation for Minecraft 1.7.X+ Protocols
- ///
- ///
- /// 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() inside Protocol18PacketTypes.cs
- /// - Also see Material.cs and ItemType.cs for updating block and item data inside MCC
- ///
- class Protocol18Handler : IMinecraftCom
+ ///
+ /// Implementation for Minecraft 1.7.X+ Protocols
+ ///
+ ///
+ /// 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() inside Protocol18PacketTypes.cs
+ /// - Also see Material.cs and ItemType.cs for updating block and item data inside MCC
+ ///
+ class Protocol18Handler : IMinecraftCom
+ {
+ 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 MC1122Version = 340;
+ internal const int MC113Version = 393;
+ internal const int MC114Version = 477;
+ internal const int MC115Version = 573;
+ internal const int MC1152Version = 578;
+ internal const int MC116Version = 735;
+ internal const int MC1161Version = 736;
+
+ private int compression_treshold = 0;
+ private bool autocomplete_received = false;
+ private int autocomplete_transaction_id = 0;
+ private readonly List autocomplete_result = new List();
+ private readonly Dictionary window_actions = new Dictionary();
+ private bool login_phase = true;
+ private int protocolversion;
+ private int currentDimension;
+
+ Protocol18Forge pForge;
+ Protocol18Terrain pTerrain;
+ IMinecraftComHandler handler;
+ EntityPalette entityPalette;
+ SocketWrapper socketWrapper;
+ DataTypes dataTypes;
+ Thread netRead;
+
+ public Protocol18Handler(TcpClient Client, int protocolVersion, IMinecraftComHandler handler, ForgeInfo forgeInfo)
{
- 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 MC1122Version = 340;
- internal const int MC113Version = 393;
- internal const int MC114Version = 477;
- internal const int MC115Version = 573;
- internal const int MC1152Version = 578;
- internal const int MC116Version = 735;
- internal const int MC1161Version = 736;
+ ConsoleIO.SetAutoCompleteEngine(this);
+ ChatParser.InitTranslations();
+ 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);
+ this.pTerrain = new Protocol18Terrain(protocolVersion, dataTypes, handler);
- private int compression_treshold = 0;
- private bool autocomplete_received = false;
- private int autocomplete_transaction_id = 0;
- private readonly List autocomplete_result = new List();
- private readonly Dictionary window_actions = new Dictionary();
- private bool login_phase = true;
- private int protocolversion;
- private int currentDimension;
+ if (handler.GetTerrainEnabled() && protocolversion > MC1152Version)
+ {
+ ConsoleIO.WriteLineFormatted("§8Terrain & Movements currently not handled for that MC version.");
+ handler.SetTerrainEnabled(false);
+ }
- Protocol18Forge pForge;
- Protocol18Terrain pTerrain;
- IMinecraftComHandler handler;
- EntityPalette entityPalette;
- SocketWrapper socketWrapper;
- DataTypes dataTypes;
- Thread netRead;
+ if (handler.GetInventoryEnabled() && (protocolversion < MC110Version || protocolversion > MC1152Version))
+ {
+ ConsoleIO.WriteLineFormatted("§8Inventories are currently not handled for that MC version.");
+ handler.SetInventoryEnabled(false);
+ }
- public Protocol18Handler(TcpClient Client, int protocolVersion, IMinecraftComHandler handler, ForgeInfo forgeInfo)
+ if (handler.GetEntityHandlingEnabled() && (protocolversion <= MC1122Version || protocolversion > MC1152Version))
+ {
+ ConsoleIO.WriteLineFormatted("§8Entities are currently not handled for that MC version.");
+ handler.SetEntityHandlingEnabled(false);
+ }
+
+ if (protocolversion >= MC113Version)
+ {
+ if (protocolVersion > MC1152Version && handler.GetTerrainEnabled())
+ throw new NotImplementedException("Please update block types handling for this Minecraft version. See Material.cs");
+ if (protocolVersion >= MC115Version)
+ Block.Palette = new Palette115();
+ else if (protocolVersion >= MC114Version)
+ Block.Palette = new Palette114();
+ else Block.Palette = new Palette113();
+ }
+ else Block.Palette = new Palette112();
+
+ if (protocolversion >= MC114Version)
+ {
+ if (protocolversion > MC1152Version && handler.GetEntityHandlingEnabled())
+ throw new NotImplementedException("Please update entity types handling for this Minecraft version. See EntityType.cs");
+ if (protocolversion >= MC115Version)
+ entityPalette = new EntityPalette115();
+ else entityPalette = new EntityPalette114();
+ }
+ else entityPalette = new EntityPalette113();
+ }
+
+ ///
+ /// Separate thread. Network reading loop.
+ ///
+ private void Updater()
+ {
+ try
+ {
+ do
{
- ConsoleIO.SetAutoCompleteEngine(this);
- ChatParser.InitTranslations();
- 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);
- this.pTerrain = new Protocol18Terrain(protocolVersion, dataTypes, handler);
-
- if (handler.GetTerrainEnabled() && protocolversion > MC1152Version)
- {
- ConsoleIO.WriteLineFormatted("§8Terrain & Movements currently not handled for that MC version.");
- handler.SetTerrainEnabled(false);
- }
-
- if (handler.GetInventoryEnabled() && (protocolversion < MC110Version || protocolversion > MC1152Version))
- {
- ConsoleIO.WriteLineFormatted("§8Inventories are currently not handled for that MC version.");
- handler.SetInventoryEnabled(false);
- }
-
- if (handler.GetEntityHandlingEnabled() && (protocolversion <= MC1122Version || protocolversion > MC1152Version))
- {
- ConsoleIO.WriteLineFormatted("§8Entities are currently not handled for that MC version.");
- handler.SetEntityHandlingEnabled(false);
- }
-
- if (protocolversion >= MC113Version)
- {
- if (protocolVersion > MC1152Version && handler.GetTerrainEnabled())
- throw new NotImplementedException("Please update block types handling for this Minecraft version. See Material.cs");
- if (protocolVersion >= MC115Version)
- Block.Palette = new Palette115();
- else if (protocolVersion >= MC114Version)
- Block.Palette = new Palette114();
- else Block.Palette = new Palette113();
- }
- else Block.Palette = new Palette112();
-
- if (protocolversion >= MC114Version)
- {
- if (protocolversion > MC1152Version && handler.GetEntityHandlingEnabled())
- throw new NotImplementedException("Please update entity types handling for this Minecraft version. See EntityType.cs");
- if (protocolversion >= MC115Version)
- entityPalette = new EntityPalette115();
- else entityPalette = new EntityPalette114();
- }
- else entityPalette = new EntityPalette113();
+ Thread.Sleep(100);
}
+ while (Update());
+ }
+ catch (System.IO.IOException) { }
+ catch (SocketException) { }
+ catch (ObjectDisposedException) { }
- ///
- /// Separate thread. Network reading loop.
- ///
- private void Updater()
+ handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, "");
+ }
+
+ ///
+ /// Read data from the network. Should be called on a separate thread.
+ ///
+ /// FALSE if an error occured, TRUE otherwise.
+ private bool Update()
+ {
+ handler.OnUpdate();
+ if (!socketWrapper.IsConnected())
+ return false;
+ try
+ {
+ while (socketWrapper.HasDataAvailable())
{
- try
- {
- do
+ int packetID = 0;
+ Queue packetData = new Queue();
+ ReadNextPacket(ref packetID, packetData);
+ HandlePacket(packetID, new Queue(packetData));
+ }
+ }
+ catch (System.IO.IOException) { return false; }
+ catch (SocketException) { return false; }
+ catch (NullReferenceException) { return false; }
+ return true;
+ }
+
+ ///
+ /// Read the next packet from the network
+ ///
+ /// will contain packet ID
+ /// will contain raw packet Data
+ internal void ReadNextPacket(ref int packetID, Queue packetData)
+ {
+ packetData.Clear();
+ int size = dataTypes.ReadNextVarIntRAW(socketWrapper); //Packet size
+ byte[] rawpacket = socketWrapper.ReadDataRAW(size);//Packet contents
+ for (int i = 0; i < rawpacket.Length; i++)
+ packetData.Enqueue(rawpacket[i]);
+
+ //Handle packet decompression
+ if (protocolversion >= MC18Version
+ && compression_treshold > 0)
+ {
+ int sizeUncompressed = dataTypes.ReadNextVarInt(packetData);
+ if (sizeUncompressed != 0) // != 0 means compressed, let's decompress
+ {
+ byte[] toDecompress = packetData.ToArray();
+ byte[] uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed);
+ packetData.Clear();
+ for (int i = 0; i < uncompressed.Length; i++)
+ packetData.Enqueue(uncompressed[i]);
+ }
+ }
+
+ packetID = dataTypes.ReadNextVarInt(packetData); //Packet ID
+ }
+
+ ///
+ /// Handle the given packet
+ ///
+ /// Packet ID
+ /// Packet contents
+ /// TRUE if the packet was processed, FALSE if ignored or unknown
+ internal bool HandlePacket(int packetID, Queue packetData)
+ {
+ try
+ {
+ if (login_phase)
+ {
+ switch (packetID) //Packet IDs are different while logging in
+ {
+ case 0x03:
+ if (protocolversion >= MC18Version)
+ compression_treshold = dataTypes.ReadNextVarInt(packetData);
+ break;
+ default:
+ return false; //Ignored packet
+ }
+ }
+ // Regular in-game packets
+ else switch (Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion))
+ {
+ case PacketIncomingType.KeepAlive:
+ SendPacket(PacketOutgoingType.KeepAlive, packetData);
+ handler.OnServerKeepAlive();
+ break;
+ case PacketIncomingType.JoinGame:
+ handler.OnGameJoined();
+ int playerEntityID = dataTypes.ReadNextInt(packetData);
+ handler.OnReceivePlayerEntityID(playerEntityID);
+ handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData));
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextByte(packetData); // Previous Gamemode - 1.16 and above
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextVarInt(packetData); // World Count - 1.16 and above
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextVarIntArray(packetData); // World Names - 1.16 and above
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextNbt(packetData); // Dimension Codec - 1.16 and above
+ if (protocolversion >= MC191Version)
+ this.currentDimension = dataTypes.ReadNextInt(packetData);
+ else if (protocolversion >= MC116Version)
+ this.currentDimension = dataTypes.ReadNextString(packetData); //In 1.16 it was changed to "Identifier" which seems to be a string
+ else
+ this.currentDimension = (sbyte)dataTypes.ReadNextByte(packetData);
+ if (protocolversion < MC114Version)
+ dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextString(packetData); // World Name - 1.16 and above
+ if (protocolversion >= MC115Version)
+ dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above
+
+ dataTypes.ReadNextByte(packetData); // Max Players
+
+ if (protocolversion < MC116Version)
+ dataTypes.ReadNextString(packetData); // Level Type - 1.15 and below
+ if (protocolversion >= MC114Version)
+ dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above
+ if (protocolversion >= MC18Version)
+ dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above
+ if (protocolversion >= MC115Version)
+ dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above
+ break;
+ case PacketIncomingType.ChatMessage:
+ string message = dataTypes.ReadNextString(packetData);
+ try
+ {
+ //Hide system messages or xp bar messages?
+ byte messageType = dataTypes.ReadNextByte(packetData);
+ if ((messageType == 1 && !Settings.DisplaySystemMessages)
+ || (messageType == 2 && !Settings.DisplayXPBarMessages))
+ break;
+ }
+ catch (ArgumentOutOfRangeException) { /* No message type */ }
+ handler.OnTextReceived(message, true);
+ break;
+ case PacketIncomingType.Respawn:
+ if (protocolversion >= MC116Version)
+ this.currentDimension = dataTypes.ReadNextString(packetData); //In 1.16 it was changed to "Identifier" which seems to be a string
+ else
+ this.currentDimension = dataTypes.ReadNextInt(packetData);
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextString(packetData); // World Name - 1.16 and above
+ if (protocolversion < MC114Version)
+ dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
+ if (protocolversion >= MC115Version)
+ dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above
+ dataTypes.ReadNextByte(packetData); // Gamemode
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextByte(packetData); // Previous Game mode - 1.16 and above
+ if (protocolversion < MC116Version)
+ dataTypes.ReadNextString(packetData); // Level Type - 1.15 and below
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above
+ if (protocolversion >= MC116Version)
+ dataTypes.ReadNextBool(packetData); // Copy metadata - 1.16 and above
+ handler.OnRespawn();
+ break;
+ case PacketIncomingType.PlayerPositionAndLook:
+ // These always need to be read, since we need the field after them for teleport confirm
+ 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);
+
+ // entity handling require player pos for distance calculating
+ if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled())
+ {
+ if (protocolversion >= MC18Version)
{
- Thread.Sleep(100);
+ Location location = handler.GetCurrentLocation();
+ location.X = (locMask & 1 << 0) != 0 ? location.X + x : x;
+ location.Y = (locMask & 1 << 1) != 0 ? location.Y + y : y;
+ location.Z = (locMask & 1 << 2) != 0 ? location.Z + z : z;
+ handler.UpdateLocation(location, yaw, pitch);
}
- while (Update());
- }
- catch (System.IO.IOException) { }
- catch (SocketException) { }
- catch (ObjectDisposedException) { }
+ else handler.UpdateLocation(new Location(x, y, z), yaw, pitch);
+ }
- handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, "");
- }
-
- ///
- /// Read data from the network. Should be called on a separate thread.
- ///
- /// FALSE if an error occured, TRUE otherwise.
- private bool Update()
- {
- handler.OnUpdate();
- if (!socketWrapper.IsConnected())
- return false;
- try
- {
- while (socketWrapper.HasDataAvailable())
+ if (protocolversion >= MC19Version)
+ {
+ int teleportID = dataTypes.ReadNextVarInt(packetData);
+ // Teleport confirm packet
+ SendPacket(PacketOutgoingType.TeleportConfirm, dataTypes.GetVarInt(teleportID));
+ }
+ break;
+ case PacketIncomingType.ChunkData:
+ if (handler.GetTerrainEnabled())
+ {
+ int chunkX = dataTypes.ReadNextInt(packetData);
+ int chunkZ = dataTypes.ReadNextInt(packetData);
+ bool chunksContinuous = dataTypes.ReadNextBool(packetData);
+ ushort chunkMask = protocolversion >= MC19Version
+ ? (ushort)dataTypes.ReadNextVarInt(packetData)
+ : dataTypes.ReadNextUShort(packetData);
+ if (protocolversion < MC18Version)
{
- int packetID = 0;
- Queue packetData = new Queue();
- ReadNextPacket(ref packetID, packetData);
- HandlePacket(packetID, new Queue(packetData));
- }
- }
- catch (System.IO.IOException) { return false; }
- catch (SocketException) { return false; }
- catch (NullReferenceException) { return false; }
- return true;
- }
-
- ///
- /// Read the next packet from the network
- ///
- /// will contain packet ID
- /// will contain raw packet Data
- internal void ReadNextPacket(ref int packetID, Queue packetData)
- {
- packetData.Clear();
- int size = dataTypes.ReadNextVarIntRAW(socketWrapper); //Packet size
- byte[] rawpacket = socketWrapper.ReadDataRAW(size);//Packet contents
- for (int i = 0; i < rawpacket.Length; i++)
- packetData.Enqueue(rawpacket[i]);
-
- //Handle packet decompression
- if (protocolversion >= MC18Version
- && compression_treshold > 0)
- {
- int sizeUncompressed = dataTypes.ReadNextVarInt(packetData);
- if (sizeUncompressed != 0) // != 0 means compressed, let's decompress
- {
- byte[] toDecompress = packetData.ToArray();
- byte[] uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed);
- packetData.Clear();
- for (int i = 0; i < uncompressed.Length; i++)
- packetData.Enqueue(uncompressed[i]);
- }
- }
-
- packetID = dataTypes.ReadNextVarInt(packetData); //Packet ID
- }
-
- ///
- /// Handle the given packet
- ///
- /// Packet ID
- /// Packet contents
- /// TRUE if the packet was processed, FALSE if ignored or unknown
- internal bool HandlePacket(int packetID, Queue packetData)
- {
- try
- {
- if (login_phase)
- {
- switch (packetID) //Packet IDs are different while logging in
- {
- case 0x03:
- if (protocolversion >= MC18Version)
- compression_treshold = dataTypes.ReadNextVarInt(packetData);
- break;
- default:
- return false; //Ignored packet
- }
- }
- // Regular in-game packets
- else switch (Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion))
- {
- case PacketIncomingType.KeepAlive:
- SendPacket(PacketOutgoingType.KeepAlive, packetData);
- handler.OnServerKeepAlive();
- break;
- case PacketIncomingType.JoinGame:
- handler.OnGameJoined();
- int playerEntityID = dataTypes.ReadNextInt(packetData);
- handler.OnReceivePlayerEntityID(playerEntityID);
- handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData));
- if (protocolversion >= MC191Version)
- this.currentDimension = dataTypes.ReadNextInt(packetData);
- else
- this.currentDimension = (sbyte)dataTypes.ReadNextByte(packetData);
- if (protocolversion < MC114Version)
- dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
- if (protocolversion >= MC115Version)
- dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above
- dataTypes.ReadNextByte(packetData);
- dataTypes.ReadNextString(packetData);
- if (protocolversion >= MC114Version)
- dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above
- if (protocolversion >= MC18Version)
- dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above
- if (protocolversion >= MC115Version)
- dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above
- break;
- case PacketIncomingType.ChatMessage:
- string message = dataTypes.ReadNextString(packetData);
- try
- {
- //Hide system messages or xp bar messages?
- byte messageType = dataTypes.ReadNextByte(packetData);
- if ((messageType == 1 && !Settings.DisplaySystemMessages)
- || (messageType == 2 && !Settings.DisplayXPBarMessages))
- break;
- }
- catch (ArgumentOutOfRangeException) { /* No message type */ }
- handler.OnTextReceived(message, true);
- break;
- case PacketIncomingType.Respawn:
- this.currentDimension = dataTypes.ReadNextInt(packetData);
- if (protocolversion < MC114Version)
- dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
- if (protocolversion >= MC115Version)
- dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above
- dataTypes.ReadNextByte(packetData);
- dataTypes.ReadNextString(packetData);
- handler.OnRespawn();
- break;
- case PacketIncomingType.PlayerPositionAndLook:
- // These always need to be read, since we need the field after them for teleport confirm
- 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);
-
- // entity handling require player pos for distance calculating
- if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled())
- {
- if (protocolversion >= MC18Version)
- {
- Location location = handler.GetCurrentLocation();
- location.X = (locMask & 1 << 0) != 0 ? location.X + x : x;
- location.Y = (locMask & 1 << 1) != 0 ? location.Y + y : y;
- location.Z = (locMask & 1 << 2) != 0 ? location.Z + z : z;
- handler.UpdateLocation(location, yaw, pitch);
- }
- else handler.UpdateLocation(new Location(x, y, z), yaw, pitch);
- }
-
- if (protocolversion >= MC19Version)
- {
- int teleportID = dataTypes.ReadNextVarInt(packetData);
- // Teleport confirm packet
- SendPacket(PacketOutgoingType.TeleportConfirm, dataTypes.GetVarInt(teleportID));
- }
- break;
- case PacketIncomingType.ChunkData:
- if (handler.GetTerrainEnabled())
- {
- int chunkX = dataTypes.ReadNextInt(packetData);
- int chunkZ = dataTypes.ReadNextInt(packetData);
- bool chunksContinuous = dataTypes.ReadNextBool(packetData);
- ushort chunkMask = protocolversion >= MC19Version
- ? (ushort)dataTypes.ReadNextVarInt(packetData)
- : dataTypes.ReadNextUShort(packetData);
- if (protocolversion < MC18Version)
- {
- ushort addBitmap = dataTypes.ReadNextUShort(packetData);
- int compressedDataSize = dataTypes.ReadNextInt(packetData);
- byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData);
- byte[] decompressed = ZlibUtils.Decompress(compressed);
- pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue(decompressed));
- }
- else
- {
- if (protocolversion >= MC114Version)
- dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above
- if (protocolversion >= MC115Version && chunksContinuous)
- dataTypes.ReadData(1024 * 4, packetData); // Biomes - 1.15 and above
- int dataSize = dataTypes.ReadNextVarInt(packetData);
- pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData);
- }
- }
- break;
- case PacketIncomingType.MapData:
- int mapid = dataTypes.ReadNextVarInt(packetData);
- byte scale = dataTypes.ReadNextByte(packetData);
- bool trackingposition = dataTypes.ReadNextBool(packetData);
- bool locked = false;
- if (protocolversion >= MC114Version)
- {
- locked = dataTypes.ReadNextBool(packetData);
- }
- int iconcount = dataTypes.ReadNextVarInt(packetData);
- handler.OnMapData(mapid, scale, trackingposition, locked, iconcount);
- break;
- case PacketIncomingType.Title:
- if (protocolversion >= MC18Version)
- {
- int action2 = dataTypes.ReadNextVarInt(packetData);
- string titletext = String.Empty;
- string subtitletext = String.Empty;
- string actionbartext = String.Empty;
- string json = String.Empty;
- int fadein = -1;
- int stay = -1;
- int fadeout = -1;
- if (protocolversion >= MC110Version)
- {
- if (action2 == 0)
- {
- json = titletext;
- titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
- }
- else if (action2 == 1)
- {
- json = subtitletext;
- subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
- }
- else if (action2 == 2)
- {
- json = actionbartext;
- actionbartext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
- }
- else if (action2 == 3)
- {
- fadein = dataTypes.ReadNextInt(packetData);
- stay = dataTypes.ReadNextInt(packetData);
- fadeout = dataTypes.ReadNextInt(packetData);
- }
- }
- else
- {
- if (action2 == 0)
- {
- json = titletext;
- titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
- }
- else if (action2 == 1)
- {
- json = subtitletext;
- subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
- }
- else if (action2 == 2)
- {
- fadein = dataTypes.ReadNextInt(packetData);
- stay = dataTypes.ReadNextInt(packetData);
- fadeout = dataTypes.ReadNextInt(packetData);
- }
- }
- handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json);
- }
- break;
- case PacketIncomingType.MultiBlockChange:
- if (handler.GetTerrainEnabled())
- {
- int chunkX = dataTypes.ReadNextInt(packetData);
- int chunkZ = dataTypes.ReadNextInt(packetData);
- int recordCount = protocolversion < MC18Version
- ? (int)dataTypes.ReadNextShort(packetData)
- : dataTypes.ReadNextVarInt(packetData);
-
- for (int i = 0; i < recordCount; i++)
- {
- byte locationXZ;
- ushort blockIdMeta;
- int blockY;
-
- if (protocolversion < MC18Version)
- {
- blockIdMeta = dataTypes.ReadNextUShort(packetData);
- blockY = (ushort)dataTypes.ReadNextByte(packetData);
- locationXZ = dataTypes.ReadNextByte(packetData);
- }
- else
- {
- locationXZ = dataTypes.ReadNextByte(packetData);
- blockY = (ushort)dataTypes.ReadNextByte(packetData);
- blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData);
- }
-
- int blockX = locationXZ >> 4;
- int blockZ = locationXZ & 0x0F;
- Block block = new Block(blockIdMeta);
- handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block);
- }
- }
- break;
- case PacketIncomingType.BlockChange:
- if (handler.GetTerrainEnabled())
- {
- if (protocolversion < MC18Version)
- {
- 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(dataTypes.ReadNextLocation(packetData), new Block((ushort)dataTypes.ReadNextVarInt(packetData)));
- }
- break;
- case PacketIncomingType.MapChunkBulk:
- if (protocolversion < MC19Version && handler.GetTerrainEnabled())
- {
- int chunkCount;
- bool hasSkyLight;
- Queue chunkData = packetData;
-
- //Read global fields
- if (protocolversion < MC18Version)
- {
- 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 Queue(decompressed);
- }
- else
- {
- hasSkyLight = dataTypes.ReadNextBool(packetData);
- chunkCount = dataTypes.ReadNextVarInt(packetData);
- }
-
- //Read chunk records
- int[] chunkXs = new int[chunkCount];
- int[] chunkZs = new int[chunkCount];
- ushort[] chunkMasks = new ushort[chunkCount];
- ushort[] addBitmaps = new ushort[chunkCount];
- for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
- {
- chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData);
- chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData);
- chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData);
- addBitmaps[chunkColumnNo] = protocolversion < MC18Version
- ? dataTypes.ReadNextUShort(packetData)
- : (ushort)0;
- }
-
- //Process chunk records
- for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
- pTerrain.ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, currentDimension, chunkData);
- }
- break;
- case PacketIncomingType.UnloadChunk:
- if (protocolversion >= MC19Version && handler.GetTerrainEnabled())
- {
- int chunkX = dataTypes.ReadNextInt(packetData);
- int chunkZ = dataTypes.ReadNextInt(packetData);
- handler.GetWorld()[chunkX, chunkZ] = null;
- }
- break;
- case PacketIncomingType.PlayerListUpdate:
- if (protocolversion >= MC18Version)
- {
- int action = dataTypes.ReadNextVarInt(packetData);
- int numActions = dataTypes.ReadNextVarInt(packetData);
- for (int i = 0; i < numActions; i++)
- {
- Guid uuid = dataTypes.ReadNextUUID(packetData);
- switch (action)
- {
- case 0x00: //Player Join
- string name = dataTypes.ReadNextString(packetData);
- int propNum = dataTypes.ReadNextVarInt(packetData);
- for (int p = 0; p < propNum; p++)
- {
- string key = dataTypes.ReadNextString(packetData);
- string val = dataTypes.ReadNextString(packetData);
- if (dataTypes.ReadNextBool(packetData))
- dataTypes.ReadNextString(packetData);
- }
- handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData));
- dataTypes.ReadNextVarInt(packetData);
- if (dataTypes.ReadNextBool(packetData))
- dataTypes.ReadNextString(packetData);
- handler.OnPlayerJoin(uuid, name);
- break;
- case 0x01: //Update gamemode
- handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData));
- break;
- case 0x02: //Update latency
- int latency = dataTypes.ReadNextVarInt(packetData);
- handler.OnLatencyUpdate(uuid, latency); //Update latency;
- break;
- case 0x03: //Update display name
- if (dataTypes.ReadNextBool(packetData))
- dataTypes.ReadNextString(packetData);
- break;
- case 0x04: //Player Leave
- handler.OnPlayerLeave(uuid);
- break;
- default:
- //Unknown player list item type
- break;
- }
- }
- }
- else //MC 1.7.X does not provide UUID in tab-list updates
- {
- string name = 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);
- else handler.OnPlayerLeave(FakeUUID);
- }
- break;
- case PacketIncomingType.TabCompleteResult:
- if (protocolversion >= MC113Version)
- {
- 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 = dataTypes.ReadNextVarInt(packetData);
- autocomplete_result.Clear();
-
- for (int i = 0; i < autocomplete_count; i++)
- {
- autocomplete_result.Add(dataTypes.ReadNextString(packetData));
- if (protocolversion >= MC113Version)
- {
- // Skip optional tooltip for each tab-complete result
- if (dataTypes.ReadNextBool(packetData))
- dataTypes.ReadNextString(packetData);
- }
- }
-
- autocomplete_received = true;
- break;
- case PacketIncomingType.PluginMessage:
- String channel = dataTypes.ReadNextString(packetData);
- // Length is unneeded as the whole remaining packetData is the entire payload of the packet.
- if (protocolversion < MC18Version)
- pForge.ReadNextVarShort(packetData);
- handler.OnPluginChannelMessage(channel, packetData.ToArray());
- return pForge.HandlePluginMessage(channel, packetData, ref currentDimension);
- case PacketIncomingType.KickPacket:
- handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
- return false;
- case PacketIncomingType.NetworkCompressionTreshold:
- if (protocolversion >= MC18Version && protocolversion < MC19Version)
- compression_treshold = dataTypes.ReadNextVarInt(packetData);
- break;
- case PacketIncomingType.OpenWindow:
- if (handler.GetInventoryEnabled())
- {
- if (protocolversion < MC114Version)
- {
- // MC 1.13 or lower
- byte windowID = dataTypes.ReadNextByte(packetData);
- string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper();
- ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type);
- string title = dataTypes.ReadNextString(packetData);
- byte slots = dataTypes.ReadNextByte(packetData);
- Container inventory = new Container(windowID, inventoryType, ChatParser.ParseText(title));
- handler.OnInventoryOpen(windowID, inventory);
- }
- else
- {
- // MC 1.14 or greater
- int windowID = dataTypes.ReadNextVarInt(packetData);
- int windowType = dataTypes.ReadNextVarInt(packetData);
- string title = dataTypes.ReadNextString(packetData);
- Container inventory = new Container(windowID, windowType, ChatParser.ParseText(title));
- handler.OnInventoryOpen(windowID, inventory);
- }
- }
- break;
- case PacketIncomingType.CloseWindow:
- if (handler.GetInventoryEnabled())
- {
- byte windowID = dataTypes.ReadNextByte(packetData);
- lock (window_actions) { window_actions[windowID] = 0; }
- handler.OnInventoryClose(windowID);
- }
- break;
- case PacketIncomingType.WindowItems:
- if (handler.GetInventoryEnabled())
- {
- byte windowId = dataTypes.ReadNextByte(packetData);
- short elements = dataTypes.ReadNextShort(packetData);
- Dictionary inventorySlots = new Dictionary();
- for (short slotId = 0; slotId < elements; slotId++)
- {
- Item item = dataTypes.ReadNextItemSlot(packetData);
- if (item != null)
- inventorySlots[slotId] = item;
- }
- handler.OnWindowItems(windowId, inventorySlots);
- }
- break;
- case PacketIncomingType.SetSlot:
- if (handler.GetInventoryEnabled())
- {
- byte windowID = dataTypes.ReadNextByte(packetData);
- short slotID = dataTypes.ReadNextShort(packetData);
- Item item = dataTypes.ReadNextItemSlot(packetData);
- handler.OnSetSlot(windowID, slotID, item);
- }
- break;
- case PacketIncomingType.ResourcePackSend:
- string url = dataTypes.ReadNextString(packetData);
- string hash = dataTypes.ReadNextString(packetData);
- // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056)
- if (hash.Length != 40)
- break;
- //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 = 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;
- case PacketIncomingType.SpawnEntity:
- if (handler.GetEntityHandlingEnabled())
- {
- Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false);
- handler.OnSpawnEntity(entity);
- }
- break;
- case PacketIncomingType.EntityEquipment:
- if (handler.GetEntityHandlingEnabled())
- {
- int entityid = dataTypes.ReadNextVarInt(packetData);
- int slot2 = dataTypes.ReadNextVarInt(packetData);
- Item item = dataTypes.ReadNextItemSlot(packetData);
- handler.OnEntityEquipment(entityid, slot2, item);
- }
- break;
- case PacketIncomingType.SpawnLivingEntity:
- if (handler.GetEntityHandlingEnabled())
- {
- Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, true);
- // packet before 1.15 has metadata at the end
- // this is not handled in dataTypes.ReadNextEntity()
- // we are simply ignoring leftover data in packet
- handler.OnSpawnEntity(entity);
- }
- break;
- case PacketIncomingType.SpawnPlayer:
- if (handler.GetEntityHandlingEnabled())
- {
- int EntityID = dataTypes.ReadNextVarInt(packetData);
- Guid UUID = dataTypes.ReadNextUUID(packetData);
- double X = dataTypes.ReadNextDouble(packetData);
- double Y = dataTypes.ReadNextDouble(packetData);
- double Z = dataTypes.ReadNextDouble(packetData);
- byte Yaw = dataTypes.ReadNextByte(packetData);
- byte Pitch = dataTypes.ReadNextByte(packetData);
-
- Location EntityLocation = new Location(X, Y, Z);
-
- handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch);
- }
- break;
- case PacketIncomingType.DestroyEntities:
- if (handler.GetEntityHandlingEnabled())
- {
- int EntityCount = dataTypes.ReadNextVarInt(packetData);
- int[] EntitiesList = new int[EntityCount];
- for (int i = 0; i < EntityCount; i++)
- {
- EntitiesList[i] = dataTypes.ReadNextVarInt(packetData);
- }
- handler.OnDestroyEntities(EntitiesList);
- }
- break;
- case PacketIncomingType.EntityPosition:
- if (handler.GetEntityHandlingEnabled())
- {
- int EntityID = dataTypes.ReadNextVarInt(packetData);
- Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
- Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
- Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
- bool OnGround = dataTypes.ReadNextBool(packetData);
- DeltaX = DeltaX / (128 * 32);
- DeltaY = DeltaY / (128 * 32);
- DeltaZ = DeltaZ / (128 * 32);
- handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
- }
- break;
- case PacketIncomingType.EntityPositionAndRotation:
- if (handler.GetEntityHandlingEnabled())
- {
- int EntityID = dataTypes.ReadNextVarInt(packetData);
- Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
- Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
- Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
- byte _yaw = dataTypes.ReadNextByte(packetData);
- byte _pitch = dataTypes.ReadNextByte(packetData);
- bool OnGround = dataTypes.ReadNextBool(packetData);
- DeltaX = DeltaX / (128 * 32);
- DeltaY = DeltaY / (128 * 32);
- DeltaZ = DeltaZ / (128 * 32);
- handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
- }
- break;
- case PacketIncomingType.EntityProperties:
- if (handler.GetEntityHandlingEnabled())
- {
- int EntityID = dataTypes.ReadNextVarInt(packetData);
- int NumberOfProperties = dataTypes.ReadNextInt(packetData);
- Dictionary keys = new Dictionary();
- for (int i = 0; i < NumberOfProperties; i++)
- {
- string _key = dataTypes.ReadNextString(packetData);
- Double _value = dataTypes.ReadNextDouble(packetData);
-
- List op0 = new List();
- List op1 = new List();
- List op2 = new List();
- int NumberOfModifiers = dataTypes.ReadNextVarInt(packetData);
- for (int j = 0; j < NumberOfModifiers; j++)
- {
- dataTypes.ReadNextUUID(packetData);
- Double amount = dataTypes.ReadNextDouble(packetData);
- byte operation = dataTypes.ReadNextByte(packetData);
- switch (operation)
- {
- case 0: op0.Add(amount); break;
- case 1: op1.Add(amount); break;
- case 2: op2.Add(amount + 1); break;
- }
- }
- if (op0.Count > 0) _value += op0.Sum();
- if (op1.Count > 0) _value *= 1 + op1.Sum();
- if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x);
- keys.Add(_key, _value);
- }
- handler.OnEntityProperties(EntityID, keys);
- }
- break;
- case PacketIncomingType.TimeUpdate:
- long WorldAge = dataTypes.ReadNextLong(packetData);
- long TimeOfday = dataTypes.ReadNextLong(packetData);
- handler.OnTimeUpdate(WorldAge, TimeOfday);
- break;
- case PacketIncomingType.EntityTeleport:
- if (handler.GetEntityHandlingEnabled())
- {
- int EntityID = dataTypes.ReadNextVarInt(packetData);
- Double X = dataTypes.ReadNextDouble(packetData);
- Double Y = dataTypes.ReadNextDouble(packetData);
- Double Z = dataTypes.ReadNextDouble(packetData);
- byte EntityYaw = dataTypes.ReadNextByte(packetData);
- byte EntityPitch = dataTypes.ReadNextByte(packetData);
- bool OnGround = dataTypes.ReadNextBool(packetData);
- handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround);
- }
- break;
- case PacketIncomingType.UpdateHealth:
- float health = dataTypes.ReadNextFloat(packetData);
- int food;
- if (protocolversion >= MC18Version)
- food = dataTypes.ReadNextVarInt(packetData);
- else
- food = dataTypes.ReadNextShort(packetData);
- dataTypes.ReadNextFloat(packetData); // Food Saturation
- handler.OnUpdateHealth(health, food);
- break;
- case PacketIncomingType.SetExperience:
- float experiencebar = dataTypes.ReadNextFloat(packetData);
- int level = dataTypes.ReadNextVarInt(packetData);
- int totalexperience = dataTypes.ReadNextVarInt(packetData);
- handler.OnSetExperience(experiencebar, level, totalexperience);
- break;
- case PacketIncomingType.Explosion:
- Location explosionLocation = new Location(dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData));
- float explosionStrength = dataTypes.ReadNextFloat(packetData);
- int explosionBlockCount = dataTypes.ReadNextInt(packetData);
- // Ignoring additional fields (records, pushback)
- handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount);
- break;
- case PacketIncomingType.HeldItemChange:
- byte slot = dataTypes.ReadNextByte(packetData);
- handler.OnHeldItemChange(slot);
- break;
- default:
- return false; //Ignored packet
- }
- return true; //Packet processed
- }
- catch (Exception innerException)
- {
- if (innerException is ThreadAbortException || innerException is SocketException || innerException.InnerException is SocketException)
- throw; //Thread abort or Connection lost rather than invalid data
- throw new System.IO.InvalidDataException(
- String.Format("Failed to process incoming packet of type {0}. (PacketID: {1}, Protocol: {2}, LoginPhase: {3}, InnerException: {4}).",
- Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion),
- packetID,
- protocolversion,
- login_phase,
- innerException.GetType()),
- innerException);
- }
- }
-
- ///
- /// Start the updating thread. Should be called after login success.
- ///
- private void StartUpdating()
- {
- netRead = new Thread(new ThreadStart(Updater));
- netRead.Name = "ProtocolPacketHandler";
- netRead.Start();
- }
-
- ///
- /// Disconnect from the server, cancel network reading.
- ///
- public void Dispose()
- {
- try
- {
- if (netRead != null)
- {
- netRead.Abort();
- socketWrapper.Disconnect();
- }
- }
- catch { }
- }
-
- ///
- /// Send a packet to the server. Packet ID, compression, and encryption will be handled automatically.
- ///
- /// packet type
- /// packet Data
- private void SendPacket(PacketOutgoingType packet, IEnumerable packetData)
- {
- SendPacket(Protocol18PacketTypes.GetPacketOutgoingID(packet, protocolversion), packetData);
- }
-
- ///
- /// Send a packet to the server. Compression and encryption will be handled automatically.
- ///
- /// packet ID
- /// packet Data
- private void SendPacket(int packetID, IEnumerable packetData)
- {
- //The inner packet
- 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 = dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), compressed_packet);
+ ushort addBitmap = dataTypes.ReadNextUShort(packetData);
+ int compressedDataSize = dataTypes.ReadNextInt(packetData);
+ byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData);
+ byte[] decompressed = ZlibUtils.Decompress(compressed);
+ pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue(decompressed));
}
else
{
- byte[] uncompressed_length = dataTypes.GetVarInt(0); //Not compressed (short packet)
- the_packet = dataTypes.ConcatBytes(uncompressed_length, the_packet);
+ if (protocolversion >= MC114Version)
+ dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above
+ if (protocolversion >= MC115Version && chunksContinuous)
+ dataTypes.ReadData(1024 * 4, packetData); // Biomes - 1.15 and above
+ int dataSize = dataTypes.ReadNextVarInt(packetData);
+ pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData);
}
- }
-
- socketWrapper.SendDataRAW(dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), the_packet));
- }
-
- ///
- /// Do the Minecraft login.
- ///
- /// True if login successful
- public bool Login()
- {
- 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 = 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 = dataTypes.GetString(handler.GetUsername());
-
- SendPacket(0x00, login_packet);
-
- int packetID = -1;
- Queue packetData = new Queue();
- while (true)
- {
- ReadNextPacket(ref packetID, packetData);
- if (packetID == 0x00) //Login rejected
+ }
+ break;
+ case PacketIncomingType.MapData:
+ int mapid = dataTypes.ReadNextVarInt(packetData);
+ byte scale = dataTypes.ReadNextByte(packetData);
+ bool trackingposition = dataTypes.ReadNextBool(packetData);
+ bool locked = false;
+ if (protocolversion >= MC114Version)
+ {
+ locked = dataTypes.ReadNextBool(packetData);
+ }
+ int iconcount = dataTypes.ReadNextVarInt(packetData);
+ handler.OnMapData(mapid, scale, trackingposition, locked, iconcount);
+ break;
+ case PacketIncomingType.Title:
+ if (protocolversion >= MC18Version)
+ {
+ int action2 = dataTypes.ReadNextVarInt(packetData);
+ string titletext = String.Empty;
+ string subtitletext = String.Empty;
+ string actionbartext = String.Empty;
+ string json = String.Empty;
+ int fadein = -1;
+ int stay = -1;
+ int fadeout = -1;
+ if (protocolversion >= MC110Version)
{
- handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
- return false;
+ if (action2 == 0)
+ {
+ json = titletext;
+ titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
+ }
+ else if (action2 == 1)
+ {
+ json = subtitletext;
+ subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
+ }
+ else if (action2 == 2)
+ {
+ json = actionbartext;
+ actionbartext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
+ }
+ else if (action2 == 3)
+ {
+ fadein = dataTypes.ReadNextInt(packetData);
+ stay = dataTypes.ReadNextInt(packetData);
+ fadeout = dataTypes.ReadNextInt(packetData);
+ }
}
- else if (packetID == 0x01) //Encryption request
+ else
{
- string serverID = dataTypes.ReadNextString(packetData);
- byte[] Serverkey = dataTypes.ReadNextByteArray(packetData);
- byte[] token = dataTypes.ReadNextByteArray(packetData);
- return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, Serverkey);
+ if (action2 == 0)
+ {
+ json = titletext;
+ titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
+ }
+ else if (action2 == 1)
+ {
+ json = subtitletext;
+ subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
+ }
+ else if (action2 == 2)
+ {
+ fadein = dataTypes.ReadNextInt(packetData);
+ stay = dataTypes.ReadNextInt(packetData);
+ fadeout = dataTypes.ReadNextInt(packetData);
+ }
}
- else if (packetID == 0x02) //Login successful
+ handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json);
+ }
+ break;
+ case PacketIncomingType.MultiBlockChange:
+ if (handler.GetTerrainEnabled())
+ {
+ int chunkX = dataTypes.ReadNextInt(packetData);
+ int chunkZ = dataTypes.ReadNextInt(packetData);
+ int recordCount = protocolversion < MC18Version
+ ? (int)dataTypes.ReadNextShort(packetData)
+ : dataTypes.ReadNextVarInt(packetData);
+
+ for (int i = 0; i < recordCount; i++)
{
- ConsoleIO.WriteLineFormatted("§8Server is in offline mode.");
- login_phase = false;
+ byte locationXZ;
+ ushort blockIdMeta;
+ int blockY;
- if (!pForge.CompleteForgeHandshake())
- {
- ConsoleIO.WriteLineFormatted("§8Forge Login Handshake did not complete successfully");
- return false;
- }
+ if (protocolversion < MC18Version)
+ {
+ blockIdMeta = dataTypes.ReadNextUShort(packetData);
+ blockY = (ushort)dataTypes.ReadNextByte(packetData);
+ locationXZ = dataTypes.ReadNextByte(packetData);
+ }
+ else
+ {
+ locationXZ = dataTypes.ReadNextByte(packetData);
+ blockY = (ushort)dataTypes.ReadNextByte(packetData);
+ blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData);
+ }
- StartUpdating();
- return true; //No need to check session or start encryption
+ int blockX = locationXZ >> 4;
+ int blockZ = locationXZ & 0x0F;
+ Block block = new Block(blockIdMeta);
+ handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block);
}
- else HandlePacket(packetID, packetData);
- }
- }
-
- ///
- /// Start network encryption. Automatically called by Login() if the server requests encryption.
- ///
- /// True if encryption was successful
- private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, byte[] serverKey)
- {
- System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverKey);
- byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
-
- if (Settings.DebugMessages)
- ConsoleIO.WriteLineFormatted("§8Crypto keys & hash generated.");
-
- if (serverIDhash != "-")
- {
- Console.WriteLine("Checking Session...");
- if (!ProtocolHandler.SessionCheck(uuid, sessionID, CryptoHandler.getServerHash(serverIDhash, serverKey, secretKey)))
+ }
+ break;
+ case PacketIncomingType.BlockChange:
+ if (handler.GetTerrainEnabled())
+ {
+ if (protocolversion < MC18Version)
{
- handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, "Failed to check session.");
- return false;
+ 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(dataTypes.ReadNextLocation(packetData), new Block((ushort)dataTypes.ReadNextVarInt(packetData)));
+ }
+ break;
+ case PacketIncomingType.MapChunkBulk:
+ if (protocolversion < MC19Version && handler.GetTerrainEnabled())
+ {
+ int chunkCount;
+ bool hasSkyLight;
+ Queue chunkData = packetData;
- //Encrypt the data
- byte[] key_enc = dataTypes.GetArray(RSAService.Encrypt(secretKey, false));
- byte[] token_enc = dataTypes.GetArray(RSAService.Encrypt(token, false));
-
- //Encryption Response packet
- SendPacket(0x01, dataTypes.ConcatBytes(key_enc, token_enc));
-
- //Start client-side encryption
- socketWrapper.SwitchToEncrypted(secretKey);
-
- //Process the next packet
- int packetID = -1;
- Queue packetData = new Queue();
- while (true)
- {
- ReadNextPacket(ref packetID, packetData);
- if (packetID == 0x00) //Login rejected
+ //Read global fields
+ if (protocolversion < MC18Version)
{
- handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
- return false;
+ 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 Queue(decompressed);
}
- else if (packetID == 0x02) //Login successful
+ else
{
- login_phase = false;
-
- if (!pForge.CompleteForgeHandshake())
- {
- ConsoleIO.WriteLineFormatted("§8Forge StartEncryption Handshake did not complete successfully");
- return false;
- }
-
- StartUpdating();
- return true;
+ hasSkyLight = dataTypes.ReadNextBool(packetData);
+ chunkCount = dataTypes.ReadNextVarInt(packetData);
}
- else HandlePacket(packetID, packetData);
- }
- }
- ///
- /// Disconnect from the server
- ///
- public void Disconnect()
- {
- socketWrapper.Disconnect();
- }
+ //Read chunk records
+ int[] chunkXs = new int[chunkCount];
+ int[] chunkZs = new int[chunkCount];
+ ushort[] chunkMasks = new ushort[chunkCount];
+ ushort[] addBitmaps = new ushort[chunkCount];
+ for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
+ {
+ chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData);
+ chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData);
+ chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData);
+ addBitmaps[chunkColumnNo] = protocolversion < MC18Version
+ ? dataTypes.ReadNextUShort(packetData)
+ : (ushort)0;
+ }
- ///
- /// Autocomplete text while typing username or command
- ///
- /// Text behind cursor
- /// Completed text
- IEnumerable IAutoComplete.AutoComplete(string BehindCursor)
- {
- if (String.IsNullOrEmpty(BehindCursor))
- return new string[] { };
+ //Process chunk records
+ for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
+ pTerrain.ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, currentDimension, chunkData);
+ }
+ break;
+ case PacketIncomingType.UnloadChunk:
+ if (protocolversion >= MC19Version && handler.GetTerrainEnabled())
+ {
+ int chunkX = dataTypes.ReadNextInt(packetData);
+ int chunkZ = dataTypes.ReadNextInt(packetData);
+ handler.GetWorld()[chunkX, chunkZ] = null;
+ }
+ break;
+ case PacketIncomingType.PlayerListUpdate:
+ if (protocolversion >= MC18Version)
+ {
+ int action = dataTypes.ReadNextVarInt(packetData);
+ int numActions = dataTypes.ReadNextVarInt(packetData);
+ for (int i = 0; i < numActions; i++)
+ {
+ Guid uuid = dataTypes.ReadNextUUID(packetData);
+ switch (action)
+ {
+ case 0x00: //Player Join
+ string name = dataTypes.ReadNextString(packetData);
+ int propNum = dataTypes.ReadNextVarInt(packetData);
+ for (int p = 0; p < propNum; p++)
+ {
+ string key = dataTypes.ReadNextString(packetData);
+ string val = dataTypes.ReadNextString(packetData);
+ if (dataTypes.ReadNextBool(packetData))
+ dataTypes.ReadNextString(packetData);
+ }
+ handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData));
+ dataTypes.ReadNextVarInt(packetData);
+ if (dataTypes.ReadNextBool(packetData))
+ dataTypes.ReadNextString(packetData);
+ handler.OnPlayerJoin(uuid, name);
+ break;
+ case 0x01: //Update gamemode
+ handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData));
+ break;
+ case 0x02: //Update latency
+ int latency = dataTypes.ReadNextVarInt(packetData);
+ handler.OnLatencyUpdate(uuid, latency); //Update latency;
+ break;
+ case 0x03: //Update display name
+ if (dataTypes.ReadNextBool(packetData))
+ dataTypes.ReadNextString(packetData);
+ break;
+ case 0x04: //Player Leave
+ handler.OnPlayerLeave(uuid);
+ break;
+ default:
+ //Unknown player list item type
+ break;
+ }
+ }
+ }
+ else //MC 1.7.X does not provide UUID in tab-list updates
+ {
+ string name = 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);
+ else handler.OnPlayerLeave(FakeUUID);
+ }
+ break;
+ case PacketIncomingType.TabCompleteResult:
+ if (protocolversion >= MC113Version)
+ {
+ autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData);
+ dataTypes.ReadNextVarInt(packetData); // Start of text to replace
+ dataTypes.ReadNextVarInt(packetData); // Length of text to replace
+ }
- byte[] transaction_id = dataTypes.GetVarInt(autocomplete_transaction_id);
- byte[] assume_command = new byte[] { 0x00 };
- byte[] has_position = new byte[] { 0x00 };
+ int autocomplete_count = dataTypes.ReadNextVarInt(packetData);
+ autocomplete_result.Clear();
- byte[] tabcomplete_packet = new byte[] { };
-
- if (protocolversion >= MC18Version)
- {
+ for (int i = 0; i < autocomplete_count; i++)
+ {
+ autocomplete_result.Add(dataTypes.ReadNextString(packetData));
if (protocolversion >= MC113Version)
{
- tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, transaction_id);
- tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor));
+ // Skip optional tooltip for each tab-complete result
+ if (dataTypes.ReadNextBool(packetData))
+ dataTypes.ReadNextString(packetData);
+ }
+ }
+
+ autocomplete_received = true;
+ break;
+ case PacketIncomingType.PluginMessage:
+ String channel = dataTypes.ReadNextString(packetData);
+ // Length is unneeded as the whole remaining packetData is the entire payload of the packet.
+ if (protocolversion < MC18Version)
+ pForge.ReadNextVarShort(packetData);
+ handler.OnPluginChannelMessage(channel, packetData.ToArray());
+ return pForge.HandlePluginMessage(channel, packetData, ref currentDimension);
+ case PacketIncomingType.KickPacket:
+ handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
+ return false;
+ case PacketIncomingType.NetworkCompressionTreshold:
+ if (protocolversion >= MC18Version && protocolversion < MC19Version)
+ compression_treshold = dataTypes.ReadNextVarInt(packetData);
+ break;
+ case PacketIncomingType.OpenWindow:
+ if (handler.GetInventoryEnabled())
+ {
+ if (protocolversion < MC114Version)
+ {
+ // MC 1.13 or lower
+ byte windowID = dataTypes.ReadNextByte(packetData);
+ string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper();
+ ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type);
+ string title = dataTypes.ReadNextString(packetData);
+ byte slots = dataTypes.ReadNextByte(packetData);
+ Container inventory = new Container(windowID, inventoryType, ChatParser.ParseText(title));
+ handler.OnInventoryOpen(windowID, inventory);
}
else
{
- tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor));
+ // MC 1.14 or greater
+ int windowID = dataTypes.ReadNextVarInt(packetData);
+ int windowType = dataTypes.ReadNextVarInt(packetData);
+ string title = dataTypes.ReadNextString(packetData);
+ Container inventory = new Container(windowID, windowType, ChatParser.ParseText(title));
+ handler.OnInventoryOpen(windowID, inventory);
+ }
+ }
+ break;
+ case PacketIncomingType.CloseWindow:
+ if (handler.GetInventoryEnabled())
+ {
+ byte windowID = dataTypes.ReadNextByte(packetData);
+ lock (window_actions) { window_actions[windowID] = 0; }
+ handler.OnInventoryClose(windowID);
+ }
+ break;
+ case PacketIncomingType.WindowItems:
+ if (handler.GetInventoryEnabled())
+ {
+ byte windowId = dataTypes.ReadNextByte(packetData);
+ short elements = dataTypes.ReadNextShort(packetData);
+ Dictionary inventorySlots = new Dictionary();
+ for (short slotId = 0; slotId < elements; slotId++)
+ {
+ Item item = dataTypes.ReadNextItemSlot(packetData);
+ if (item != null)
+ inventorySlots[slotId] = item;
+ }
+ handler.OnWindowItems(windowId, inventorySlots);
+ }
+ break;
+ case PacketIncomingType.SetSlot:
+ if (handler.GetInventoryEnabled())
+ {
+ byte windowID = dataTypes.ReadNextByte(packetData);
+ short slotID = dataTypes.ReadNextShort(packetData);
+ Item item = dataTypes.ReadNextItemSlot(packetData);
+ handler.OnSetSlot(windowID, slotID, item);
+ }
+ break;
+ case PacketIncomingType.ResourcePackSend:
+ string url = dataTypes.ReadNextString(packetData);
+ string hash = dataTypes.ReadNextString(packetData);
+ // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056)
+ if (hash.Length != 40)
+ break;
+ //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 = 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;
+ case PacketIncomingType.SpawnEntity:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false);
+ handler.OnSpawnEntity(entity);
+ }
+ break;
+ case PacketIncomingType.EntityEquipment:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ int entityid = dataTypes.ReadNextVarInt(packetData);
+ int slot2 = dataTypes.ReadNextVarInt(packetData);
+ Item item = dataTypes.ReadNextItemSlot(packetData);
+ handler.OnEntityEquipment(entityid, slot2, item);
+ }
+ break;
+ case PacketIncomingType.SpawnLivingEntity:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, true);
+ // packet before 1.15 has metadata at the end
+ // this is not handled in dataTypes.ReadNextEntity()
+ // we are simply ignoring leftover data in packet
+ handler.OnSpawnEntity(entity);
+ }
+ break;
+ case PacketIncomingType.SpawnPlayer:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ int EntityID = dataTypes.ReadNextVarInt(packetData);
+ Guid UUID = dataTypes.ReadNextUUID(packetData);
+ double X = dataTypes.ReadNextDouble(packetData);
+ double Y = dataTypes.ReadNextDouble(packetData);
+ double Z = dataTypes.ReadNextDouble(packetData);
+ byte Yaw = dataTypes.ReadNextByte(packetData);
+ byte Pitch = dataTypes.ReadNextByte(packetData);
- if (protocolversion >= MC19Version)
+ Location EntityLocation = new Location(X, Y, Z);
+
+ handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch);
+ }
+ break;
+ case PacketIncomingType.DestroyEntities:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ int EntityCount = dataTypes.ReadNextVarInt(packetData);
+ int[] EntitiesList = new int[EntityCount];
+ for (int i = 0; i < EntityCount; i++)
+ {
+ EntitiesList[i] = dataTypes.ReadNextVarInt(packetData);
+ }
+ handler.OnDestroyEntities(EntitiesList);
+ }
+ break;
+ case PacketIncomingType.EntityPosition:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ int EntityID = dataTypes.ReadNextVarInt(packetData);
+ Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
+ Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
+ Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
+ bool OnGround = dataTypes.ReadNextBool(packetData);
+ DeltaX = DeltaX / (128 * 32);
+ DeltaY = DeltaY / (128 * 32);
+ DeltaZ = DeltaZ / (128 * 32);
+ handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
+ }
+ break;
+ case PacketIncomingType.EntityPositionAndRotation:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ int EntityID = dataTypes.ReadNextVarInt(packetData);
+ Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
+ Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
+ Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
+ byte _yaw = dataTypes.ReadNextByte(packetData);
+ byte _pitch = dataTypes.ReadNextByte(packetData);
+ bool OnGround = dataTypes.ReadNextBool(packetData);
+ DeltaX = DeltaX / (128 * 32);
+ DeltaY = DeltaY / (128 * 32);
+ DeltaZ = DeltaZ / (128 * 32);
+ handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
+ }
+ break;
+ case PacketIncomingType.EntityProperties:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ int EntityID = dataTypes.ReadNextVarInt(packetData);
+ int NumberOfProperties = dataTypes.ReadNextInt(packetData);
+ Dictionary keys = new Dictionary();
+ for (int i = 0; i < NumberOfProperties; i++)
+ {
+ string _key = dataTypes.ReadNextString(packetData);
+ Double _value = dataTypes.ReadNextDouble(packetData);
+
+ List op0 = new List();
+ List op1 = new List();
+ List op2 = new List();
+ int NumberOfModifiers = dataTypes.ReadNextVarInt(packetData);
+ for (int j = 0; j < NumberOfModifiers; j++)
+ {
+ dataTypes.ReadNextUUID(packetData);
+ Double amount = dataTypes.ReadNextDouble(packetData);
+ byte operation = dataTypes.ReadNextByte(packetData);
+ switch (operation)
{
- tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, assume_command);
+ case 0: op0.Add(amount); break;
+ case 1: op1.Add(amount); break;
+ case 2: op2.Add(amount + 1); break;
}
-
- tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, has_position);
+ }
+ if (op0.Count > 0) _value += op0.Sum();
+ if (op1.Count > 0) _value *= 1 + op1.Sum();
+ if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x);
+ keys.Add(_key, _value);
}
- }
- else
- {
- tabcomplete_packet = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor));
- }
-
- autocomplete_received = false;
- autocomplete_result.Clear();
- autocomplete_result.Add(BehindCursor);
- SendPacket(PacketOutgoingType.TabComplete, tabcomplete_packet);
-
- int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms)
- while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; }
- if (autocomplete_result.Count > 0)
- ConsoleIO.WriteLineFormatted("§8" + String.Join(" ", autocomplete_result), false);
- return autocomplete_result;
- }
-
- ///
- /// Ping a Minecraft server to get information about the server
- ///
- /// True if ping was successful
- public static bool doPing(string host, int port, ref int protocolversion, ref ForgeInfo forgeInfo)
- {
- 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 = dataTypes.GetVarInt(0);
- byte[] protocol_version = dataTypes.GetVarInt(-1);
- byte[] server_port = BitConverter.GetBytes((ushort)port); Array.Reverse(server_port);
- 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);
-
- socketWrapper.SendDataRAW(tosend);
-
- byte[] status_request = dataTypes.GetVarInt(0);
- byte[] request_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(status_request.Length), status_request);
-
- socketWrapper.SendDataRAW(request_packet);
-
- int packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
- if (packetLength > 0) //Read Response length
- {
- Queue packetData = new Queue(socketWrapper.ReadDataRAW(packetLength));
- if (dataTypes.ReadNextVarInt(packetData) == 0x00) //Read Packet ID
- {
- string result = dataTypes.ReadNextString(packetData); //Get the Json data
-
- if (!String.IsNullOrEmpty(result) && result.StartsWith("{") && result.EndsWith("}"))
- {
- Json.JSONData jsonData = Json.ParseJson(result);
- if (jsonData.Type == Json.JSONData.DataType.Object && jsonData.Properties.ContainsKey("version"))
- {
- Json.JSONData versionData = jsonData.Properties["version"];
-
- //Retrieve display name of the Minecraft version
- if (versionData.Properties.ContainsKey("name"))
- version = versionData.Properties["name"].StringValue;
-
- //Retrieve protocol version number for handling this server
- if (versionData.Properties.ContainsKey("protocol"))
- protocolversion = dataTypes.Atoi(versionData.Properties["protocol"].StringValue);
-
- // Check for forge on the server.
- Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo);
-
- ConsoleIO.WriteLineFormatted("§8Server version : " + version + " (protocol v" + protocolversion + (forgeInfo != null ? ", with Forge)." : ")."));
-
- return true;
- }
- }
- }
- }
- return false;
- }
-
- ///
- /// Get max length for chat messages
- ///
- /// Max length, in characters
- public int GetMaxChatMessageLength()
- {
- return protocolversion > MC110Version
- ? 256
- : 100;
- }
-
- ///
- /// Send a chat message to the server
- ///
- /// Message
- /// True if properly sent
- public bool SendChatMessage(string message)
- {
- if (String.IsNullOrEmpty(message))
- return true;
- try
- {
- byte[] message_packet = dataTypes.GetString(message);
- SendPacket(PacketOutgoingType.ChatMessage, message_packet);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- public bool SendEntityAction(int PlayerEntityID, int ActionID)
- {
- try
- {
- List fields = new List();
- fields.AddRange(dataTypes.GetVarInt(PlayerEntityID));
- fields.AddRange(dataTypes.GetVarInt(ActionID));
- fields.AddRange(dataTypes.GetVarInt(0));
- SendPacket(PacketOutgoingType.EntityAction, fields);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- ///
- /// Send a respawn packet to the server
- ///
- /// True if properly sent
- public bool SendRespawnPacket()
- {
- try
- {
- SendPacket(PacketOutgoingType.ClientStatus, new byte[] { 0 });
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- ///
- /// Tell the server what client is being used to connect to the server
- ///
- /// Client string describing the client
- /// True if brand info was successfully sent
- public bool SendBrandInfo(string brandInfo)
- {
- if (String.IsNullOrEmpty(brandInfo))
- return false;
- // Plugin channels were significantly changed between Minecraft 1.12 and 1.13
- // https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14132#Plugin_Channels
- if (protocolversion >= MC113Version)
- {
- return SendPluginChannelPacket("minecraft:brand", dataTypes.GetString(brandInfo));
- }
- else
- {
- return SendPluginChannelPacket("MC|Brand", dataTypes.GetString(brandInfo));
- }
- }
-
- ///
- /// Inform the server of the client's Minecraft settings
- ///
- /// Client language eg en_US
- /// View distance, in chunks
- /// Game difficulty (client-side...)
- /// Chat mode (allows muting yourself)
- /// Show chat colors
- /// Show skin layers
- /// 1.9+ main hand
- /// True if client settings were successfully sent
- public bool SendClientSettings(string language, byte viewDistance, byte difficulty, byte chatMode, bool chatColors, byte skinParts, byte mainHand)
- {
- try
- {
- List fields = new List();
- fields.AddRange(dataTypes.GetString(language));
- fields.Add(viewDistance);
- fields.AddRange(protocolversion >= MC19Version
- ? dataTypes.GetVarInt(chatMode)
- : new byte[] { chatMode });
- fields.Add(chatColors ? (byte)1 : (byte)0);
- if (protocolversion < MC18Version)
- {
- fields.Add(difficulty);
- fields.Add((byte)(skinParts & 0x1)); //show cape
- }
- else fields.Add(skinParts);
- if (protocolversion >= MC19Version)
- fields.AddRange(dataTypes.GetVarInt(mainHand));
- SendPacket(PacketOutgoingType.ClientSettings, fields);
- }
- catch (SocketException) { }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- return false;
- }
-
- ///
- /// Send a location update to the server
- ///
- /// The new location of the player
- /// True if the player is on the ground
- /// Optional new yaw for updating player look
- /// Optional new pitch for updating player look
- /// True if the location update was successfully sent
- public bool SendLocationUpdate(Location location, bool onGround, float? yaw = null, float? pitch = null)
- {
- if (handler.GetTerrainEnabled())
- {
- byte[] yawpitch = new byte[0];
- PacketOutgoingType packetType = PacketOutgoingType.PlayerPosition;
-
- if (yaw.HasValue && pitch.HasValue)
- {
- yawpitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), dataTypes.GetFloat(pitch.Value));
- packetType = PacketOutgoingType.PlayerPositionAndLook;
- }
-
- try
- {
- SendPacket(packetType, dataTypes.ConcatBytes(
- dataTypes.GetDouble(location.X),
- dataTypes.GetDouble(location.Y),
- protocolversion < MC18Version
- ? dataTypes.GetDouble(location.Y + 1.62)
- : new byte[0],
- dataTypes.GetDouble(location.Z),
- yawpitch,
- new byte[] { onGround ? (byte)1 : (byte)0 }));
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
- else return false;
- }
-
- ///
- /// Send a plugin channel packet (0x17) to the server, compression and encryption will be handled automatically
- ///
- /// Channel to send packet on
- /// packet Data
- public bool SendPluginChannelPacket(string channel, byte[] data)
- {
- try
- {
- // In 1.7, length needs to be included.
- // In 1.8, it must not be.
- if (protocolversion < MC18Version)
- {
- byte[] length = BitConverter.GetBytes((short)data.Length);
- Array.Reverse(length);
-
- SendPacket(PacketOutgoingType.PluginMessage, dataTypes.ConcatBytes(dataTypes.GetString(channel), length, data));
- }
- else
- {
- SendPacket(PacketOutgoingType.PluginMessage, dataTypes.ConcatBytes(dataTypes.GetString(channel), data));
- }
-
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- ///
- /// Send an Interact Entity Packet to server
- ///
- ///
- ///
- ///
- public bool SendInteractEntity(int EntityID, int type)
- {
- try
- {
- List fields = new List();
- fields.AddRange(dataTypes.GetVarInt(EntityID));
- fields.AddRange(dataTypes.GetVarInt(type));
- SendPacket(PacketOutgoingType.InteractEntity, fields);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- // TODO: Interact at block location (e.g. chest minecart)
- public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z, int hand)
- {
- try
- {
- List fields = new List();
- fields.AddRange(dataTypes.GetVarInt(EntityID));
- fields.AddRange(dataTypes.GetVarInt(type));
- fields.AddRange(dataTypes.GetFloat(X));
- fields.AddRange(dataTypes.GetFloat(Y));
- fields.AddRange(dataTypes.GetFloat(Z));
- fields.AddRange(dataTypes.GetVarInt(hand));
- SendPacket(PacketOutgoingType.InteractEntity, fields);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
- public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z)
- {
- return false;
- }
-
- public bool SendUseItem(int hand)
- {
- if (protocolversion < MC19Version)
- return false; // Packet does not exist prior to MC 1.9
- // According to https://wiki.vg/index.php?title=Protocol&oldid=5486#Player_Block_Placement
- // MC 1.7 does this using Player Block Placement with special values
- // TODO once Player Block Placement is implemented for older versions
- try
- {
- List packet = new List();
- packet.AddRange(dataTypes.GetVarInt(hand));
- SendPacket(PacketOutgoingType.UseItem, packet);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- public bool SendPlayerDigging(int status, Location location, Direction face)
- {
- try
- {
- List packet = new List();
- packet.AddRange(dataTypes.GetVarInt(status));
- packet.AddRange(dataTypes.GetLocation(location));
- packet.AddRange(dataTypes.GetVarInt(dataTypes.GetBlockFace(face)));
- SendPacket(PacketOutgoingType.PlayerDigging, packet);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- public bool SendPlayerBlockPlacement(int hand, Location location, Direction face)
- {
- if (protocolversion < MC114Version)
- return false; // NOT IMPLEMENTED for older MC versions
- try
- {
- List packet = new List();
- packet.AddRange(dataTypes.GetVarInt(hand));
- packet.AddRange(dataTypes.GetLocation(location));
- packet.AddRange(dataTypes.GetVarInt(dataTypes.GetBlockFace(face)));
- packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorX
- packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorY
- packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorZ
- packet.Add(0); // insideBlock = false;
- SendPacket(PacketOutgoingType.PlayerBlockPlacement, packet);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- public bool SendHeldItemChange(short slot)
- {
- try
- {
- List packet = new List();
- packet.AddRange(dataTypes.GetShort(slot));
- SendPacket(PacketOutgoingType.HeldItemChange, packet);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
- public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item)
- {
- try
- {
- short actionNumber;
- lock (window_actions)
- {
- if (!window_actions.ContainsKey(windowId))
- window_actions[windowId] = 0;
- actionNumber = (short)(window_actions[windowId] + 1);
- window_actions[windowId] = actionNumber;
- }
-
- byte button = 0;
- byte mode = 0;
- switch (action)
- {
- case WindowActionType.LeftClick: button = 0; break;
- case WindowActionType.RightClick: button = 1; break;
- case WindowActionType.MiddleClick: button = 2; mode = 3; break;
- case WindowActionType.DropItem:
- button = 0;
- mode = 4;
- item = new Item(-1, 0, null);
- Container inventory = handler.GetInventory(windowId);
- if (inventory.Items.ContainsKey(slotId))
- inventory.Items[slotId].Count--; // server won't update us after dropped
- if (inventory.Items[slotId].Count == 0)
- inventory.Items.Remove(slotId);
- break;
- case WindowActionType.DropItemStack:
- button = 1;
- mode = 4;
- item = new Item(-1, 0, null);
- inventory = handler.GetInventory(windowId);
- inventory.Items.Remove(slotId); // server won't update us after dropped
- break;
- }
-
- List packet = new List();
- packet.Add((byte)windowId);
- packet.AddRange(dataTypes.GetShort((short)slotId));
- packet.Add(button);
- packet.AddRange(dataTypes.GetShort(actionNumber));
-
- if (protocolversion >= MC19Version)
- packet.AddRange(dataTypes.GetVarInt(mode));
- else packet.Add(mode);
-
- packet.AddRange(dataTypes.GetItemSlot(item));
-
- SendPacket(PacketOutgoingType.ClickWindow, packet);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- public bool SendCreativeInventoryAction(int slot, ItemType itemType, int count, Dictionary nbt)
- {
- try
- {
- List packet = new List();
- packet.AddRange(dataTypes.GetShort((short)slot));
- packet.AddRange(dataTypes.GetItemSlot(new Item((int)itemType, count, nbt)));
- SendPacket(PacketOutgoingType.CreativeInventoryAction, packet);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- public bool SendAnimation(int animation, int playerid)
- {
- try
- {
- if (animation == 0 || animation == 1)
- {
- List packet = new List();
-
- if (protocolversion < MC18Version)
- {
- packet.AddRange(dataTypes.GetInt(playerid));
- packet.Add((byte)1); // Swing arm
- }
- else if (protocolversion < MC19Version)
- {
- // No fields in 1.8.X
- }
- else // MC 1.9+
- {
- packet.AddRange(dataTypes.GetVarInt(animation));
- }
-
- SendPacket(PacketOutgoingType.Animation, packet);
- return true;
- }
- else
- {
- return false;
- }
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- public bool SendCloseWindow(int windowId)
- {
- try
- {
- lock (window_actions)
- {
- if (window_actions.ContainsKey(windowId))
- window_actions[windowId] = 0;
- }
- SendPacket(PacketOutgoingType.CloseWindow, new[] { (byte)windowId });
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
-
- public bool SendUpdateSign(Location sign, string line1, string line2, string line3, string line4)
- {
- try
- {
- if (line1.Length > 23)
- line1 = line1.Substring(0, 23);
- if (line2.Length > 23)
- line2 = line1.Substring(0, 23);
- if (line3.Length > 23)
- line3 = line1.Substring(0, 23);
- if (line4.Length > 23)
- line4 = line1.Substring(0, 23);
-
- List packet = new List();
- packet.AddRange(dataTypes.GetLocation(sign));
- packet.AddRange(dataTypes.GetString(line1));
- packet.AddRange(dataTypes.GetString(line2));
- packet.AddRange(dataTypes.GetString(line3));
- packet.AddRange(dataTypes.GetString(line4));
- SendPacket(PacketOutgoingType.UpdateSign, packet);
- return true;
- }
- catch (SocketException) { return false; }
- catch (System.IO.IOException) { return false; }
- catch (ObjectDisposedException) { return false; }
- }
+ handler.OnEntityProperties(EntityID, keys);
+ }
+ break;
+ case PacketIncomingType.TimeUpdate:
+ long WorldAge = dataTypes.ReadNextLong(packetData);
+ long TimeOfday = dataTypes.ReadNextLong(packetData);
+ handler.OnTimeUpdate(WorldAge, TimeOfday);
+ break;
+ case PacketIncomingType.EntityTeleport:
+ if (handler.GetEntityHandlingEnabled())
+ {
+ int EntityID = dataTypes.ReadNextVarInt(packetData);
+ Double X = dataTypes.ReadNextDouble(packetData);
+ Double Y = dataTypes.ReadNextDouble(packetData);
+ Double Z = dataTypes.ReadNextDouble(packetData);
+ byte EntityYaw = dataTypes.ReadNextByte(packetData);
+ byte EntityPitch = dataTypes.ReadNextByte(packetData);
+ bool OnGround = dataTypes.ReadNextBool(packetData);
+ handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround);
+ }
+ break;
+ case PacketIncomingType.UpdateHealth:
+ float health = dataTypes.ReadNextFloat(packetData);
+ int food;
+ if (protocolversion >= MC18Version)
+ food = dataTypes.ReadNextVarInt(packetData);
+ else
+ food = dataTypes.ReadNextShort(packetData);
+ dataTypes.ReadNextFloat(packetData); // Food Saturation
+ handler.OnUpdateHealth(health, food);
+ break;
+ case PacketIncomingType.SetExperience:
+ float experiencebar = dataTypes.ReadNextFloat(packetData);
+ int level = dataTypes.ReadNextVarInt(packetData);
+ int totalexperience = dataTypes.ReadNextVarInt(packetData);
+ handler.OnSetExperience(experiencebar, level, totalexperience);
+ break;
+ case PacketIncomingType.Explosion:
+ Location explosionLocation = new Location(dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData));
+ float explosionStrength = dataTypes.ReadNextFloat(packetData);
+ int explosionBlockCount = dataTypes.ReadNextInt(packetData);
+ // Ignoring additional fields (records, pushback)
+ handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount);
+ break;
+ case PacketIncomingType.HeldItemChange:
+ byte slot = dataTypes.ReadNextByte(packetData);
+ handler.OnHeldItemChange(slot);
+ break;
+ default:
+ return false; //Ignored packet
+ }
+ return true; //Packet processed
+ }
+ catch (Exception innerException)
+ {
+ if (innerException is ThreadAbortException || innerException is SocketException || innerException.InnerException is SocketException)
+ throw; //Thread abort or Connection lost rather than invalid data
+ throw new System.IO.InvalidDataException(
+ String.Format("Failed to process incoming packet of type {0}. (PacketID: {1}, Protocol: {2}, LoginPhase: {3}, InnerException: {4}).",
+ Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion),
+ packetID,
+ protocolversion,
+ login_phase,
+ innerException.GetType()),
+ innerException);
+ }
}
+
+ ///
+ /// Start the updating thread. Should be called after login success.
+ ///
+ private void StartUpdating()
+ {
+ netRead = new Thread(new ThreadStart(Updater));
+ netRead.Name = "ProtocolPacketHandler";
+ netRead.Start();
+ }
+
+ ///
+ /// Disconnect from the server, cancel network reading.
+ ///
+ public void Dispose()
+ {
+ try
+ {
+ if (netRead != null)
+ {
+ netRead.Abort();
+ socketWrapper.Disconnect();
+ }
+ }
+ catch { }
+ }
+
+ ///
+ /// Send a packet to the server. Packet ID, compression, and encryption will be handled automatically.
+ ///
+ /// packet type
+ /// packet Data
+ private void SendPacket(PacketOutgoingType packet, IEnumerable packetData)
+ {
+ SendPacket(Protocol18PacketTypes.GetPacketOutgoingID(packet, protocolversion), packetData);
+ }
+
+ ///
+ /// Send a packet to the server. Compression and encryption will be handled automatically.
+ ///
+ /// packet ID
+ /// packet Data
+ private void SendPacket(int packetID, IEnumerable packetData)
+ {
+ //The inner packet
+ 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 = dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), compressed_packet);
+ }
+ else
+ {
+ byte[] uncompressed_length = dataTypes.GetVarInt(0); //Not compressed (short packet)
+ the_packet = dataTypes.ConcatBytes(uncompressed_length, the_packet);
+ }
+ }
+
+ socketWrapper.SendDataRAW(dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), the_packet));
+ }
+
+ ///
+ /// Do the Minecraft login.
+ ///
+ /// True if login successful
+ public bool Login()
+ {
+ 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 = 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 = dataTypes.GetString(handler.GetUsername());
+
+ SendPacket(0x00, login_packet);
+
+ int packetID = -1;
+ Queue packetData = new Queue();
+ while (true)
+ {
+ ReadNextPacket(ref packetID, packetData);
+ if (packetID == 0x00) //Login rejected
+ {
+ handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
+ return false;
+ }
+ else if (packetID == 0x01) //Encryption request
+ {
+ 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
+ {
+ ConsoleIO.WriteLineFormatted("§8Server is in offline mode.");
+ login_phase = false;
+
+ if (!pForge.CompleteForgeHandshake())
+ {
+ ConsoleIO.WriteLineFormatted("§8Forge Login Handshake did not complete successfully");
+ return false;
+ }
+
+ StartUpdating();
+ return true; //No need to check session or start encryption
+ }
+ else HandlePacket(packetID, packetData);
+ }
+ }
+
+ ///
+ /// Start network encryption. Automatically called by Login() if the server requests encryption.
+ ///
+ /// True if encryption was successful
+ private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, byte[] serverKey)
+ {
+ System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverKey);
+ byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
+
+ if (Settings.DebugMessages)
+ ConsoleIO.WriteLineFormatted("§8Crypto keys & hash generated.");
+
+ if (serverIDhash != "-")
+ {
+ Console.WriteLine("Checking Session...");
+ if (!ProtocolHandler.SessionCheck(uuid, sessionID, CryptoHandler.getServerHash(serverIDhash, serverKey, secretKey)))
+ {
+ handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, "Failed to check session.");
+ return false;
+ }
+ }
+
+ //Encrypt the data
+ byte[] key_enc = dataTypes.GetArray(RSAService.Encrypt(secretKey, false));
+ byte[] token_enc = dataTypes.GetArray(RSAService.Encrypt(token, false));
+
+ //Encryption Response packet
+ SendPacket(0x01, dataTypes.ConcatBytes(key_enc, token_enc));
+
+ //Start client-side encryption
+ socketWrapper.SwitchToEncrypted(secretKey);
+
+ //Process the next packet
+ int packetID = -1;
+ Queue packetData = new Queue();
+ while (true)
+ {
+ ReadNextPacket(ref packetID, packetData);
+ if (packetID == 0x00) //Login rejected
+ {
+ handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
+ return false;
+ }
+ else if (packetID == 0x02) //Login successful
+ {
+ login_phase = false;
+
+ if (!pForge.CompleteForgeHandshake())
+ {
+ ConsoleIO.WriteLineFormatted("§8Forge StartEncryption Handshake did not complete successfully");
+ return false;
+ }
+
+ StartUpdating();
+ return true;
+ }
+ else HandlePacket(packetID, packetData);
+ }
+ }
+
+ ///
+ /// Disconnect from the server
+ ///
+ public void Disconnect()
+ {
+ socketWrapper.Disconnect();
+ }
+
+ ///
+ /// Autocomplete text while typing username or command
+ ///
+ /// Text behind cursor
+ /// Completed text
+ IEnumerable IAutoComplete.AutoComplete(string BehindCursor)
+ {
+ if (String.IsNullOrEmpty(BehindCursor))
+ return new string[] { };
+
+ byte[] transaction_id = dataTypes.GetVarInt(autocomplete_transaction_id);
+ byte[] assume_command = new byte[] { 0x00 };
+ byte[] has_position = new byte[] { 0x00 };
+
+ byte[] tabcomplete_packet = new byte[] { };
+
+ if (protocolversion >= MC18Version)
+ {
+ if (protocolversion >= MC113Version)
+ {
+ tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, transaction_id);
+ tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor));
+ }
+ else
+ {
+ tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor));
+
+ if (protocolversion >= MC19Version)
+ {
+ tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, assume_command);
+ }
+
+ tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, has_position);
+ }
+ }
+ else
+ {
+ tabcomplete_packet = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor));
+ }
+
+ autocomplete_received = false;
+ autocomplete_result.Clear();
+ autocomplete_result.Add(BehindCursor);
+ SendPacket(PacketOutgoingType.TabComplete, tabcomplete_packet);
+
+ int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms)
+ while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; }
+ if (autocomplete_result.Count > 0)
+ ConsoleIO.WriteLineFormatted("§8" + String.Join(" ", autocomplete_result), false);
+ return autocomplete_result;
+ }
+
+ ///
+ /// Ping a Minecraft server to get information about the server
+ ///
+ /// True if ping was successful
+ public static bool doPing(string host, int port, ref int protocolversion, ref ForgeInfo forgeInfo)
+ {
+ 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 = dataTypes.GetVarInt(0);
+ byte[] protocol_version = dataTypes.GetVarInt(-1);
+ byte[] server_port = BitConverter.GetBytes((ushort)port); Array.Reverse(server_port);
+ 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);
+
+ socketWrapper.SendDataRAW(tosend);
+
+ byte[] status_request = dataTypes.GetVarInt(0);
+ byte[] request_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(status_request.Length), status_request);
+
+ socketWrapper.SendDataRAW(request_packet);
+
+ int packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
+ if (packetLength > 0) //Read Response length
+ {
+ Queue packetData = new Queue(socketWrapper.ReadDataRAW(packetLength));
+ if (dataTypes.ReadNextVarInt(packetData) == 0x00) //Read Packet ID
+ {
+ string result = dataTypes.ReadNextString(packetData); //Get the Json data
+
+ if (!String.IsNullOrEmpty(result) && result.StartsWith("{") && result.EndsWith("}"))
+ {
+ Json.JSONData jsonData = Json.ParseJson(result);
+ if (jsonData.Type == Json.JSONData.DataType.Object && jsonData.Properties.ContainsKey("version"))
+ {
+ Json.JSONData versionData = jsonData.Properties["version"];
+
+ //Retrieve display name of the Minecraft version
+ if (versionData.Properties.ContainsKey("name"))
+ version = versionData.Properties["name"].StringValue;
+
+ //Retrieve protocol version number for handling this server
+ if (versionData.Properties.ContainsKey("protocol"))
+ protocolversion = dataTypes.Atoi(versionData.Properties["protocol"].StringValue);
+
+ // Check for forge on the server.
+ Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo);
+
+ ConsoleIO.WriteLineFormatted("§8Server version : " + version + " (protocol v" + protocolversion + (forgeInfo != null ? ", with Forge)." : ")."));
+
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Get max length for chat messages
+ ///
+ /// Max length, in characters
+ public int GetMaxChatMessageLength()
+ {
+ return protocolversion > MC110Version
+ ? 256
+ : 100;
+ }
+
+ ///
+ /// Send a chat message to the server
+ ///
+ /// Message
+ /// True if properly sent
+ public bool SendChatMessage(string message)
+ {
+ if (String.IsNullOrEmpty(message))
+ return true;
+ try
+ {
+ byte[] message_packet = dataTypes.GetString(message);
+ SendPacket(PacketOutgoingType.ChatMessage, message_packet);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ public bool SendEntityAction(int PlayerEntityID, int ActionID)
+ {
+ try
+ {
+ List fields = new List();
+ fields.AddRange(dataTypes.GetVarInt(PlayerEntityID));
+ fields.AddRange(dataTypes.GetVarInt(ActionID));
+ fields.AddRange(dataTypes.GetVarInt(0));
+ SendPacket(PacketOutgoingType.EntityAction, fields);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ ///
+ /// Send a respawn packet to the server
+ ///
+ /// True if properly sent
+ public bool SendRespawnPacket()
+ {
+ try
+ {
+ SendPacket(PacketOutgoingType.ClientStatus, new byte[] { 0 });
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ ///
+ /// Tell the server what client is being used to connect to the server
+ ///
+ /// Client string describing the client
+ /// True if brand info was successfully sent
+ public bool SendBrandInfo(string brandInfo)
+ {
+ if (String.IsNullOrEmpty(brandInfo))
+ return false;
+ // Plugin channels were significantly changed between Minecraft 1.12 and 1.13
+ // https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14132#Plugin_Channels
+ if (protocolversion >= MC113Version)
+ {
+ return SendPluginChannelPacket("minecraft:brand", dataTypes.GetString(brandInfo));
+ }
+ else
+ {
+ return SendPluginChannelPacket("MC|Brand", dataTypes.GetString(brandInfo));
+ }
+ }
+
+ ///
+ /// Inform the server of the client's Minecraft settings
+ ///
+ /// Client language eg en_US
+ /// View distance, in chunks
+ /// Game difficulty (client-side...)
+ /// Chat mode (allows muting yourself)
+ /// Show chat colors
+ /// Show skin layers
+ /// 1.9+ main hand
+ /// True if client settings were successfully sent
+ public bool SendClientSettings(string language, byte viewDistance, byte difficulty, byte chatMode, bool chatColors, byte skinParts, byte mainHand)
+ {
+ try
+ {
+ List fields = new List();
+ fields.AddRange(dataTypes.GetString(language));
+ fields.Add(viewDistance);
+ fields.AddRange(protocolversion >= MC19Version
+ ? dataTypes.GetVarInt(chatMode)
+ : new byte[] { chatMode });
+ fields.Add(chatColors ? (byte)1 : (byte)0);
+ if (protocolversion < MC18Version)
+ {
+ fields.Add(difficulty);
+ fields.Add((byte)(skinParts & 0x1)); //show cape
+ }
+ else fields.Add(skinParts);
+ if (protocolversion >= MC19Version)
+ fields.AddRange(dataTypes.GetVarInt(mainHand));
+ SendPacket(PacketOutgoingType.ClientSettings, fields);
+ }
+ catch (SocketException) { }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ return false;
+ }
+
+ ///
+ /// Send a location update to the server
+ ///
+ /// The new location of the player
+ /// True if the player is on the ground
+ /// Optional new yaw for updating player look
+ /// Optional new pitch for updating player look
+ /// True if the location update was successfully sent
+ public bool SendLocationUpdate(Location location, bool onGround, float? yaw = null, float? pitch = null)
+ {
+ if (handler.GetTerrainEnabled())
+ {
+ byte[] yawpitch = new byte[0];
+ PacketOutgoingType packetType = PacketOutgoingType.PlayerPosition;
+
+ if (yaw.HasValue && pitch.HasValue)
+ {
+ yawpitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), dataTypes.GetFloat(pitch.Value));
+ packetType = PacketOutgoingType.PlayerPositionAndLook;
+ }
+
+ try
+ {
+ SendPacket(packetType, dataTypes.ConcatBytes(
+ dataTypes.GetDouble(location.X),
+ dataTypes.GetDouble(location.Y),
+ protocolversion < MC18Version
+ ? dataTypes.GetDouble(location.Y + 1.62)
+ : new byte[0],
+ dataTypes.GetDouble(location.Z),
+ yawpitch,
+ new byte[] { onGround ? (byte)1 : (byte)0 }));
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+ else return false;
+ }
+
+ ///
+ /// Send a plugin channel packet (0x17) to the server, compression and encryption will be handled automatically
+ ///
+ /// Channel to send packet on
+ /// packet Data
+ public bool SendPluginChannelPacket(string channel, byte[] data)
+ {
+ try
+ {
+ // In 1.7, length needs to be included.
+ // In 1.8, it must not be.
+ if (protocolversion < MC18Version)
+ {
+ byte[] length = BitConverter.GetBytes((short)data.Length);
+ Array.Reverse(length);
+
+ SendPacket(PacketOutgoingType.PluginMessage, dataTypes.ConcatBytes(dataTypes.GetString(channel), length, data));
+ }
+ else
+ {
+ SendPacket(PacketOutgoingType.PluginMessage, dataTypes.ConcatBytes(dataTypes.GetString(channel), data));
+ }
+
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ ///
+ /// Send an Interact Entity Packet to server
+ ///
+ ///
+ ///
+ ///
+ public bool SendInteractEntity(int EntityID, int type)
+ {
+ try
+ {
+ List fields = new List();
+ fields.AddRange(dataTypes.GetVarInt(EntityID));
+ fields.AddRange(dataTypes.GetVarInt(type));
+ SendPacket(PacketOutgoingType.InteractEntity, fields);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ // TODO: Interact at block location (e.g. chest minecart)
+ public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z, int hand)
+ {
+ try
+ {
+ List fields = new List();
+ fields.AddRange(dataTypes.GetVarInt(EntityID));
+ fields.AddRange(dataTypes.GetVarInt(type));
+ fields.AddRange(dataTypes.GetFloat(X));
+ fields.AddRange(dataTypes.GetFloat(Y));
+ fields.AddRange(dataTypes.GetFloat(Z));
+ fields.AddRange(dataTypes.GetVarInt(hand));
+ SendPacket(PacketOutgoingType.InteractEntity, fields);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+ public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z)
+ {
+ return false;
+ }
+
+ public bool SendUseItem(int hand)
+ {
+ if (protocolversion < MC19Version)
+ return false; // Packet does not exist prior to MC 1.9
+ // According to https://wiki.vg/index.php?title=Protocol&oldid=5486#Player_Block_Placement
+ // MC 1.7 does this using Player Block Placement with special values
+ // TODO once Player Block Placement is implemented for older versions
+ try
+ {
+ List packet = new List();
+ packet.AddRange(dataTypes.GetVarInt(hand));
+ SendPacket(PacketOutgoingType.UseItem, packet);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ public bool SendPlayerDigging(int status, Location location, Direction face)
+ {
+ try
+ {
+ List packet = new List();
+ packet.AddRange(dataTypes.GetVarInt(status));
+ packet.AddRange(dataTypes.GetLocation(location));
+ packet.AddRange(dataTypes.GetVarInt(dataTypes.GetBlockFace(face)));
+ SendPacket(PacketOutgoingType.PlayerDigging, packet);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ public bool SendPlayerBlockPlacement(int hand, Location location, Direction face)
+ {
+ if (protocolversion < MC114Version)
+ return false; // NOT IMPLEMENTED for older MC versions
+ try
+ {
+ List packet = new List();
+ packet.AddRange(dataTypes.GetVarInt(hand));
+ packet.AddRange(dataTypes.GetLocation(location));
+ packet.AddRange(dataTypes.GetVarInt(dataTypes.GetBlockFace(face)));
+ packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorX
+ packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorY
+ packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorZ
+ packet.Add(0); // insideBlock = false;
+ SendPacket(PacketOutgoingType.PlayerBlockPlacement, packet);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ public bool SendHeldItemChange(short slot)
+ {
+ try
+ {
+ List packet = new List();
+ packet.AddRange(dataTypes.GetShort(slot));
+ SendPacket(PacketOutgoingType.HeldItemChange, packet);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+ public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item)
+ {
+ try
+ {
+ short actionNumber;
+ lock (window_actions)
+ {
+ if (!window_actions.ContainsKey(windowId))
+ window_actions[windowId] = 0;
+ actionNumber = (short)(window_actions[windowId] + 1);
+ window_actions[windowId] = actionNumber;
+ }
+
+ byte button = 0;
+ byte mode = 0;
+ switch (action)
+ {
+ case WindowActionType.LeftClick: button = 0; break;
+ case WindowActionType.RightClick: button = 1; break;
+ case WindowActionType.MiddleClick: button = 2; mode = 3; break;
+ case WindowActionType.DropItem:
+ button = 0;
+ mode = 4;
+ item = new Item(-1, 0, null);
+ Container inventory = handler.GetInventory(windowId);
+ if (inventory.Items.ContainsKey(slotId))
+ inventory.Items[slotId].Count--; // server won't update us after dropped
+ if (inventory.Items[slotId].Count == 0)
+ inventory.Items.Remove(slotId);
+ break;
+ case WindowActionType.DropItemStack:
+ button = 1;
+ mode = 4;
+ item = new Item(-1, 0, null);
+ inventory = handler.GetInventory(windowId);
+ inventory.Items.Remove(slotId); // server won't update us after dropped
+ break;
+ }
+
+ List packet = new List();
+ packet.Add((byte)windowId);
+ packet.AddRange(dataTypes.GetShort((short)slotId));
+ packet.Add(button);
+ packet.AddRange(dataTypes.GetShort(actionNumber));
+
+ if (protocolversion >= MC19Version)
+ packet.AddRange(dataTypes.GetVarInt(mode));
+ else packet.Add(mode);
+
+ packet.AddRange(dataTypes.GetItemSlot(item));
+
+ SendPacket(PacketOutgoingType.ClickWindow, packet);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ public bool SendCreativeInventoryAction(int slot, ItemType itemType, int count, Dictionary nbt)
+ {
+ try
+ {
+ List packet = new List();
+ packet.AddRange(dataTypes.GetShort((short)slot));
+ packet.AddRange(dataTypes.GetItemSlot(new Item((int)itemType, count, nbt)));
+ SendPacket(PacketOutgoingType.CreativeInventoryAction, packet);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ public bool SendAnimation(int animation, int playerid)
+ {
+ try
+ {
+ if (animation == 0 || animation == 1)
+ {
+ List packet = new List();
+
+ if (protocolversion < MC18Version)
+ {
+ packet.AddRange(dataTypes.GetInt(playerid));
+ packet.Add((byte)1); // Swing arm
+ }
+ else if (protocolversion < MC19Version)
+ {
+ // No fields in 1.8.X
+ }
+ else // MC 1.9+
+ {
+ packet.AddRange(dataTypes.GetVarInt(animation));
+ }
+
+ SendPacket(PacketOutgoingType.Animation, packet);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ public bool SendCloseWindow(int windowId)
+ {
+ try
+ {
+ lock (window_actions)
+ {
+ if (window_actions.ContainsKey(windowId))
+ window_actions[windowId] = 0;
+ }
+ SendPacket(PacketOutgoingType.CloseWindow, new[] { (byte)windowId });
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+
+ public bool SendUpdateSign(Location sign, string line1, string line2, string line3, string line4)
+ {
+ try
+ {
+ if (line1.Length > 23)
+ line1 = line1.Substring(0, 23);
+ if (line2.Length > 23)
+ line2 = line1.Substring(0, 23);
+ if (line3.Length > 23)
+ line3 = line1.Substring(0, 23);
+ if (line4.Length > 23)
+ line4 = line1.Substring(0, 23);
+
+ List packet = new List();
+ packet.AddRange(dataTypes.GetLocation(sign));
+ packet.AddRange(dataTypes.GetString(line1));
+ packet.AddRange(dataTypes.GetString(line2));
+ packet.AddRange(dataTypes.GetString(line3));
+ packet.AddRange(dataTypes.GetString(line4));
+ SendPacket(PacketOutgoingType.UpdateSign, packet);
+ return true;
+ }
+ catch (SocketException) { return false; }
+ catch (System.IO.IOException) { return false; }
+ catch (ObjectDisposedException) { return false; }
+ }
+ }
}