From 93112d2c02ad4a3387a5dc1788bf704493cb6a4e Mon Sep 17 00:00:00 2001 From: Anon Date: Fri, 17 Nov 2023 18:21:57 +0100 Subject: [PATCH] Implemented 1.20.2 fully, needs more testing --- MinecraftClient/ChatBots/Farmer.cs | 2 +- .../Protocol/Handlers/DataTypes.cs | 102 +- .../Protocol/Handlers/Protocol18.cs | 5090 +++++++++-------- 3 files changed, 2662 insertions(+), 2532 deletions(-) diff --git a/MinecraftClient/ChatBots/Farmer.cs b/MinecraftClient/ChatBots/Farmer.cs index 57232253..f072ba2e 100644 --- a/MinecraftClient/ChatBots/Farmer.cs +++ b/MinecraftClient/ChatBots/Farmer.cs @@ -786,7 +786,7 @@ namespace MinecraftClient.ChatBots { var playerInventory = GetPlayerInventory(); - if (playerInventory.Items.TryGetValue(GetCurrentSlot() - 36, out Item value) && value.Type == itemType) + if (playerInventory.Items.TryGetValue(GetCurrentSlot() - 36, out var value) && value.Type == itemType) return true; // Already selected // Search the full inventory diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index 78e594a1..f5ad9566 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -425,10 +425,10 @@ namespace MinecraftClient.Protocol.Handlers if (itemPresent) { int itemID = ReadNextVarInt(cache); - + if (itemID == -1) return null; - + ItemType type = itemPalette.FromId(itemID); byte itemCount = ReadNextByte(cache); Dictionary nbt = ReadNextNbt(cache); @@ -440,15 +440,15 @@ namespace MinecraftClient.Protocol.Handlers { // MC 1.13 and lower short itemID = ReadNextShort(cache); - + if (itemID == -1) return null; - + byte itemCount = ReadNextByte(cache); - - if(protocolversion < Protocol18Handler.MC_1_13_Version) + + if (protocolversion < Protocol18Handler.MC_1_13_Version) ReadNextShort(cache); - + Dictionary nbt = ReadNextNbt(cache); return new Item(itemPalette.FromId(itemID), itemCount, nbt); } @@ -501,7 +501,7 @@ namespace MinecraftClient.Protocol.Handlers entityY = ReadNextDouble(cache); // Y entityZ = ReadNextDouble(cache); // Z } - + int data = -1; byte entityPitch, entityYaw; @@ -520,8 +520,9 @@ namespace MinecraftClient.Protocol.Handlers entityYaw = ReadNextByte(cache); // Head Yaw // Data - data = protocolversion >= Protocol18Handler.MC_1_19_Version - ? ReadNextVarInt(cache) : ReadNextInt(cache); + data = protocolversion >= Protocol18Handler.MC_1_19_Version + ? ReadNextVarInt(cache) + : ReadNextInt(cache); } // In 1.8 those 3 fields for Velocity are optional @@ -541,7 +542,8 @@ namespace MinecraftClient.Protocol.Handlers ReadNextShort(cache); } - return new Entity(entityID, entityType, new Location(entityX, entityY, entityZ), entityYaw, entityPitch, data); + return new Entity(entityID, entityType, new Location(entityX, entityY, entityZ), entityYaw, entityPitch, + data); } /// @@ -563,11 +565,14 @@ namespace MinecraftClient.Protocol.Handlers 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 (protocolversion < Protocol18Handler.MC_1_20_2_Version) + { + // NBT root name + var rootName = Encoding.ASCII.GetString(ReadData(ReadNextUShort(cache), cache)); - if (!String.IsNullOrEmpty(rootName)) - nbtData[""] = rootName; + if (!string.IsNullOrEmpty(rootName)) + nbtData[""] = rootName; + } } while (true) @@ -646,12 +651,13 @@ namespace MinecraftClient.Protocol.Handlers /// /// /// - public Dictionary ReadNextMetadata(Queue cache, ItemPalette itemPalette, EntityMetadataPalette metadataPalette) + public Dictionary ReadNextMetadata(Queue cache, ItemPalette itemPalette, + EntityMetadataPalette metadataPalette) { Dictionary data = new(); byte key = ReadNextByte(cache); byte terminteValue = protocolversion <= Protocol18Handler.MC_1_8_Version - ? (byte)0x7f // 1.8 (https://wiki.vg/index.php?title=Entity_metadata&oldid=6220#Entity_Metadata_Format) + ? (byte)0x7f // 1.8 (https://wiki.vg/index.php?title=Entity_metadata&oldid=6220#Entity_Metadata_Format) : (byte)0xff; // 1.9+ while (key != terminteValue) @@ -659,8 +665,8 @@ namespace MinecraftClient.Protocol.Handlers int typeId = protocolversion <= Protocol18Handler.MC_1_8_Version ? key >> 5 // 1.8 : ReadNextVarInt(cache); // 1.9+ - - + + EntityMetaDataType type; try { @@ -668,9 +674,10 @@ namespace MinecraftClient.Protocol.Handlers } catch (KeyNotFoundException) { - throw new System.IO.InvalidDataException("Unknown Metadata Type ID " + typeId + ". Is this up to date for new MC Version?"); + throw new System.IO.InvalidDataException("Unknown Metadata Type ID " + typeId + + ". Is this up to date for new MC Version?"); } - + if (protocolversion <= Protocol18Handler.MC_1_8_Version) key = (byte)(key & 0x1f); @@ -737,6 +744,7 @@ namespace MinecraftClient.Protocol.Handlers { value = ReadNextLocation(cache); } + break; case EntityMetaDataType.Direction: // Direction (VarInt) value = ReadNextVarInt(cache); @@ -746,6 +754,7 @@ namespace MinecraftClient.Protocol.Handlers { value = ReadNextUUID(cache); } + break; case EntityMetaDataType.BlockId: // BlockID (VarInt) value = ReadNextVarInt(cache); @@ -773,6 +782,7 @@ namespace MinecraftClient.Protocol.Handlers { value = ReadNextVarInt(cache); } + break; case EntityMetaDataType.Pose: // Pose value = ReadNextVarInt(cache); @@ -795,6 +805,7 @@ namespace MinecraftClient.Protocol.Handlers // Dimension and blockPos, currently not in use value = new Tuple(ReadNextString(cache), ReadNextLocation(cache)); } + break; case EntityMetaDataType.PaintingVariant: // Painting Variant value = ReadNextVarInt(cache); @@ -824,6 +835,7 @@ namespace MinecraftClient.Protocol.Handlers data[key] = value; key = ReadNextByte(cache); } + return data; } @@ -856,7 +868,8 @@ namespace MinecraftClient.Protocol.Handlers case 3: if (protocolversion < Protocol18Handler.MC_1_17_Version || protocolversion > Protocol18Handler.MC_1_17_1_Version) - ReadNextVarInt(cache); // Block State (minecraft:block before 1.18, minecraft:block_marker after 1.18) + ReadNextVarInt( + cache); // Block State (minecraft:block before 1.18, minecraft:block_marker after 1.18) break; case 4: if (protocolversion == Protocol18Handler.MC_1_17_Version @@ -870,38 +883,44 @@ namespace MinecraftClient.Protocol.Handlers break; case 14: // 1.15 - 1.16.5 and 1.18 - 1.19.4 - if ((protocolversion >= Protocol18Handler.MC_1_15_Version && protocolversion < Protocol18Handler.MC_1_17_Version) + if ((protocolversion >= Protocol18Handler.MC_1_15_Version && + protocolversion < Protocol18Handler.MC_1_17_Version) || protocolversion > Protocol18Handler.MC_1_17_1_Version) ReadDustParticle(cache); break; case 15: - if (protocolversion == Protocol18Handler.MC_1_17_Version || protocolversion == Protocol18Handler.MC_1_17_1_Version) + if (protocolversion == Protocol18Handler.MC_1_17_Version || + protocolversion == Protocol18Handler.MC_1_17_1_Version) ReadDustParticle(cache); else { if (protocolversion > Protocol18Handler.MC_1_17_1_Version) ReadDustParticleColorTransition(cache); } + break; case 16: - if (protocolversion == Protocol18Handler.MC_1_17_Version || protocolversion == Protocol18Handler.MC_1_17_1_Version) + if (protocolversion == Protocol18Handler.MC_1_17_Version || + protocolversion == Protocol18Handler.MC_1_17_1_Version) ReadDustParticleColorTransition(cache); break; case 23: // 1.15 - 1.16.5 - if (protocolversion >= Protocol18Handler.MC_1_15_Version && protocolversion < Protocol18Handler.MC_1_17_Version) + if (protocolversion >= Protocol18Handler.MC_1_15_Version && + protocolversion < Protocol18Handler.MC_1_17_Version) ReadNextVarInt(cache); // Block State (minecraft:falling_dust) break; case 24: // 1.18 - 1.19.2 onwards - if (protocolversion > Protocol18Handler.MC_1_17_1_Version && protocolversion < Protocol18Handler.MC_1_19_3_Version) + if (protocolversion > Protocol18Handler.MC_1_17_1_Version && + protocolversion < Protocol18Handler.MC_1_19_3_Version) ReadNextVarInt(cache); // Block State (minecraft:falling_dust) break; case 25: // 1.17 - 1.17.1 and 1.19.3 onwards if (protocolversion == Protocol18Handler.MC_1_17_Version - || protocolversion == Protocol18Handler.MC_1_17_1_Version - || protocolversion >= Protocol18Handler.MC_1_19_3_Version) + || protocolversion == Protocol18Handler.MC_1_17_1_Version + || protocolversion >= Protocol18Handler.MC_1_19_3_Version) ReadNextVarInt(cache); // Block State (minecraft:falling_dust) break; case 27: @@ -915,27 +934,31 @@ namespace MinecraftClient.Protocol.Handlers break; case 32: // 1.15 - 1.16.5 - if (protocolversion >= Protocol18Handler.MC_1_15_Version && protocolversion < Protocol18Handler.MC_1_17_Version) + if (protocolversion >= Protocol18Handler.MC_1_15_Version && + protocolversion < Protocol18Handler.MC_1_17_Version) ReadNextItemSlot(cache, itemPalette); // Item (minecraft:item) break; case 36: // 1.17 - 1.17.1 - if (protocolversion == Protocol18Handler.MC_1_17_Version || protocolversion == Protocol18Handler.MC_1_17_1_Version) + if (protocolversion == Protocol18Handler.MC_1_17_Version || + protocolversion == Protocol18Handler.MC_1_17_1_Version) { ReadNextItemSlot(cache, itemPalette); // Item (minecraft:item) } - else if (protocolversion > Protocol18Handler.MC_1_17_1_Version && protocolversion < Protocol18Handler.MC_1_19_3_Version) + else if (protocolversion > Protocol18Handler.MC_1_17_1_Version && + protocolversion < Protocol18Handler.MC_1_19_3_Version) { // minecraft:vibration ReadNextLocation(cache); // Origin (Starting Position) ReadNextLocation(cache); // Desitination (Ending Position) ReadNextVarInt(cache); // Ticks } + break; case 37: // minecraft:vibration if (protocolversion == Protocol18Handler.MC_1_17_Version - || protocolversion == Protocol18Handler.MC_1_17_1_Version) + || protocolversion == Protocol18Handler.MC_1_17_1_Version) { ReadNextDouble(cache); // Origin X ReadNextDouble(cache); // Origin Y @@ -945,6 +968,7 @@ namespace MinecraftClient.Protocol.Handlers ReadNextDouble(cache); // Destination Z ReadNextInt(cache); // Ticks } + break; case 39: if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) @@ -966,6 +990,7 @@ namespace MinecraftClient.Protocol.Handlers ReadNextVarInt(cache); } + break; } } @@ -1186,7 +1211,8 @@ namespace MinecraftClient.Protocol.Handlers } else { - throw new System.IO.InvalidDataException("GetNbt: Cannot encode data type " + obj.GetType().Name + " into NBT!"); + throw new System.IO.InvalidDataException("GetNbt: Cannot encode data type " + obj.GetType().Name + + " into NBT!"); } } @@ -1349,13 +1375,13 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion >= Protocol18Handler.MC_1_14_Version) { locationBytes = BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | - ((((ulong)location.Z) & 0x3FFFFFF) << 12) | - (((ulong)location.Y) & 0xFFF)); + ((((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)); + ((((ulong)location.Y) & 0xFFF) << 26) | + (((ulong)location.Z) & 0x3FFFFFF)); Array.Reverse(locationBytes); //Endianness return locationBytes; diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index cfd0a988..90268aae 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -72,7 +72,7 @@ namespace MinecraftClient.Protocol.Handlers private int compression_treshold = 0; private int autocomplete_transaction_id = 0; private readonly Dictionary window_actions = new(); - private bool login_phase = true; + private CurrentState currentState = CurrentState.Login; private readonly int protocolVersion; private int currentDimension; private bool isOnlineMode = false; @@ -116,32 +116,32 @@ namespace MinecraftClient.Protocol.Handlers randomGen = RandomNumberGenerator.Create(); lastSeenMessagesCollector = protocolVersion >= MC_1_19_3_Version ? new(20) : new(5); - if (handler.GetTerrainEnabled() && protocolVersion > MC_1_20_Version) + if (handler.GetTerrainEnabled() && protocolVersion > MC_1_20_2_Version) { - log.Error("§c" + Translations.extra_terrainandmovement_disabled); + log.Error($"§c{Translations.extra_terrainandmovement_disabled}"); handler.SetTerrainEnabled(false); } if (handler.GetInventoryEnabled() && - protocolVersion is < MC_1_9_Version or > MC_1_20_Version) + protocolVersion is < MC_1_9_Version or > MC_1_20_2_Version) { - log.Error("§c" + Translations.extra_inventory_disabled); + log.Error($"§c{Translations.extra_inventory_disabled}"); handler.SetInventoryEnabled(false); } if (handler.GetEntityHandlingEnabled() && - protocolVersion is < MC_1_8_Version or > MC_1_20_Version) + protocolVersion is < MC_1_8_Version or > MC_1_20_2_Version) { - log.Error("§c" + Translations.extra_entity_disabled); + log.Error($"§c{Translations.extra_entity_disabled}"); handler.SetEntityHandlingEnabled(false); } Block.Palette = protocolVersion switch { // Block palette - > MC_1_20_Version when handler.GetTerrainEnabled() => + > MC_1_20_2_Version when handler.GetTerrainEnabled() => throw new NotImplementedException(Translations.exception_palette_block), - MC_1_20_Version => new Palette120(), + >= MC_1_20_Version => new Palette120(), MC_1_19_4_Version => new Palette1194(), MC_1_19_3_Version => new Palette1193(), >= MC_1_19_Version => new Palette119(), @@ -156,9 +156,9 @@ namespace MinecraftClient.Protocol.Handlers entityPalette = protocolVersion switch { // Entity palette - > MC_1_20_Version when handler.GetEntityHandlingEnabled() => + > MC_1_20_2_Version when handler.GetEntityHandlingEnabled() => throw new NotImplementedException(Translations.exception_palette_entity), - MC_1_20_Version => new EntityPalette120(), + >= MC_1_20_Version => new EntityPalette120(), MC_1_19_4_Version => new EntityPalette1194(), MC_1_19_3_Version => new EntityPalette1193(), >= MC_1_19_Version => new EntityPalette119(), @@ -177,9 +177,9 @@ namespace MinecraftClient.Protocol.Handlers itemPalette = protocolVersion switch { // Item palette - > MC_1_20_Version when handler.GetInventoryEnabled() => + > MC_1_20_2_Version when handler.GetInventoryEnabled() => throw new NotImplementedException(Translations.exception_palette_item), - MC_1_20_Version => new ItemPalette120(), + >= MC_1_20_Version => new ItemPalette120(), MC_1_19_4_Version => new ItemPalette1194(), MC_1_19_3_Version => new ItemPalette1193(), >= MC_1_19_Version => new ItemPalette119(), @@ -190,10 +190,11 @@ namespace MinecraftClient.Protocol.Handlers _ => new ItemPalette115() }; - // MessageType - // You can find it in https://wiki.vg/Protocol#Player_Chat_Message or /net/minecraft/network/message/MessageType.java - if (this.protocolVersion >= MC_1_19_2_Version) - ChatParser.ChatId2Type = new() + ChatParser.ChatId2Type = this.protocolVersion switch + { + // MessageType + // You can find it in https://wiki.vg/Protocol#Player_Chat_Message or /net/minecraft/network/message/MessageType.java + >= MC_1_19_2_Version => new() { { 0, ChatParser.MessageType.CHAT }, { 1, ChatParser.MessageType.SAY_COMMAND }, @@ -202,9 +203,8 @@ namespace MinecraftClient.Protocol.Handlers { 4, ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING }, { 5, ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING }, { 6, ChatParser.MessageType.EMOTE_COMMAND }, - }; - else if (this.protocolVersion == MC_1_19_Version) - ChatParser.ChatId2Type = new() + }, + MC_1_19_Version => new() { { 0, ChatParser.MessageType.CHAT }, { 1, ChatParser.MessageType.RAW_MSG }, @@ -214,7 +214,9 @@ namespace MinecraftClient.Protocol.Handlers { 5, ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING }, { 6, ChatParser.MessageType.EMOTE_COMMAND }, { 7, ChatParser.MessageType.RAW_MSG }, - }; + }, + _ => ChatParser.ChatId2Type + }; } /// @@ -222,7 +224,7 @@ namespace MinecraftClient.Protocol.Handlers /// private void Updater(object? o) { - CancellationToken cancelToken = (CancellationToken)o!; + var cancelToken = (CancellationToken)o!; if (cancelToken.IsCancellationRequested) return; @@ -237,19 +239,18 @@ namespace MinecraftClient.Protocol.Handlers handler.OnUpdate(); stopWatch.Restart(); - while (packetQueue.TryTake(out Tuple>? packetInfo)) + while (packetQueue.TryTake(out var packetInfo)) { - (int packetID, Queue packetData) = packetInfo; - HandlePacket(packetID, packetData); + var (packetId, packetData) = packetInfo; + HandlePacket(packetId, packetData); - if (stopWatch.Elapsed.Milliseconds >= 100) - { - handler.OnUpdate(); - stopWatch.Restart(); - } + if (stopWatch.Elapsed.Milliseconds < 100) continue; + + handler.OnUpdate(); + stopWatch.Restart(); } - int sleepLength = 100 - stopWatch.Elapsed.Milliseconds; + var sleepLength = 100 - stopWatch.Elapsed.Milliseconds; if (sleepLength > 0) Thread.Sleep(sleepLength); } @@ -275,14 +276,14 @@ namespace MinecraftClient.Protocol.Handlers /// internal void PacketReader(object? o) { - CancellationToken cancelToken = (CancellationToken)o!; + var cancelToken = (CancellationToken)o!; while (socketWrapper.IsConnected() && !cancelToken.IsCancellationRequested) { try { while (socketWrapper.HasDataAvailable()) { - packetQueue.Add(ReadNextPacket()); + packetQueue.Add(ReadNextPacket(), cancelToken); if (cancelToken.IsCancellationRequested) break; @@ -317,2158 +318,2247 @@ namespace MinecraftClient.Protocol.Handlers /// /// Read the next packet from the network /// - /// will contain packet ID + /// will contain packet ID /// will contain raw packet Data internal Tuple> ReadNextPacket() { - int size = dataTypes.ReadNextVarIntRAW(socketWrapper); //Packet size + var size = dataTypes.ReadNextVarIntRAW(socketWrapper); //Packet size Queue packetData = new(socketWrapper.ReadDataRAW(size)); //Packet contents //Handle packet decompression if (protocolVersion >= MC_1_8_Version && compression_treshold > 0) { - int sizeUncompressed = dataTypes.ReadNextVarInt(packetData); + var sizeUncompressed = dataTypes.ReadNextVarInt(packetData); if (sizeUncompressed != 0) // != 0 means compressed, let's decompress { - byte[] toDecompress = packetData.ToArray(); - byte[] uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed); + var toDecompress = packetData.ToArray(); + var uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed); packetData = new(uncompressed); } } - int packetID = dataTypes.ReadNextVarInt(packetData); //Packet ID - + var packetId = dataTypes.ReadNextVarInt(packetData); // Packet ID if (handler.GetNetworkPacketCaptureEnabled()) - { - List clone = packetData.ToList(); - handler.OnNetworkPacket(packetID, clone, login_phase, true); - } + handler.OnNetworkPacket(packetId, packetData.ToList(), currentState == CurrentState.Login, true); - return new(packetID, packetData); + return new(packetId, packetData); } /// /// Handle the given packet /// - /// Packet ID + /// Packet ID /// Packet contents /// TRUE if the packet was processed, FALSE if ignored or unknown - internal bool HandlePacket(int packetID, Queue packetData) + internal bool HandlePacket(int packetId, Queue packetData) { try { - if (login_phase) + switch (currentState) { - switch (packetID) //Packet IDs are different while logging in - { - case 0x03: - if (protocolVersion >= MC_1_8_Version) - compression_treshold = dataTypes.ReadNextVarInt(packetData); - break; - case 0x04: - int messageId = dataTypes.ReadNextVarInt(packetData); - string channel = dataTypes.ReadNextString(packetData); - List responseData = new(); - bool understood = pForge.HandleLoginPluginRequest(channel, packetData, ref responseData); - SendLoginPluginResponse(messageId, understood, responseData.ToArray()); - return understood; - default: - return false; //Ignored packet - } - } - // Regular in-game packets - else - switch (packetPalette.GetIncommingTypeById(packetID)) - { - case PacketTypesIn.KeepAlive: - SendPacket(PacketTypesOut.KeepAlive, packetData); - handler.OnServerKeepAlive(); - break; - case PacketTypesIn.Ping: - SendPacket(PacketTypesOut.Pong, packetData); - break; - case PacketTypesIn.JoinGame: + // https://wiki.vg/Protocol#Login + case CurrentState.Login: + switch (packetId) { - // Temporary fix - log.Debug("Receive JoinGame"); - - receiveDeclareCommands = receivePlayerInfo = false; - - messageIndex = 0; - pendingAcknowledgments = 0; - - lastReceivedMessage = null; - lastSeenMessagesCollector = protocolVersion >= MC_1_19_3_Version ? new(20) : new(5); - } - handler.OnGameJoined(isOnlineMode); - - var playerEntityId = dataTypes.ReadNextInt(packetData); - handler.OnReceivePlayerEntityID(playerEntityId); - - if (protocolVersion >= MC_1_16_2_Version) - dataTypes.ReadNextBool(packetData); // Is hardcore - 1.16.2 and above - - if (protocolVersion < MC_1_20_2_Version) - handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData)); - - if (protocolVersion >= MC_1_16_Version) - { - if (protocolVersion < MC_1_20_2_Version) - dataTypes.ReadNextByte(packetData); // Previous Gamemode - 1.16 - 1.20.2 - - var worldCount = - dataTypes.ReadNextVarInt( - packetData); // Dimension Count (World Count) - 1.16 and above - for (var i = 0; i < worldCount; i++) - dataTypes.ReadNextString( - packetData); // Dimension Names (World Names) - 1.16 and above - - if (protocolVersion < MC_1_20_2_Version) - { - var registryCodec = - dataTypes.ReadNextNbt( - packetData); // Registry Codec (Dimension Codec) - 1.16 and above - if (protocolVersion >= MC_1_19_Version) - ChatParser.ReadChatType(registryCodec); - if (handler.GetTerrainEnabled()) - World.StoreDimensionList(registryCodec); - } - } - - if (protocolVersion < MC_1_20_2_Version) - { - // Current dimension - // String: 1.19 and above - // NBT Tag Compound: [1.16.2 to 1.18.2] - // String identifier: 1.16 and 1.16.1 - // varInt: [1.9.1 to 1.15.2] - // byte: below 1.9.1 - string? dimensionTypeName = null; - Dictionary? dimensionType = null; - switch (protocolVersion) - { - case >= MC_1_16_Version: - { - switch (protocolVersion) - { - case >= MC_1_19_Version: - dimensionTypeName = - dataTypes.ReadNextString(packetData); // Dimension Type: Identifier - break; - case >= MC_1_16_2_Version: - dimensionType = - dataTypes.ReadNextNbt( - packetData); // Dimension Type: NBT Tag Compound - break; - default: - dataTypes.ReadNextString(packetData); - break; - } - - currentDimension = 0; - break; - } - case >= MC_1_9_1_Version: - currentDimension = dataTypes.ReadNextInt(packetData); - break; - default: - currentDimension = (sbyte)dataTypes.ReadNextByte(packetData); - break; - } - - switch (protocolVersion) - { - case < MC_1_14_Version: - dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below - break; - case >= MC_1_16_Version: - { - var dimensionName = - dataTypes.ReadNextString( - packetData); // Dimension Name (World Name) - 1.16 and above - - if (handler.GetTerrainEnabled()) - { - switch (protocolVersion) - { - case >= MC_1_16_2_Version and <= MC_1_18_2_Version: - World.StoreOneDimension(dimensionName, dimensionType!); - World.SetDimension(dimensionName); - break; - default: - World.SetDimension(dimensionTypeName!); - break; - } - } - - break; - } - } - } - - if (protocolVersion is >= MC_1_15_Version and < MC_1_20_2_Version) - dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 - 1.20.2 - if (protocolVersion >= MC_1_16_2_Version) - dataTypes.ReadNextVarInt(packetData); // Max Players - 1.16.2 and above - else - dataTypes.ReadNextByte(packetData); // Max Players - 1.16.1 and below - if (protocolVersion < MC_1_16_Version) - dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below - if (protocolVersion >= MC_1_14_Version) - dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above - if (protocolVersion >= MC_1_18_1_Version) - dataTypes.ReadNextVarInt(packetData); // Simulation Distance - 1.18 and above - if (protocolVersion >= MC_1_8_Version) - dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above - if (protocolVersion >= MC_1_15_Version) - dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above - - if (protocolVersion < MC_1_20_2_Version) - { - if (protocolVersion >= MC_1_16_Version) - { - dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and 1.20.2 - dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and 1.20.2 - } - - if (protocolVersion >= MC_1_19_Version) - { - if (dataTypes.ReadNextBool(packetData)) // Has death location - { - dataTypes.SkipNextString(packetData); // Death dimension name: Identifier - dataTypes.ReadNextLocation(packetData); // Death location - } - } - - if (protocolVersion >= MC_1_20_Version) - dataTypes.ReadNextVarInt(packetData); // Portal Cooldown - 1.20 and above - } - else - { - dataTypes.ReadNextBool(packetData); // Do limited crafting - var dimensionTypeName = - dataTypes.ReadNextString(packetData); // Dimension Type: Identifier - dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above - - if (handler.GetTerrainEnabled()) - World.SetDimension(dimensionTypeName); - - dataTypes.ReadNextLong(packetData); // Hashed world seed - handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData)); - dataTypes.ReadNextByte(packetData); // Previous Gamemode - dataTypes.ReadNextBool(packetData); // Is Debug - dataTypes.ReadNextBool(packetData); // Is Flat - var hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location - if (hasDeathLocation) - { - dataTypes.SkipNextString(packetData); // Death dimension name: Identifier - dataTypes.ReadNextLocation(packetData); // Death location - } - - dataTypes.ReadNextVarInt(packetData); // Portal Cooldown - } - - break; - case PacketTypesIn.SpawnPainting: // Just skip, no need for this - return true; - case PacketTypesIn.DeclareCommands: - if (protocolVersion >= MC_1_19_Version) - { - log.Debug("Receive DeclareCommands"); - DeclareCommands.Read(dataTypes, packetData, protocolVersion); - receiveDeclareCommands = true; - if (receivePlayerInfo) - handler.SetCanSendMessage(true); - } - - break; - case PacketTypesIn.ChatMessage: - int messageType = 0; - - if (protocolVersion <= MC_1_18_2_Version) // 1.18 and bellow - { - string message = dataTypes.ReadNextString(packetData); - - Guid senderUUID; + // Set Compression + case 0x03: if (protocolVersion >= MC_1_8_Version) - { - //Hide system messages or xp bar messages? - messageType = dataTypes.ReadNextByte(packetData); - if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) - || (messageType == 2 && !Config.Main.Advanced.ShowSystemMessages)) - break; - - if (protocolVersion >= MC_1_16_5_Version) - senderUUID = dataTypes.ReadNextUUID(packetData); - else senderUUID = Guid.Empty; - } - else - senderUUID = Guid.Empty; - - handler.OnTextReceived(new(message, null, true, messageType, senderUUID)); - } - else if (protocolVersion == MC_1_19_Version) // 1.19 - { - string signedChat = dataTypes.ReadNextString(packetData); - - bool hasUnsignedChatContent = dataTypes.ReadNextBool(packetData); - string? unsignedChatContent = - hasUnsignedChatContent ? dataTypes.ReadNextString(packetData) : null; - - messageType = dataTypes.ReadNextVarInt(packetData); - if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) - || (messageType == 2 && !Config.Main.Advanced.ShowXPBarMessages)) - break; - - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - string senderDisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - - bool hasSenderTeamName = dataTypes.ReadNextBool(packetData); - string? senderTeamName = hasSenderTeamName - ? ChatParser.ParseText(dataTypes.ReadNextString(packetData)) - : null; - - long timestamp = dataTypes.ReadNextLong(packetData); - - long salt = dataTypes.ReadNextLong(packetData); - - byte[] messageSignature = dataTypes.ReadNextByteArray(packetData); - - bool verifyResult; - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - verifyResult = player != null && player.VerifyMessage(signedChat, timestamp, salt, - ref messageSignature); - } - - ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, - senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - handler.OnTextReceived(chat); - } - else if (protocolVersion == MC_1_19_2_Version) - { - // 1.19.1 - 1.19.2 - byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextByteArray(packetData) - : null; - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - - string signedChat = dataTypes.ReadNextString(packetData); - string? decorated = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextString(packetData) - : null; - - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); - LastSeenMessageList.AcknowledgedMessage[] lastSeenMessageList = - new LastSeenMessageList.AcknowledgedMessage[lastSeenMessageListLen]; - for (int i = 0; i < lastSeenMessageListLen; ++i) - { - Guid user = dataTypes.ReadNextUUID(packetData); - byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); - lastSeenMessageList[i] = new(user, lastSignature, true); - } - - LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); - - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextString(packetData) - : null; - - MessageFilterType filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); - if (filterEnum == MessageFilterType.PartiallyFiltered) - dataTypes.ReadNextULongArray(packetData); - - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextString(packetData) - : null; - - Dictionary chatInfo = Json.ParseJson(chatName).Properties; - string senderDisplayName = - (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]) - .StringValue; - string? senderTeamName = null; - ChatParser.MessageType messageTypeEnum = - ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || - messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0] - .Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && - string.IsNullOrWhiteSpace(senderDisplayName)) - { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; - } - } - - bool verifyResult; - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player == null || !player.IsMessageChainLegal()) - verifyResult = false; - else - { - bool lastVerifyResult = player.IsMessageChainLegal(); - verifyResult = player.VerifyMessage(signedChat, timestamp, salt, - ref headerSignature, ref precedingSignature, lastSeenMessages); - if (lastVerifyResult && !verifyResult) - log.Warn(string.Format(Translations.chat_message_chain_broken, - senderDisplayName)); - } - } - - ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, - senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender()) - Acknowledge(chat); - handler.OnTextReceived(chat); - } - else if (protocolVersion >= MC_1_19_3_Version) - { - // 1.19.3+ - // Header section - // net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket#write - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - int index = dataTypes.ReadNextVarInt(packetData); - // Signature is fixed size of 256 bytes - byte[]? messageSignature = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextByteArray(packetData, 256) - : null; - - // Body - // net.minecraft.network.message.MessageBody.Serialized#write - string message = dataTypes.ReadNextString(packetData); - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - // Previous Messages - // net.minecraft.network.message.LastSeenMessageList.Indexed#write - // net.minecraft.network.message.MessageSignatureData.Indexed#write - int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); - Tuple[] previousMessageSignatures = - new Tuple[totalPreviousMessages]; - for (int i = 0; i < totalPreviousMessages; i++) - { - // net.minecraft.network.message.MessageSignatureData.Indexed#fromBuf - int messageId = dataTypes.ReadNextVarInt(packetData) - 1; - if (messageId == -1) - previousMessageSignatures[i] = new Tuple(messageId, - dataTypes.ReadNextByteArray(packetData, 256)); - else - previousMessageSignatures[i] = new Tuple(messageId, null); - } - - // Other - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextString(packetData) - : null; - - MessageFilterType filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); - - if (filterType == MessageFilterType.PartiallyFiltered) - dataTypes.ReadNextULongArray(packetData); - - // Network Target - // net.minecraft.network.message.MessageType.Serialized#write - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextString(packetData) - : null; - - ChatParser.MessageType messageTypeEnum = - ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - - Dictionary chatInfo = - Json.ParseJson(targetName ?? chatName).Properties; - string senderDisplayName = - (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]) - .StringValue; - string? senderTeamName = null; - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || - messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0] - .Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && - string.IsNullOrWhiteSpace(senderDisplayName)) - { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; - } - } - - bool verifyResult; - if (!isOnlineMode || messageSignature == null) - verifyResult = false; - else - { - if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - var player = handler.GetPlayerInfo(senderUUID); - if (player == null || !player.IsMessageChainLegal()) - verifyResult = false; - else - { - verifyResult = player.VerifyMessage(message, senderUUID, player.ChatUuid, - index, timestamp, salt, ref messageSignature, - previousMessageSignatures); - } - } - } - - ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, - senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - lock (MessageSigningLock) - Acknowledge(chat); - handler.OnTextReceived(chat); - } - - break; - case PacketTypesIn.ChunkBatchFinished: - dataTypes.ReadNextVarInt(packetData); // Number of chunks received - SendChunkBatchReceived( - // ReSharper disable once PossibleLossOfFraction - 25 / (DateTimeOffset.Now.ToUnixTimeMilliseconds() - lastCHunkBatchStartedAt) - ); - break; - case PacketTypesIn.ChunkBatchStarted: - lastCHunkBatchStartedAt = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - break; - case PacketTypesIn.StartConfiguration: - SendAcknowledgeConfiguration(); - break; - case PacketTypesIn.HideMessage: - byte[] hideMessageSignature = dataTypes.ReadNextByteArray(packetData); - ConsoleIO.WriteLine( - $"HideMessage was not processed! (SigLen={hideMessageSignature.Length})"); - break; - case PacketTypesIn.SystemChat: - string systemMessage = dataTypes.ReadNextString(packetData); - if (protocolVersion >= MC_1_19_3_Version) - { - bool isOverlay = dataTypes.ReadNextBool(packetData); - if (isOverlay) - { - if (!Config.Main.Advanced.ShowXPBarMessages) - break; - } - else - { - if (!Config.Main.Advanced.ShowSystemMessages) - break; - } - - handler.OnTextReceived(new(systemMessage, null, true, -1, Guid.Empty, true)); - } - else - { - int msgType = dataTypes.ReadNextVarInt(packetData); - if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)) - break; - handler.OnTextReceived(new(systemMessage, null, true, msgType, Guid.Empty, true)); - } - - break; - case PacketTypesIn.ProfilelessChatMessage: - string message_ = dataTypes.ReadNextString(packetData); - int messageType_ = dataTypes.ReadNextVarInt(packetData); - string messageName = dataTypes.ReadNextString(packetData); - string? targetName_ = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextString(packetData) - : null; - ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, true, messageType_, - Guid.Empty, true); - profilelessChat.isSenderJson = true; - handler.OnTextReceived(profilelessChat); - break; - case PacketTypesIn.CombatEvent: - // 1.8 - 1.16.5 - if (protocolVersion >= MC_1_8_Version && protocolVersion <= MC_1_16_5_Version) - { - CombatEventType eventType = (CombatEventType)dataTypes.ReadNextVarInt(packetData); - - if (eventType == CombatEventType.EntityDead) - { - dataTypes.SkipNextVarInt(packetData); - - handler.OnPlayerKilled( - dataTypes.ReadNextInt(packetData), - ChatParser.ParseText(dataTypes.ReadNextString(packetData)) - ); - } - } - - break; - case PacketTypesIn.DeathCombatEvent: - dataTypes.SkipNextVarInt(packetData); - - handler.OnPlayerKilled( - protocolVersion >= MC_1_20_Version ? -1 : dataTypes.ReadNextInt(packetData), - ChatParser.ParseText(dataTypes.ReadNextString(packetData)) - ); - - break; - case PacketTypesIn.DamageEvent: // 1.19.4 - if (handler.GetEntityHandlingEnabled() && protocolVersion >= MC_1_19_4_Version) - { - var entityId = dataTypes.ReadNextVarInt(packetData); - var sourceTypeId = dataTypes.ReadNextVarInt(packetData); - var sourceCauseId = dataTypes.ReadNextVarInt(packetData); - var sourceDirectId = dataTypes.ReadNextVarInt(packetData); - - Location? sourcePos; - if (dataTypes.ReadNextBool(packetData)) - { - sourcePos = new Location() - { - X = dataTypes.ReadNextDouble(packetData), - Y = dataTypes.ReadNextDouble(packetData), - Z = dataTypes.ReadNextDouble(packetData) - }; - } - - // TODO: Write a function to use this data ? But seems not too useful - } - - break; - case PacketTypesIn.MessageHeader: // 1.19.2 only - if (protocolVersion == MC_1_19_2_Version) - { - byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) - ? dataTypes.ReadNextByteArray(packetData) - : null; - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - byte[] bodyDigest = dataTypes.ReadNextByteArray(packetData); - - bool verifyResult; - - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - - if (player == null || !player.IsMessageChainLegal()) - verifyResult = false; - else - { - bool lastVerifyResult = player.IsMessageChainLegal(); - verifyResult = player.VerifyMessageHead(ref precedingSignature, - ref headerSignature, ref bodyDigest); - if (lastVerifyResult && !verifyResult) - log.Warn(string.Format(Translations.chat_message_chain_broken, - player.Name)); - } - } - } - - break; - case PacketTypesIn.Respawn: - string? dimensionTypeNameRespawn = null; - Dictionary? dimensionTypeRespawn = null; - if (protocolVersion >= MC_1_16_Version) - { - if (protocolVersion >= MC_1_19_Version) - dimensionTypeNameRespawn = - dataTypes.ReadNextString(packetData); // Dimension Type: Identifier - else if (protocolVersion >= MC_1_16_2_Version) - dimensionTypeRespawn = - dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound - else - dataTypes.ReadNextString(packetData); - currentDimension = 0; - } - else - { - // 1.15 and below - currentDimension = dataTypes.ReadNextInt(packetData); - } - - if (protocolVersion >= MC_1_16_Version) - { - string dimensionName = - dataTypes.ReadNextString( - packetData); // Dimension Name (World Name) - 1.16 and above - if (handler.GetTerrainEnabled()) - { - if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) - { - World.StoreOneDimension(dimensionName, dimensionTypeRespawn!); - World.SetDimension(dimensionName); - } - else if (protocolVersion >= MC_1_19_Version) - { - World.SetDimension(dimensionTypeNameRespawn!); - } - } - } - - if (protocolVersion < MC_1_14_Version) - dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below - if (protocolVersion >= MC_1_15_Version) - dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above - dataTypes.ReadNextByte(packetData); // Gamemode - if (protocolVersion >= MC_1_16_Version) - dataTypes.ReadNextByte(packetData); // Previous Game mode - 1.16 and above - if (protocolVersion < MC_1_16_Version) - dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below - if (protocolVersion >= MC_1_16_Version) - { - dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above - dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above - - if (protocolVersion < MC_1_20_2_Version) - dataTypes.ReadNextBool(packetData); // Copy metadata (Data Kept) - 1.16 - 1.20.2 - } - - if (protocolVersion >= MC_1_19_Version) - { - if (dataTypes.ReadNextBool(packetData)) // Has death location - { - dataTypes.ReadNextString(packetData); // Death dimension name: Identifier - dataTypes.ReadNextLocation(packetData); // Death location - } - } - - if (protocolVersion >= MC_1_20_Version) - dataTypes.ReadNextVarInt(packetData); // Portal Cooldown - - if (protocolVersion >= MC_1_20_2_Version) - dataTypes.ReadNextBool(packetData); // Copy metadata (Data Kept) - 1.20.2 and above - - handler.OnRespawn(); - break; - case PacketTypesIn.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); - Location location = new(x, y, z); - 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 >= MC_1_8_Version) - { - Location current = handler.GetCurrentLocation(); - location.X = (locMask & 1 << 0) != 0 ? current.X + x : x; - location.Y = (locMask & 1 << 1) != 0 ? current.Y + y : y; - location.Z = (locMask & 1 << 2) != 0 ? current.Z + z : z; - } - } - - if (protocolVersion >= MC_1_9_Version) - { - int teleportID = dataTypes.ReadNextVarInt(packetData); - - if (teleportID < 0) - { - yaw = LastYaw; - pitch = LastPitch; - } - else - { - LastYaw = yaw; - LastPitch = pitch; - } - - handler.UpdateLocation(location, yaw, pitch); - - // Teleport confirm packet - SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportID)); - if (Config.Main.Advanced.TemporaryFixBadpacket) - { - SendLocationUpdate(location, true, yaw, pitch, true); - if (teleportID == 1) - SendLocationUpdate(location, true, yaw, pitch, true); - } - } - else - { - handler.UpdateLocation(location, yaw, pitch); - LastYaw = yaw; - LastPitch = pitch; - } - - if (protocolVersion >= MC_1_17_Version && protocolVersion < MC_1_19_4_Version) - dataTypes.ReadNextBool(packetData); // Dismount Vehicle - 1.17 to 1.19.3 - } - break; - case PacketTypesIn.ChunkData: - if (handler.GetTerrainEnabled()) - { - Interlocked.Increment(ref handler.GetWorld().chunkCnt); - Interlocked.Increment(ref handler.GetWorld().chunkLoadNotCompleted); - - int chunkX = dataTypes.ReadNextInt(packetData); - int chunkZ = dataTypes.ReadNextInt(packetData); - if (protocolVersion >= MC_1_17_Version) - { - ulong[]? verticalStripBitmask = null; - - if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) - verticalStripBitmask = - dataTypes.ReadNextULongArray( - packetData); // Bit Mask Length and Primary Bit Mask - - dataTypes.ReadNextNbt(packetData); // Heightmaps - - if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) - { - int biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length - for (int i = 0; i < biomesLength; i++) - dataTypes.SkipNextVarInt(packetData); // Biomes - } - - int dataSize = dataTypes.ReadNextVarInt(packetData); // Size - - pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - - // Block Entity data: ignored - // Trust edges: ignored (Removed in 1.20) - // Light data: ignored - } - else - { - bool chunksContinuous = dataTypes.ReadNextBool(packetData); - if (protocolVersion >= MC_1_16_Version && protocolVersion <= MC_1_16_1_Version) - dataTypes.ReadNextBool(packetData); // Ignore old data - 1.16 to 1.16.1 only - ushort chunkMask = protocolVersion >= MC_1_9_Version - ? (ushort)dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextUShort(packetData); - if (protocolVersion < MC_1_8_Version) - { - 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)); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - } - else - { - if (protocolVersion >= MC_1_14_Version) - dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above - int biomesLength = 0; - if (protocolVersion >= MC_1_16_2_Version) - if (chunksContinuous) - biomesLength = - dataTypes.ReadNextVarInt( - packetData); // Biomes length - 1.16.2 and above - if (protocolVersion >= MC_1_15_Version && chunksContinuous) - { - if (protocolVersion >= MC_1_16_2_Version) - { - for (int i = 0; i < biomesLength; i++) - { - // Biomes - 1.16.2 and above - // Don't use ReadNextVarInt because it cost too much time - dataTypes.SkipNextVarInt(packetData); - } - } - else dataTypes.DropData(1024 * 4, packetData); // Biomes - 1.15 and above - } - - int dataSize = dataTypes.ReadNextVarInt(packetData); - - pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, - chunksContinuous, currentDimension, packetData); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - } - } - } - - break; - case PacketTypesIn.ChunksBiomes: // 1.19.4 - // Biomes are not handled by MCC - break; - case PacketTypesIn.MapData: - if (protocolVersion < MC_1_8_Version) + compression_treshold = dataTypes.ReadNextVarInt(packetData); break; - int mapid = dataTypes.ReadNextVarInt(packetData); - byte scale = dataTypes.ReadNextByte(packetData); + // Login Plugin Request + case 0x04: + var messageId = dataTypes.ReadNextVarInt(packetData); + var channel = dataTypes.ReadNextString(packetData); + List responseData = new(); + var understood = pForge.HandleLoginPluginRequest(channel, packetData, ref responseData); + SendLoginPluginResponse(messageId, understood, responseData.ToArray()); + return understood; + + // Ignore other packets at this stage + default: + return true; + } + + break; + + // https://wiki.vg/Protocol#Configuration + case CurrentState.Configuration: + switch (packetId) + { + // Disconnect + case 0x01: + handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, + ChatParser.ParseText(dataTypes.ReadNextString(packetData))); + return false; + + // Finish Configuration + case 0x02: + currentState = CurrentState.Play; + SendPacket(0x02, new List()); + break; + + // Keep Alive + case 0x03: + SendPacket(0x03, packetData); + break; + + // Ping + case 0x04: + SendPacket(0x04, packetData); + break; + + // Registry Codec + case 0x05: + var registryCodec = dataTypes.ReadNextNbt(packetData); + ChatParser.ReadChatType(registryCodec); + + if (handler.GetTerrainEnabled()) + World.StoreDimensionList(registryCodec); + + break; + + // Resource Pack + case 0x06: + var url = dataTypes.ReadNextString(packetData); + var hash = dataTypes.ReadNextString(packetData); + dataTypes.ReadNextBool(packetData); // Forced + var hasPromptMessage = + dataTypes.ReadNextBool(packetData); - - // 1.9 + - bool trackingPosition = true; - - // 1.14+ - bool locked = false; - - // 1.17+ (locked and trackingPosition switched places) - if (protocolVersion >= MC_1_17_Version) - { - if (protocolVersion >= MC_1_14_Version) - locked = dataTypes.ReadNextBool(packetData); - - if (protocolVersion >= MC_1_9_Version) - trackingPosition = dataTypes.ReadNextBool(packetData); - } - else - { - if (protocolVersion >= MC_1_9_Version) - trackingPosition = dataTypes.ReadNextBool(packetData); - - if (protocolVersion >= MC_1_14_Version) - locked = dataTypes.ReadNextBool(packetData); - } - - int iconcount = 0; - List icons = new(); - - // 1,9 + = needs tracking position to be true to get the icons - if (protocolVersion <= MC_1_16_5_Version || trackingPosition) - { - iconcount = dataTypes.ReadNextVarInt(packetData); - - for (int i = 0; i < iconcount; i++) - { - MapIcon mapIcon = new(); - - // 1.8 - 1.13 - if (protocolVersion < MC_1_13_2_Version) - { - byte directionAndtype = dataTypes.ReadNextByte(packetData); - byte direction, type; - - // 1.12.2+ - if (protocolVersion >= MC_1_12_2_Version) - { - direction = (byte)(directionAndtype & 0xF); - type = (byte)((directionAndtype >> 4) & 0xF); - } - else // 1.8 - 1.12 - { - direction = (byte)((directionAndtype >> 4) & 0xF); - type = (byte)(directionAndtype & 0xF); - } - - mapIcon.Type = (MapIconType)type; - mapIcon.Direction = direction; - } - - // 1.13.2+ - if (protocolVersion >= MC_1_13_2_Version) - mapIcon.Type = (MapIconType)dataTypes.ReadNextVarInt(packetData); - - mapIcon.X = dataTypes.ReadNextByte(packetData); - mapIcon.Z = dataTypes.ReadNextByte(packetData); - - // 1.13.2+ - if (protocolVersion >= MC_1_13_2_Version) - { - mapIcon.Direction = dataTypes.ReadNextByte(packetData); - - if (dataTypes.ReadNextBool(packetData)) // Has Display Name? - mapIcon.DisplayName = - ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - - icons.Add(mapIcon); - } - } - - byte columnsUpdated = dataTypes.ReadNextByte(packetData); // width - byte rowsUpdated = 0; // height - byte mapCoulmnX = 0; - byte mapRowZ = 0; - byte[]? colors = null; - - if (columnsUpdated > 0) - { - rowsUpdated = dataTypes.ReadNextByte(packetData); // height - mapCoulmnX = dataTypes.ReadNextByte(packetData); - mapRowZ = dataTypes.ReadNextByte(packetData); - colors = dataTypes.ReadNextByteArray(packetData); - } - - handler.OnMapData(mapid, scale, trackingPosition, locked, icons, columnsUpdated, - rowsUpdated, mapCoulmnX, mapRowZ, colors); - break; - case PacketTypesIn.TradeList: - if ((protocolVersion >= MC_1_14_Version) && (handler.GetInventoryEnabled())) - { - // MC 1.14 or greater - int windowID = dataTypes.ReadNextVarInt(packetData); - int size = dataTypes.ReadNextByte(packetData); - List trades = new(); - for (int tradeId = 0; tradeId < size; tradeId++) - { - VillagerTrade trade = dataTypes.ReadNextTrade(packetData, itemPalette); - trades.Add(trade); - } - - VillagerInfo villagerInfo = new() - { - Level = dataTypes.ReadNextVarInt(packetData), - Experience = dataTypes.ReadNextVarInt(packetData), - IsRegularVillager = dataTypes.ReadNextBool(packetData), - CanRestock = dataTypes.ReadNextBool(packetData) - }; - handler.OnTradeList(windowID, trades, villagerInfo); - } - - break; - case PacketTypesIn.Title: - if (protocolVersion >= MC_1_8_Version) - { - 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 >= MC_1_10_Version) - { - 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 PacketTypesIn.MultiBlockChange: - if (handler.GetTerrainEnabled()) - { - if (protocolVersion >= MC_1_16_2_Version) - { - long chunkSection = dataTypes.ReadNextLong(packetData); - int sectionX = (int)(chunkSection >> 42); - int sectionY = (int)((chunkSection << 44) >> 44); - int sectionZ = (int)((chunkSection << 22) >> 42); - - if (protocolVersion < MC_1_20_Version) - dataTypes.ReadNextBool(packetData); // Useless boolean (Related to light update) - - int blocksSize = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < blocksSize; i++) - { - ulong chunkSectionPosition = (ulong)dataTypes.ReadNextVarLong(packetData); - int blockId = (int)(chunkSectionPosition >> 12); - int localX = (int)((chunkSectionPosition >> 8) & 0x0F); - int localZ = (int)((chunkSectionPosition >> 4) & 0x0F); - int localY = (int)(chunkSectionPosition & 0x0F); - - Block block = new((ushort)blockId); - int blockX = (sectionX * 16) + localX; - int blockY = (sectionY * 16) + localY; - int blockZ = (sectionZ * 16) + localZ; - - Location location = new(blockX, blockY, blockZ); - - handler.OnBlockChange(location, block); - } - } - else - { - int chunkX = dataTypes.ReadNextInt(packetData); - int chunkZ = dataTypes.ReadNextInt(packetData); - int recordCount = protocolVersion < MC_1_8_Version - ? (int)dataTypes.ReadNextShort(packetData) - : dataTypes.ReadNextVarInt(packetData); - - for (int i = 0; i < recordCount; i++) - { - byte locationXZ; - ushort blockIdMeta; - int blockY; - - if (protocolVersion < MC_1_8_Version) - { - 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; - - Location location = new(chunkX, chunkZ, blockX, blockY, blockZ); - Block block = new(blockIdMeta); - handler.OnBlockChange(location, block); - } - } - } - - break; - case PacketTypesIn.ServerData: - string motd = "-"; - - bool hasMotd = false; - if (protocolVersion < MC_1_19_4_Version) - { - hasMotd = dataTypes.ReadNextBool(packetData); - - if (hasMotd) - motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else - { - hasMotd = true; - motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - - string iconBase64 = "-"; - bool hasIcon = dataTypes.ReadNextBool(packetData); - if (hasIcon) - iconBase64 = dataTypes.ReadNextString(packetData); - - bool previewsChat = false; - if (protocolVersion < MC_1_19_3_Version) - previewsChat = dataTypes.ReadNextBool(packetData); - - handler.OnServerDataRecived(hasMotd, motd, hasIcon, iconBase64, previewsChat); - break; - case PacketTypesIn.BlockChange: - if (handler.GetTerrainEnabled()) - { - if (protocolVersion < MC_1_8_Version) - { - 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); - - Location location = new(blockX, blockY, blockZ); - Block block = new(blockId, blockMeta); - handler.OnBlockChange(location, block); - } - else - { - Location location = dataTypes.ReadNextLocation(packetData); - Block block = new((ushort)dataTypes.ReadNextVarInt(packetData)); - handler.OnBlockChange(location, block); - } - } - - break; - case PacketTypesIn.SetDisplayChatPreview: - bool previewsChatSetting = dataTypes.ReadNextBool(packetData); - handler.OnChatPreviewSettingUpdate(previewsChatSetting); - break; - case PacketTypesIn.ChatSuggestions: - break; - case PacketTypesIn.MapChunkBulk: - if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) - { - int chunkCount; - bool hasSkyLight; - Queue chunkData = packetData; - - //Read global fields - if (protocolVersion < MC_1_8_Version) - { - 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 < MC_1_8_Version - ? 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); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - } - } - - break; - case PacketTypesIn.UnloadChunk: - if (protocolVersion >= MC_1_9_Version && handler.GetTerrainEnabled()) - { - int chunkX = dataTypes.ReadNextInt(packetData); - int chunkZ = dataTypes.ReadNextInt(packetData); - - // Warning: It is legal to include unloaded chunks in the UnloadChunk packet. - // Since chunks that have not been loaded are not recorded, this may result - // in loading chunks that should be unloaded and inaccurate statistics. - if (handler.GetWorld()[chunkX, chunkZ] != null) - Interlocked.Decrement(ref handler.GetWorld().chunkCnt); - - handler.GetWorld()[chunkX, chunkZ] = null; - } - - break; - case PacketTypesIn.ChangeGameState: - if (protocolVersion >= MC_1_15_2_Version) - { - byte reason = dataTypes.ReadNextByte(packetData); - float state = dataTypes.ReadNextFloat(packetData); - - handler.OnGameEvent(reason, state); - } - - break; - case PacketTypesIn.PlayerInfo: - if (protocolVersion >= MC_1_19_3_Version) - { - byte actionBitset = dataTypes.ReadNextByte(packetData); - int numberOfActions = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < numberOfActions; i++) - { - Guid playerUuid = dataTypes.ReadNextUUID(packetData); - - PlayerInfo player; - if ((actionBitset & (1 << 0)) > 0) // Actions bit 0: add player - { - string name = dataTypes.ReadNextString(packetData); - int numberOfProperties = dataTypes.ReadNextVarInt(packetData); - for (int j = 0; j < numberOfProperties; ++j) - { - dataTypes.SkipNextString(packetData); - dataTypes.SkipNextString(packetData); - if (dataTypes.ReadNextBool(packetData)) - dataTypes.SkipNextString(packetData); - } - - player = new(name, playerUuid); - handler.OnPlayerJoin(player); - } - else - { - PlayerInfo? playerGet = handler.GetPlayerInfo(playerUuid); - if (playerGet == null) - { - player = new(string.Empty, playerUuid); - handler.OnPlayerJoin(player); - } - else - { - player = playerGet; - } - } - - if ((actionBitset & (1 << 1)) > 0) // Actions bit 1: initialize chat - { - bool hasSignatureData = dataTypes.ReadNextBool(packetData); - if (hasSignatureData) - { - Guid chatUuid = dataTypes.ReadNextUUID(packetData); - long publicKeyExpiryTime = dataTypes.ReadNextLong(packetData); - byte[] encodedPublicKey = dataTypes.ReadNextByteArray(packetData); - byte[] publicKeySignature = dataTypes.ReadNextByteArray(packetData); - player.SetPublicKey(chatUuid, publicKeyExpiryTime, encodedPublicKey, - publicKeySignature); - - if (playerUuid == handler.GetUserUuid()) - { - log.Debug("Receive ChatUuid = " + chatUuid); - this.chatUuid = chatUuid; - } - } - else - { - player.ClearPublicKey(); - - if (playerUuid == handler.GetUserUuid()) - { - log.Debug("Receive ChatUuid = Empty"); - } - } - - if (playerUuid == handler.GetUserUuid()) - { - receivePlayerInfo = true; - if (receiveDeclareCommands) - handler.SetCanSendMessage(true); - } - } - - if ((actionBitset & 1 << 2) > 0) // Actions bit 2: update gamemode - { - handler.OnGamemodeUpdate(playerUuid, dataTypes.ReadNextVarInt(packetData)); - } - - if ((actionBitset & (1 << 3)) > 0) // Actions bit 3: update listed - { - player.Listed = dataTypes.ReadNextBool(packetData); - } - - if ((actionBitset & (1 << 4)) > 0) // Actions bit 4: update latency - { - int latency = dataTypes.ReadNextVarInt(packetData); - handler.OnLatencyUpdate(playerUuid, latency); //Update latency; - } - - if ((actionBitset & (1 << 5)) > 0) // Actions bit 5: update display name - { - if (dataTypes.ReadNextBool(packetData)) - player.DisplayName = dataTypes.ReadNextString(packetData); - else - player.DisplayName = null; - } - } - } - else if (protocolVersion >= MC_1_8_Version) - { - int action = dataTypes.ReadNextVarInt(packetData); // Action Name - int numberOfPlayers = dataTypes.ReadNextVarInt(packetData); // Number Of Players - - for (int i = 0; i < numberOfPlayers; i++) - { - Guid uuid = dataTypes.ReadNextUUID(packetData); // Player UUID - - switch (action) - { - case 0x00: //Player Join (Add player since 1.19) - string name = dataTypes.ReadNextString(packetData); // Player name - int propNum = - dataTypes.ReadNextVarInt( - packetData); // Number of properties in the following array - - // Property: Tuple[]? properties = - useProperty ? new Tuple[propNum] : null; - for (int p = 0; p < propNum; p++) - { - string propertyName = - dataTypes.ReadNextString(packetData); // Name: String (32767) - string val = - dataTypes.ReadNextString(packetData); // Value: String (32767) - string? propertySignature = null; - if (dataTypes.ReadNextBool(packetData)) // Is Signed - propertySignature = - dataTypes.ReadNextString( - packetData); // Signature: String (32767) - if (useProperty) - properties![p] = new(propertyName, val, propertySignature); - } -#pragma warning restore CS0162 // Unreachable code detected - - int gameMode = dataTypes.ReadNextVarInt(packetData); // Gamemode - handler.OnGamemodeUpdate(uuid, gameMode); - - int ping = dataTypes.ReadNextVarInt(packetData); // Ping - - string? displayName = null; - if (dataTypes.ReadNextBool(packetData)) // Has display name - displayName = dataTypes.ReadNextString(packetData); // Display name - - // 1.19 Additions - long? keyExpiration = null; - byte[]? publicKey = null, signature = null; - if (protocolVersion >= MC_1_19_Version) - { - if (dataTypes.ReadNextBool( - packetData)) // Has Sig Data (if true, red the following fields) - { - keyExpiration = dataTypes.ReadNextLong(packetData); // Timestamp - - int publicKeyLength = - dataTypes.ReadNextVarInt(packetData); // Public Key Length - if (publicKeyLength > 0) - publicKey = dataTypes.ReadData(publicKeyLength, - packetData); // Public key - - int signatureLength = - dataTypes.ReadNextVarInt(packetData); // Signature Length - if (signatureLength > 0) - signature = dataTypes.ReadData(signatureLength, - packetData); // Public key - } - } - - handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, - displayName, keyExpiration, publicKey, signature)); - 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)) - { - PlayerInfo? player = handler.GetPlayerInfo(uuid); - if (player != null) - player.DisplayName = dataTypes.ReadNextString(packetData); - else - dataTypes.SkipNextString(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(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16) - .ToArray()); - if (online) - handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); - else handler.OnPlayerLeave(FakeUUID); - } - - break; - case PacketTypesIn.PlayerRemove: - int numberOfLeavePlayers = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < numberOfLeavePlayers; ++i) - { - Guid playerUuid = dataTypes.ReadNextUUID(packetData); - handler.OnPlayerLeave(playerUuid); - } - - break; - case PacketTypesIn.TabComplete: - int old_transaction_id = autocomplete_transaction_id; - if (protocolVersion >= MC_1_13_Version) - { - 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); - - string[] autocomplete_result = new string[autocomplete_count]; - for (int i = 0; i < autocomplete_count; i++) - { - autocomplete_result[i] = dataTypes.ReadNextString(packetData); - if (protocolVersion >= MC_1_13_Version) - { - // Skip optional tooltip for each tab-complete resul`t - if (dataTypes.ReadNextBool(packetData)) - dataTypes.SkipNextString(packetData); - } - } - - handler.OnAutoCompleteDone(old_transaction_id, autocomplete_result); - break; - case PacketTypesIn.PluginMessage: - String channel = dataTypes.ReadNextString(packetData); - // Length is unneeded as the whole remaining packetData is the entire payload of the packet. - if (protocolVersion < MC_1_8_Version) - pForge.ReadNextVarShort(packetData); - handler.OnPluginChannelMessage(channel, packetData.ToArray()); - return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); - case PacketTypesIn.Disconnect: - handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, - ChatParser.ParseText(dataTypes.ReadNextString(packetData))); - return false; - case PacketTypesIn.SetCompression: - if (protocolVersion >= MC_1_8_Version && protocolVersion < MC_1_9_Version) - compression_treshold = dataTypes.ReadNextVarInt(packetData); - break; - case PacketTypesIn.OpenWindow: - if (handler.GetInventoryEnabled()) - { - if (protocolVersion < MC_1_14_Version) - { - // 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(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(windowID, windowType, ChatParser.ParseText(title)); - handler.OnInventoryOpen(windowID, inventory); - } - } - - break; - case PacketTypesIn.CloseWindow: - if (handler.GetInventoryEnabled()) - { - byte windowID = dataTypes.ReadNextByte(packetData); - lock (window_actions) - { - window_actions[windowID] = 0; - } - - handler.OnInventoryClose(windowID); - } - - break; - case PacketTypesIn.WindowItems: - if (handler.GetInventoryEnabled()) - { - byte windowId = dataTypes.ReadNextByte(packetData); - int stateId = -1; - int elements = 0; - - if (protocolVersion >= MC_1_17_1_Version) - { - // State ID and Elements as VarInt - 1.17.1 and above - stateId = dataTypes.ReadNextVarInt(packetData); - elements = dataTypes.ReadNextVarInt(packetData); - } - else - { - // Elements as Short - 1.17.0 and below - dataTypes.ReadNextShort(packetData); - } - - Dictionary inventorySlots = new(); - for (int slotId = 0; slotId < elements; slotId++) - { - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - if (item != null) - inventorySlots[slotId] = item; - } - - if (protocolVersion >= MC_1_17_1_Version) // Carried Item - 1.17.1 and above - dataTypes.ReadNextItemSlot(packetData, itemPalette); - - handler.OnWindowItems(windowId, inventorySlots, stateId); - } - - break; - case PacketTypesIn.WindowProperty: - byte containerId = dataTypes.ReadNextByte(packetData); - short propertyId = dataTypes.ReadNextShort(packetData); - short propertyValue = dataTypes.ReadNextShort(packetData); - - handler.OnWindowProperties(containerId, propertyId, propertyValue); - break; - case PacketTypesIn.SetSlot: - if (handler.GetInventoryEnabled()) - { - byte windowID = dataTypes.ReadNextByte(packetData); - int stateId = -1; - if (protocolVersion >= MC_1_17_1_Version) - stateId = dataTypes.ReadNextVarInt(packetData); // State ID - 1.17.1 and above - short slotID = dataTypes.ReadNextShort(packetData); - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnSetSlot(windowID, slotID, item, stateId); - } - - break; - case PacketTypesIn.WindowConfirmation: - if (handler.GetInventoryEnabled()) - { - byte windowID = dataTypes.ReadNextByte(packetData); - short actionID = dataTypes.ReadNextShort(packetData); - bool accepted = dataTypes.ReadNextBool(packetData); - if (!accepted) - SendWindowConfirmation(windowID, actionID, true); - } - - break; - case PacketTypesIn.ResourcePackSend: - string url = dataTypes.ReadNextString(packetData); - string hash = dataTypes.ReadNextString(packetData); - bool forced = true; // Assume forced for MC 1.16 and below - if (protocolVersion >= MC_1_17_Version) - { - forced = dataTypes.ReadNextBool(packetData); - bool hasPromptMessage = - dataTypes.ReadNextBool(packetData); // Has Prompt Message (Boolean) - 1.17 and above if (hasPromptMessage) - dataTypes.SkipNextString( - packetData); // Prompt Message (Optional Chat) - 1.17 and above - } + dataTypes.SkipNextString(packetData); - // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056) - if (!url.StartsWith("http") && hash.Length != 40) // Some server may have null hash value + // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056) + if (!url.StartsWith("http") && + hash.Length != 40) // Some server may have null hash value + break; + + //Send back "accepted" and "successfully loaded" responses for plugins or server config making use of resource pack mandatory + var responseHeader = Array.Empty(); + SendPacket(0x05, + dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(3))); // Accepted pack + SendPacket(0x05, + dataTypes.ConcatBytes(responseHeader, + DataTypes.GetVarInt(0))); // Successfully loaded break; - //Send back "accepted" and "successfully loaded" responses for plugins or server config making use of resource pack mandatory - byte[] responseHeader = Array.Empty(); - if (protocolVersion < - MC_1_10_Version) //MC 1.10 does not include resource pack hash in responses - responseHeader = dataTypes.ConcatBytes(DataTypes.GetVarInt(hash.Length), - Encoding.UTF8.GetBytes(hash)); - SendPacket(PacketTypesOut.ResourcePackStatus, - dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(3))); //Accepted pack - SendPacket(PacketTypesOut.ResourcePackStatus, - dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(0))); //Successfully loaded - break; - case PacketTypesIn.SpawnEntity: - if (handler.GetEntityHandlingEnabled()) - { - Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false); - handler.OnSpawnEntity(entity); - } - break; - case PacketTypesIn.EntityEquipment: - if (handler.GetEntityHandlingEnabled()) - { - int entityid = dataTypes.ReadNextVarInt(packetData); - if (protocolVersion >= MC_1_16_Version) - { - bool hasNext; - do - { - byte bitsData = dataTypes.ReadNextByte(packetData); - // Top bit set if another entry follows, and otherwise unset if this is the last item in the array - hasNext = (bitsData >> 7) == 1; - int slot2 = bitsData >> 1; - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnEntityEquipment(entityid, slot2, item); - } while (hasNext); - } - else - { - int slot2 = protocolVersion < MC_1_9_Version - ? dataTypes.ReadNextShort(packetData) - : dataTypes.ReadNextVarInt(packetData); + // Ignore other packets at this stage + default: + return true; + } - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnEntityEquipment(entityid, slot2, item); - } - } + break; - break; - case PacketTypesIn.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); - } + // https://wiki.vg/Protocol#Play + case CurrentState.Play: + return HandlePlayPackets(packetId, packetData); - break; - case PacketTypesIn.SpawnPlayer: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Guid UUID = dataTypes.ReadNextUUID(packetData); - - double x, y, z; - - if (protocolVersion < MC_1_9_Version) - { - x = dataTypes.ReadNextInt(packetData) / 32.0D; - y = dataTypes.ReadNextInt(packetData) / 32.0D; - z = dataTypes.ReadNextInt(packetData) / 32.0D; - } - else - { - x = dataTypes.ReadNextDouble(packetData); - y = dataTypes.ReadNextDouble(packetData); - z = dataTypes.ReadNextDouble(packetData); - } - - byte Yaw = dataTypes.ReadNextByte(packetData); - byte Pitch = dataTypes.ReadNextByte(packetData); - - Location EntityLocation = new(x, y, z); - - handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch); - } - - break; - case PacketTypesIn.EntityEffect: - if (handler.GetEntityHandlingEnabled()) - { - int entityid = dataTypes.ReadNextVarInt(packetData); - Inventory.Effects effect = Effects.Speed; - int effectId = protocolVersion >= MC_1_18_2_Version - ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextByte(packetData); - if (Enum.TryParse(effectId.ToString(), out effect)) - { - int amplifier = dataTypes.ReadNextByte(packetData); - int duration = dataTypes.ReadNextVarInt(packetData); - byte flags = dataTypes.ReadNextByte(packetData); - - bool hasFactorData = false; - Dictionary? factorCodec = null; - - if (protocolVersion >= MC_1_19_Version) - { - hasFactorData = dataTypes.ReadNextBool(packetData); - if (hasFactorData) - factorCodec = dataTypes.ReadNextNbt(packetData); - } - - handler.OnEntityEffect(entityid, effect, amplifier, duration, flags, hasFactorData, - factorCodec); - } - } - - break; - case PacketTypesIn.DestroyEntities: - if (handler.GetEntityHandlingEnabled()) - { - int entityCount = 1; // 1.17.0 has only one entity per packet - if (protocolVersion != MC_1_17_Version) - entityCount = - dataTypes.ReadNextVarInt(packetData); // All other versions have a "count" field - int[] entityList = new int[entityCount]; - for (int i = 0; i < entityCount; i++) - { - entityList[i] = dataTypes.ReadNextVarInt(packetData); - } - - handler.OnDestroyEntities(entityList); - } - - break; - case PacketTypesIn.EntityPosition: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - - Double DeltaX, DeltaY, DeltaZ; - - if (protocolVersion < MC_1_9_Version) - { - DeltaX = Convert.ToDouble(dataTypes.ReadNextByte(packetData)); - DeltaY = Convert.ToDouble(dataTypes.ReadNextByte(packetData)); - DeltaZ = Convert.ToDouble(dataTypes.ReadNextByte(packetData)); - } - else - { - DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - 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 PacketTypesIn.EntityPositionAndRotation: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - - Double DeltaX, DeltaY, DeltaZ; - - if (protocolVersion < MC_1_9_Version) - { - DeltaX = dataTypes.ReadNextByte(packetData) / 32.0D; - DeltaY = dataTypes.ReadNextByte(packetData) / 32.0D; - DeltaZ = dataTypes.ReadNextByte(packetData) / 32.0D; - } - else - { - DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - } - - - float _yaw = dataTypes.ReadNextByte(packetData) * (1F / 256) * 360; - float _pitch = dataTypes.ReadNextByte(packetData) * (1F / 256) * 360; - bool OnGround = dataTypes.ReadNextBool(packetData); - DeltaX = DeltaX / (128 * 32); - DeltaY = DeltaY / (128 * 32); - DeltaZ = DeltaZ / (128 * 32); - - handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, _yaw, _pitch, OnGround); - } - - break; - case PacketTypesIn.EntityRotation: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - - - float _yaw = dataTypes.ReadNextByte(packetData) * (1F / 256) * 360; - float _pitch = dataTypes.ReadNextByte(packetData) * (1F / 256) * 360; - bool OnGround = dataTypes.ReadNextBool(packetData); - - handler.OnEntityRotation(EntityID, _yaw, _pitch, OnGround); - } - - break; - case PacketTypesIn.EntityProperties: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - int NumberOfProperties = protocolVersion >= MC_1_17_Version - ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextInt(packetData); - Dictionary keys = new(); - for (int i = 0; i < NumberOfProperties; i++) - { - string _key = dataTypes.ReadNextString(packetData); - Double _value = dataTypes.ReadNextDouble(packetData); - - List op0 = new(); - List op1 = new(); - List op2 = new(); - 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 PacketTypesIn.EntityMetadata: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Dictionary metadata = - dataTypes.ReadNextMetadata(packetData, itemPalette, entityMetadataPalette); - - // Also make a palette for field? Will be a lot of work - int healthField = protocolVersion switch - { - > MC_1_20_Version => throw new NotImplementedException(Translations - .exception_palette_healthfield), - // 1.17 and above - >= MC_1_17_Version => 9, - // 1.14 and above - >= MC_1_14_Version => 8, - // 1.10 and above - >= MC_1_10_Version => 7, - // 1.8 and above - >= MC_1_8_Version => 6, - _ => throw new NotImplementedException(Translations.exception_palette_healthfield) - }; - - if (metadata.TryGetValue(healthField, out var healthObj) && healthObj != null && - healthObj is float) - handler.OnEntityHealth(EntityID, (float)healthObj); - - handler.OnEntityMetadata(EntityID, metadata); - } - - break; - case PacketTypesIn.EntityStatus: - if (handler.GetEntityHandlingEnabled()) - { - int entityId = dataTypes.ReadNextInt(packetData); - byte status = dataTypes.ReadNextByte(packetData); - handler.OnEntityStatus(entityId, status); - } - - break; - case PacketTypesIn.TimeUpdate: - long WorldAge = dataTypes.ReadNextLong(packetData); - long TimeOfday = dataTypes.ReadNextLong(packetData); - handler.OnTimeUpdate(WorldAge, TimeOfday); - break; - case PacketTypesIn.EntityTeleport: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - - double x, y, z; - - if (protocolVersion < MC_1_9_Version) - { - x = dataTypes.ReadNextInt(packetData) / 32.0D; - y = dataTypes.ReadNextInt(packetData) / 32.0D; - z = dataTypes.ReadNextInt(packetData) / 32.0D; - } - else - { - x = dataTypes.ReadNextDouble(packetData); - y = dataTypes.ReadNextDouble(packetData); - 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 PacketTypesIn.UpdateHealth: - float health = dataTypes.ReadNextFloat(packetData); - int food; - if (protocolVersion >= MC_1_8_Version) - food = dataTypes.ReadNextVarInt(packetData); - else - food = dataTypes.ReadNextShort(packetData); - dataTypes.ReadNextFloat(packetData); // Food Saturation - handler.OnUpdateHealth(health, food); - break; - case PacketTypesIn.SetExperience: - float experiencebar = dataTypes.ReadNextFloat(packetData); - int totalexperience, level; - level = dataTypes.ReadNextVarInt(packetData); - totalexperience = dataTypes.ReadNextVarInt(packetData); - handler.OnSetExperience(experiencebar, level, totalexperience); - break; - case PacketTypesIn.Explosion: - Location explosionLocation; - if (protocolVersion >= MC_1_19_3_Version) - explosionLocation = new(dataTypes.ReadNextDouble(packetData), - dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData)); - else - explosionLocation = new(dataTypes.ReadNextFloat(packetData), - dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData)); - - float explosionStrength = dataTypes.ReadNextFloat(packetData); - int explosionBlockCount = protocolVersion >= MC_1_17_Version - ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextInt(packetData); - - for (int i = 0; i < explosionBlockCount; i++) - dataTypes.ReadData(3, packetData); - - float playerVelocityX = dataTypes.ReadNextFloat(packetData); - float playerVelocityY = dataTypes.ReadNextFloat(packetData); - float playerVelocityZ = dataTypes.ReadNextFloat(packetData); - - handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); - break; - case PacketTypesIn.HeldItemChange: - byte slot = dataTypes.ReadNextByte(packetData); - handler.OnHeldItemChange(slot); - break; - case PacketTypesIn.ScoreboardObjective: - string objectivename = dataTypes.ReadNextString(packetData); - byte mode = dataTypes.ReadNextByte(packetData); - string objectivevalue = String.Empty; - int type2 = -1; - if (mode == 0 || mode == 2) - { - objectivevalue = dataTypes.ReadNextString(packetData); - type2 = dataTypes.ReadNextVarInt(packetData); - } - - handler.OnScoreboardObjective(objectivename, mode, objectivevalue, type2); - break; - case PacketTypesIn.UpdateScore: - string entityname = dataTypes.ReadNextString(packetData); - int action3 = protocolVersion >= MC_1_18_2_Version - ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextByte(packetData); - string objectivename2 = string.Empty; - int value = -1; - if (action3 != 1 || protocolVersion >= MC_1_8_Version) - objectivename2 = dataTypes.ReadNextString(packetData); - if (action3 != 1) - value = dataTypes.ReadNextVarInt(packetData); - handler.OnUpdateScore(entityname, action3, objectivename2, value); - break; - case PacketTypesIn.BlockChangedAck: - handler.OnBlockChangeAck(dataTypes.ReadNextVarInt(packetData)); - break; - case PacketTypesIn.BlockBreakAnimation: - if (handler.GetEntityHandlingEnabled() && handler.GetTerrainEnabled()) - { - int playerId = dataTypes.ReadNextVarInt(packetData); - Location blockLocation = dataTypes.ReadNextLocation(packetData); - byte stage = dataTypes.ReadNextByte(packetData); - handler.OnBlockBreakAnimation(playerId, blockLocation, stage); - } - - break; - case PacketTypesIn.EntityAnimation: - if (handler.GetEntityHandlingEnabled()) - { - int playerId2 = dataTypes.ReadNextVarInt(packetData); - byte animation = dataTypes.ReadNextByte(packetData); - handler.OnEntityAnimation(playerId2, animation); - } - - break; - - case PacketTypesIn.OpenSignEditor: - var signLocation = dataTypes.ReadNextLocation(packetData); - var isFrontText = true; - - if (protocolVersion >= MC_1_20_Version) - isFrontText = dataTypes.ReadNextBool(packetData); - - // TODO: Use - break; - - // Temporarily disabled until I find a fix - /*case PacketTypesIn.BlockEntityData: - var location_ = dataTypes.ReadNextLocation(packetData); - var type_ = dataTypes.ReadNextInt(packetData); - var nbt = dataTypes.ReadNextNbt(packetData); - var nbtJson = JsonConvert.SerializeObject(nbt["messages"]); - - //log.Info($"BLOCK ENTITY DATA -> {location_.ToString()} [{type_}] -> NBT: {nbtJson}"); - - break;*/ - - default: - return false; //Ignored packet - } - - return true; //Packet processed + default: + return true; + } } 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(Translations.exception_packet_process, - packetPalette.GetIncommingTypeById(packetID), - packetID, + packetPalette.GetIncommingTypeById(packetId), + packetId, protocolVersion, - login_phase, + currentState == CurrentState.Login, innerException.GetType()), innerException); } + + return true; + } + + private bool HandlePlayPackets(int packetId, Queue packetData) + { + switch (packetPalette.GetIncommingTypeById(packetId)) + { + case PacketTypesIn.KeepAlive: // Keep Alive (Play) + SendPacket(PacketTypesOut.KeepAlive, packetData); + handler.OnServerKeepAlive(); + break; + + case PacketTypesIn.Ping: + SendPacket(PacketTypesOut.Pong, packetData); + break; + + case PacketTypesIn.JoinGame: + { + // Temporary fix + log.Debug("Receive JoinGame"); + + receiveDeclareCommands = receivePlayerInfo = false; + + messageIndex = 0; + pendingAcknowledgments = 0; + + lastReceivedMessage = null; + lastSeenMessagesCollector = protocolVersion >= MC_1_19_3_Version ? new(20) : new(5); + } + handler.OnGameJoined(isOnlineMode); + + var playerEntityId = dataTypes.ReadNextInt(packetData); + handler.OnReceivePlayerEntityID(playerEntityId); + + if (protocolVersion >= MC_1_16_2_Version) + dataTypes.ReadNextBool(packetData); // Is hardcore - 1.16.2 and above + + if (protocolVersion < MC_1_20_2_Version) + handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData)); + + if (protocolVersion >= MC_1_16_Version) + { + if (protocolVersion < MC_1_20_2_Version) + dataTypes.ReadNextByte(packetData); // Previous Gamemode - 1.16 - 1.20.2 + + var worldCount = + dataTypes.ReadNextVarInt( + packetData); // Dimension Count (World Count) - 1.16 and above + for (var i = 0; i < worldCount; i++) + dataTypes.ReadNextString( + packetData); // Dimension Names (World Names) - 1.16 and above + + if (protocolVersion < MC_1_20_2_Version) + { + var registryCodec = + dataTypes.ReadNextNbt( + packetData); // Registry Codec (Dimension Codec) - 1.16 and above + if (protocolVersion >= MC_1_19_Version) + ChatParser.ReadChatType(registryCodec); + if (handler.GetTerrainEnabled()) + World.StoreDimensionList(registryCodec); + } + } + + if (protocolVersion < MC_1_20_2_Version) + { + // Current dimension + // String: 1.19 and above + // NBT Tag Compound: [1.16.2 to 1.18.2] + // String identifier: 1.16 and 1.16.1 + // varInt: [1.9.1 to 1.15.2] + // byte: below 1.9.1 + string? dimensionTypeName = null; + Dictionary? dimensionType = null; + switch (protocolVersion) + { + case >= MC_1_16_Version: + { + switch (protocolVersion) + { + case >= MC_1_19_Version: + dimensionTypeName = + dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + break; + case >= MC_1_16_2_Version: + dimensionType = + dataTypes.ReadNextNbt( + packetData); // Dimension Type: NBT Tag Compound + break; + default: + dataTypes.ReadNextString(packetData); + break; + } + + currentDimension = 0; + break; + } + case >= MC_1_9_1_Version: + currentDimension = dataTypes.ReadNextInt(packetData); + break; + default: + currentDimension = (sbyte)dataTypes.ReadNextByte(packetData); + break; + } + + switch (protocolVersion) + { + case < MC_1_14_Version: + dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below + break; + case >= MC_1_16_Version: + { + var dimensionName = + dataTypes.ReadNextString( + packetData); // Dimension Name (World Name) - 1.16 and above + + if (handler.GetTerrainEnabled()) + { + switch (protocolVersion) + { + case >= MC_1_16_2_Version and <= MC_1_18_2_Version: + World.StoreOneDimension(dimensionName, dimensionType!); + World.SetDimension(dimensionName); + break; + default: + World.SetDimension(dimensionTypeName!); + break; + } + } + + break; + } + } + } + + if (protocolVersion is >= MC_1_15_Version and < MC_1_20_2_Version) + dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 - 1.20.2 + if (protocolVersion >= MC_1_16_2_Version) + dataTypes.ReadNextVarInt(packetData); // Max Players - 1.16.2 and above + else + dataTypes.ReadNextByte(packetData); // Max Players - 1.16.1 and below + if (protocolVersion < MC_1_16_Version) + dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below + if (protocolVersion >= MC_1_14_Version) + dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above + if (protocolVersion >= MC_1_18_1_Version) + dataTypes.ReadNextVarInt(packetData); // Simulation Distance - 1.18 and above + if (protocolVersion >= MC_1_8_Version) + dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above + if (protocolVersion >= MC_1_15_Version) + dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above + + if (protocolVersion < MC_1_20_2_Version) + { + if (protocolVersion >= MC_1_16_Version) + { + dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and 1.20.2 + dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and 1.20.2 + } + + if (protocolVersion >= MC_1_19_Version) + { + if (dataTypes.ReadNextBool(packetData)) // Has death location + { + dataTypes.SkipNextString(packetData); // Death dimension name: Identifier + dataTypes.ReadNextLocation(packetData); // Death location + } + } + + if (protocolVersion >= MC_1_20_Version) + dataTypes.ReadNextVarInt(packetData); // Portal Cooldown - 1.20 and above + } + else + { + dataTypes.ReadNextBool(packetData); // Do limited crafting + var dimensionTypeName = + dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above + + if (handler.GetTerrainEnabled()) + World.SetDimension(dimensionTypeName); + + dataTypes.ReadNextLong(packetData); // Hashed world seed + handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData)); + dataTypes.ReadNextByte(packetData); // Previous Gamemode + dataTypes.ReadNextBool(packetData); // Is Debug + dataTypes.ReadNextBool(packetData); // Is Flat + var hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location + if (hasDeathLocation) + { + dataTypes.SkipNextString(packetData); // Death dimension name: Identifier + dataTypes.ReadNextLocation(packetData); // Death location + } + + dataTypes.ReadNextVarInt(packetData); // Portal Cooldown + } + + break; + case PacketTypesIn.SpawnPainting: // Just skip, no need for this + return true; + case PacketTypesIn.DeclareCommands: + if (protocolVersion >= MC_1_19_Version) + { + log.Debug("Receive DeclareCommands"); + DeclareCommands.Read(dataTypes, packetData, protocolVersion); + receiveDeclareCommands = true; + if (receivePlayerInfo) + handler.SetCanSendMessage(true); + } + + break; + case PacketTypesIn.ChatMessage: + var messageType = 0; + + if (protocolVersion <= MC_1_18_2_Version) // 1.18 and bellow + { + var message = dataTypes.ReadNextString(packetData); + + Guid senderUuid; + if (protocolVersion >= MC_1_8_Version) + { + //Hide system messages or xp bar messages? + messageType = dataTypes.ReadNextByte(packetData); + if (messageType == 1 && !Config.Main.Advanced.ShowSystemMessages + || messageType == 2 && !Config.Main.Advanced.ShowSystemMessages) + break; + + senderUuid = protocolVersion >= MC_1_16_5_Version + ? dataTypes.ReadNextUUID(packetData) + : Guid.Empty; + } + else + senderUuid = Guid.Empty; + + handler.OnTextReceived(new(message, null, true, messageType, senderUuid)); + } + else if (protocolVersion == MC_1_19_Version) // 1.19 + { + var signedChat = dataTypes.ReadNextString(packetData); + var hasUnsignedChatContent = dataTypes.ReadNextBool(packetData); + var unsignedChatContent = hasUnsignedChatContent ? dataTypes.ReadNextString(packetData) : null; + + messageType = dataTypes.ReadNextVarInt(packetData); + if (messageType == 1 && !Config.Main.Advanced.ShowSystemMessages + || messageType == 2 && !Config.Main.Advanced.ShowXPBarMessages) + break; + + var senderUuid = dataTypes.ReadNextUUID(packetData); + var senderDisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + var hasSenderTeamName = dataTypes.ReadNextBool(packetData); + var senderTeamName = hasSenderTeamName + ? ChatParser.ParseText(dataTypes.ReadNextString(packetData)) + : null; + + var timestamp = dataTypes.ReadNextLong(packetData); + var salt = dataTypes.ReadNextLong(packetData); + var messageSignature = dataTypes.ReadNextByteArray(packetData); + + bool verifyResult; + if (!isOnlineMode) + verifyResult = false; + else if (senderUuid == handler.GetUserUuid()) + verifyResult = true; + else + { + var player = handler.GetPlayerInfo(senderUuid); + verifyResult = player != null && player.VerifyMessage(signedChat, timestamp, salt, + ref messageSignature); + } + + ChatMessage chat = new(signedChat, true, messageType, senderUuid, unsignedChatContent, + senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); + handler.OnTextReceived(chat); + } + else if (protocolVersion == MC_1_19_2_Version) + { + // 1.19.1 - 1.19.2 + var precedingSignature = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextByteArray(packetData) + : null; + var senderUuid = dataTypes.ReadNextUUID(packetData); + var headerSignature = dataTypes.ReadNextByteArray(packetData); + var signedChat = dataTypes.ReadNextString(packetData); + var decorated = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextString(packetData) + : null; + + var timestamp = dataTypes.ReadNextLong(packetData); + var salt = dataTypes.ReadNextLong(packetData); + + var lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); + var lastSeenMessageList = + new LastSeenMessageList.AcknowledgedMessage[lastSeenMessageListLen]; + + for (var i = 0; i < lastSeenMessageListLen; ++i) + { + var user = dataTypes.ReadNextUUID(packetData); + var lastSignature = dataTypes.ReadNextByteArray(packetData); + lastSeenMessageList[i] = new(user, lastSignature, true); + } + + LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); + var unsignedChatContent = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextString(packetData) + : null; + + var filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + if (filterEnum == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + var chatTypeId = dataTypes.ReadNextVarInt(packetData); + var chatName = dataTypes.ReadNextString(packetData); + var targetName = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextString(packetData) + : null; + + var chatInfo = Json.ParseJson(chatName).Properties; + var senderDisplayName = + (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]) + .StringValue; + string? senderTeamName = null; + var messageTypeEnum = + ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || + messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0] + .Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + var player = handler.GetPlayerInfo(senderUuid); + if (player != null && (player.DisplayName != null || player is { Name: not null }) && + string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + bool verifyResult; + if (!isOnlineMode) + verifyResult = false; + else if (senderUuid == handler.GetUserUuid()) + verifyResult = true; + else + { + var player = handler.GetPlayerInfo(senderUuid); + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else + { + var lastVerifyResult = player.IsMessageChainLegal(); + verifyResult = player.VerifyMessage(signedChat, timestamp, salt, + ref headerSignature, ref precedingSignature, lastSeenMessages); + if (lastVerifyResult && !verifyResult) + log.Warn(string.Format(Translations.chat_message_chain_broken, + senderDisplayName)); + } + } + + ChatMessage chat = new(signedChat, false, chatTypeId, senderUuid, unsignedChatContent, + senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); + if (isOnlineMode && !chat.LacksSender()) + Acknowledge(chat); + handler.OnTextReceived(chat); + } + else if (protocolVersion >= MC_1_19_3_Version) + { + // 1.19.3+ + // Header section + // net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket#write + var senderUuid = dataTypes.ReadNextUUID(packetData); + var index = dataTypes.ReadNextVarInt(packetData); + // Signature is fixed size of 256 bytes + var messageSignature = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextByteArray(packetData, 256) + : null; + + // Body + // net.minecraft.network.message.MessageBody.Serialized#write + var message = dataTypes.ReadNextString(packetData); + var timestamp = dataTypes.ReadNextLong(packetData); + var salt = dataTypes.ReadNextLong(packetData); + + // Previous Messages + // net.minecraft.network.message.LastSeenMessageList.Indexed#write + // net.minecraft.network.message.MessageSignatureData.Indexed#write + var totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); + var previousMessageSignatures = new Tuple[totalPreviousMessages]; + + for (var i = 0; i < totalPreviousMessages; i++) + { + // net.minecraft.network.message.MessageSignatureData.Indexed#fromBuf + var messageId = dataTypes.ReadNextVarInt(packetData) - 1; + if (messageId == -1) + previousMessageSignatures[i] = new Tuple(messageId, + dataTypes.ReadNextByteArray(packetData, 256)); + else + previousMessageSignatures[i] = new Tuple(messageId, null); + } + + // Other + var unsignedChatContent = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextString(packetData) + : null; + + var filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + + if (filterType == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + // Network Target + // net.minecraft.network.message.MessageType.Serialized#write + var chatTypeId = dataTypes.ReadNextVarInt(packetData); + var chatName = dataTypes.ReadNextString(packetData); + var targetName = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextString(packetData) + : null; + + var messageTypeEnum = + ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + + var chatInfo = + Json.ParseJson(targetName ?? chatName).Properties; + var senderDisplayName = + (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]) + .StringValue; + string? senderTeamName = null; + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || + messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0] + .Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + var player = handler.GetPlayerInfo(senderUuid); + if (player != null && (player.DisplayName != null || player.Name != null) && + string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + bool verifyResult; + if (!isOnlineMode || messageSignature == null) + verifyResult = false; + else + { + if (senderUuid == handler.GetUserUuid()) + verifyResult = true; + else + { + var player = handler.GetPlayerInfo(senderUuid); + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else + { + verifyResult = player.VerifyMessage(message, senderUuid, player.ChatUuid, + index, timestamp, salt, ref messageSignature, + previousMessageSignatures); + } + } + } + + ChatMessage chat = new(message, false, chatTypeId, senderUuid, unsignedChatContent, + senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); + lock (MessageSigningLock) + Acknowledge(chat); + handler.OnTextReceived(chat); + } + + break; + case PacketTypesIn.ChunkBatchFinished: + dataTypes.ReadNextVarInt(packetData); // Number of chunks received + SendChunkBatchReceived( + // ReSharper disable once PossibleLossOfFraction + 25 / (DateTimeOffset.Now.ToUnixTimeMilliseconds() - lastCHunkBatchStartedAt) + ); + break; + case PacketTypesIn.ChunkBatchStarted: + lastCHunkBatchStartedAt = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + break; + case PacketTypesIn.StartConfiguration: + SendAcknowledgeConfiguration(); + break; + case PacketTypesIn.HideMessage: + var hideMessageSignature = dataTypes.ReadNextByteArray(packetData); + ConsoleIO.WriteLine( + $"HideMessage was not processed! (SigLen={hideMessageSignature.Length})"); + break; + case PacketTypesIn.SystemChat: + var systemMessage = dataTypes.ReadNextString(packetData); + + if (protocolVersion >= MC_1_19_3_Version) + { + var isOverlay = dataTypes.ReadNextBool(packetData); + if (isOverlay) + { + if (!Config.Main.Advanced.ShowXPBarMessages) + break; + } + else + { + if (!Config.Main.Advanced.ShowSystemMessages) + break; + } + + handler.OnTextReceived(new(systemMessage, null, true, -1, Guid.Empty, true)); + } + else + { + var msgType = dataTypes.ReadNextVarInt(packetData); + if (msgType == 1 && !Config.Main.Advanced.ShowSystemMessages) + break; + handler.OnTextReceived(new(systemMessage, null, true, msgType, Guid.Empty, true)); + } + + break; + case PacketTypesIn.ProfilelessChatMessage: + var message_ = dataTypes.ReadNextString(packetData); + var messageType_ = dataTypes.ReadNextVarInt(packetData); + var messageName = dataTypes.ReadNextString(packetData); + var targetName_ = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextString(packetData) + : null; + ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, true, messageType_, + Guid.Empty, true); + profilelessChat.isSenderJson = true; + handler.OnTextReceived(profilelessChat); + break; + case PacketTypesIn.CombatEvent: + // 1.8 - 1.16.5 + if (protocolVersion is >= MC_1_8_Version and <= MC_1_16_5_Version) + { + var eventType = (CombatEventType)dataTypes.ReadNextVarInt(packetData); + + if (eventType == CombatEventType.EntityDead) + { + dataTypes.SkipNextVarInt(packetData); + + handler.OnPlayerKilled( + dataTypes.ReadNextInt(packetData), + ChatParser.ParseText(dataTypes.ReadNextString(packetData)) + ); + } + } + + break; + case PacketTypesIn.DeathCombatEvent: + dataTypes.SkipNextVarInt(packetData); + + handler.OnPlayerKilled( + protocolVersion >= MC_1_20_Version ? -1 : dataTypes.ReadNextInt(packetData), + ChatParser.ParseText(dataTypes.ReadNextString(packetData)) + ); + + break; + case PacketTypesIn.DamageEvent: // 1.19.4 + if (handler.GetEntityHandlingEnabled() && protocolVersion >= MC_1_19_4_Version) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + var sourceTypeId = dataTypes.ReadNextVarInt(packetData); + var sourceCauseId = dataTypes.ReadNextVarInt(packetData); + var sourceDirectId = dataTypes.ReadNextVarInt(packetData); + + Location? sourcePosition; + if (dataTypes.ReadNextBool(packetData)) + { + sourcePosition = new Location() + { + X = dataTypes.ReadNextDouble(packetData), + Y = dataTypes.ReadNextDouble(packetData), + Z = dataTypes.ReadNextDouble(packetData) + }; + } + + // TODO: Write a function to use this data ? But seems not too useful + } + + break; + case PacketTypesIn.MessageHeader: // 1.19.2 only + if (protocolVersion == MC_1_19_2_Version) + { + var precedingSignature = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextByteArray(packetData) + : null; + + var senderUuid = dataTypes.ReadNextUUID(packetData); + var headerSignature = dataTypes.ReadNextByteArray(packetData); + var bodyDigest = dataTypes.ReadNextByteArray(packetData); + + bool verifyResult; + + if (!isOnlineMode) + verifyResult = false; + else if (senderUuid == handler.GetUserUuid()) + verifyResult = true; + else + { + var player = handler.GetPlayerInfo(senderUuid); + + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else + { + var lastVerifyResult = player.IsMessageChainLegal(); + verifyResult = player.VerifyMessageHead(ref precedingSignature, + ref headerSignature, ref bodyDigest); + if (lastVerifyResult && !verifyResult) + log.Warn(string.Format(Translations.chat_message_chain_broken, + player.Name)); + } + } + } + + break; + case PacketTypesIn.Respawn: + string? dimensionTypeNameRespawn = null; + Dictionary? dimensionTypeRespawn = null; + if (protocolVersion >= MC_1_16_Version) + { + switch (protocolVersion) + { + case >= MC_1_19_Version: + dimensionTypeNameRespawn = + dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + break; + case >= MC_1_16_2_Version: + dimensionTypeRespawn = + dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound + break; + default: + dataTypes.ReadNextString(packetData); + break; + } + + currentDimension = 0; + } + else + { + // 1.15 and below + currentDimension = dataTypes.ReadNextInt(packetData); + } + + switch (protocolVersion) + { + case >= MC_1_16_Version: + { + var dimensionName = + dataTypes.ReadNextString( + packetData); // Dimension Name (World Name) - 1.16 and above + + if (handler.GetTerrainEnabled()) + { + switch (protocolVersion) + { + case >= MC_1_16_2_Version and <= MC_1_18_2_Version: + World.StoreOneDimension(dimensionName, dimensionTypeRespawn!); + World.SetDimension(dimensionName); + break; + case >= MC_1_19_Version: + World.SetDimension(dimensionTypeNameRespawn!); + break; + } + } + + break; + } + case < MC_1_14_Version: + dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below + break; + } + + if (protocolVersion >= MC_1_15_Version) + dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above + dataTypes.ReadNextByte(packetData); // Gamemode + + switch (protocolVersion) + { + case >= MC_1_16_Version: + dataTypes.ReadNextByte(packetData); // Previous Game mode - 1.16 and above + break; + case < MC_1_16_Version: + dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below + break; + } + + if (protocolVersion >= MC_1_16_Version) + { + dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above + dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above + + if (protocolVersion < MC_1_20_2_Version) + dataTypes.ReadNextBool(packetData); // Copy metadata (Data Kept) - 1.16 - 1.20.2 + } + + if (protocolVersion >= MC_1_19_Version) + { + if (dataTypes.ReadNextBool(packetData)) // Has death location + { + dataTypes.ReadNextString(packetData); // Death dimension name: Identifier + dataTypes.ReadNextLocation(packetData); // Death location + } + } + + if (protocolVersion >= MC_1_20_Version) + dataTypes.ReadNextVarInt(packetData); // Portal Cooldown + + if (protocolVersion >= MC_1_20_2_Version) + dataTypes.ReadNextBool(packetData); // Copy metadata (Data Kept) - 1.20.2 and above + + handler.OnRespawn(); + break; + case PacketTypesIn.PlayerPositionAndLook: + { + // These always need to be read, since we need the field after them for teleport confirm + var location = new Location( + dataTypes.ReadNextDouble(packetData), // X + dataTypes.ReadNextDouble(packetData), // Y + dataTypes.ReadNextDouble(packetData) // Z + ); + + var yaw = dataTypes.ReadNextFloat(packetData); + var pitch = dataTypes.ReadNextFloat(packetData); + var locMask = dataTypes.ReadNextByte(packetData); + + // entity handling require player pos for distance calculating + if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled()) + { + if (protocolVersion >= MC_1_8_Version) + { + var currentLocation = handler.GetCurrentLocation(); + location.X = (locMask & 1 << 0) != 0 ? currentLocation.X + location.X : location.X; + location.Y = (locMask & 1 << 1) != 0 ? currentLocation.Y + location.Y : location.Y; + location.Z = (locMask & 1 << 2) != 0 ? currentLocation.Z + location.Z : location.Z; + } + } + + if (protocolVersion >= MC_1_9_Version) + { + var teleportId = dataTypes.ReadNextVarInt(packetData); + + if (teleportId < 0) + { + yaw = LastYaw; + pitch = LastPitch; + } + else + { + LastYaw = yaw; + LastPitch = pitch; + } + + handler.UpdateLocation(location, yaw, pitch); + + // Teleport confirm packet + SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportId)); + + if (Config.Main.Advanced.TemporaryFixBadpacket) + { + SendLocationUpdate(location, true, yaw, pitch, true); + + if (teleportId == 1) + SendLocationUpdate(location, true, yaw, pitch, true); + } + } + else + { + handler.UpdateLocation(location, yaw, pitch); + LastYaw = yaw; + LastPitch = pitch; + } + + if (protocolVersion is >= MC_1_17_Version and < MC_1_19_4_Version) + dataTypes.ReadNextBool(packetData); // Dismount Vehicle - 1.17 to 1.19.3 + } + break; + case PacketTypesIn.ChunkData: + if (handler.GetTerrainEnabled()) + { + Interlocked.Increment(ref handler.GetWorld().chunkCnt); + Interlocked.Increment(ref handler.GetWorld().chunkLoadNotCompleted); + + var chunkX = dataTypes.ReadNextInt(packetData); + var chunkZ = dataTypes.ReadNextInt(packetData); + if (protocolVersion >= MC_1_17_Version) + { + ulong[]? verticalStripBitmask = null; + + if (protocolVersion is MC_1_17_Version or MC_1_17_1_Version) + verticalStripBitmask = + dataTypes.ReadNextULongArray( + packetData); // Bit Mask Length and Primary Bit Mask + + dataTypes.ReadNextNbt(packetData); // Heightmaps + + if (protocolVersion is MC_1_17_Version or MC_1_17_1_Version) + { + var biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length + for (var i = 0; i < biomesLength; i++) + dataTypes.SkipNextVarInt(packetData); // Biomes + } + + var dataSize = dataTypes.ReadNextVarInt(packetData); // Size + + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + + // Block Entity data: ignored + // Trust edges: ignored (Removed in 1.20) + // Light data: ignored + } + else + { + var chunksContinuous = dataTypes.ReadNextBool(packetData); + if (protocolVersion >= MC_1_16_Version && protocolVersion <= MC_1_16_1_Version) + dataTypes.ReadNextBool(packetData); // Ignore old data - 1.16 to 1.16.1 only + var chunkMask = protocolVersion >= MC_1_9_Version + ? (ushort)dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextUShort(packetData); + if (protocolVersion < MC_1_8_Version) + { + var addBitmap = dataTypes.ReadNextUShort(packetData); + var compressedDataSize = dataTypes.ReadNextInt(packetData); + var compressed = dataTypes.ReadData(compressedDataSize, packetData); + var decompressed = ZlibUtils.Decompress(compressed); + + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, + currentDimension == 0, chunksContinuous, currentDimension, + new Queue(decompressed)); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + } + else + { + if (protocolVersion >= MC_1_14_Version) + dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above + var biomesLength = 0; + if (protocolVersion >= MC_1_16_2_Version) + if (chunksContinuous) + biomesLength = + dataTypes.ReadNextVarInt( + packetData); // Biomes length - 1.16.2 and above + if (protocolVersion >= MC_1_15_Version && chunksContinuous) + { + if (protocolVersion >= MC_1_16_2_Version) + { + for (var i = 0; i < biomesLength; i++) + { + // Biomes - 1.16.2 and above + // Don't use ReadNextVarInt because it cost too much time + dataTypes.SkipNextVarInt(packetData); + } + } + else dataTypes.DropData(1024 * 4, packetData); // Biomes - 1.15 and above + } + + var dataSize = dataTypes.ReadNextVarInt(packetData); + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, + chunksContinuous, currentDimension, packetData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + } + } + } + + break; + case PacketTypesIn.ChunksBiomes: // 1.19.4 + // Biomes are not handled by MCC + break; + case PacketTypesIn.MapData: + if (protocolVersion < MC_1_8_Version) + break; + + var mapId = dataTypes.ReadNextVarInt(packetData); + var scale = dataTypes.ReadNextByte(packetData); + + + // 1.9 + + var trackingPosition = true; + + // 1.14+ + var locked = false; + + // 1.17+ (locked and trackingPosition switched places) + if (protocolVersion >= MC_1_17_Version) + { + if (protocolVersion >= MC_1_14_Version) + locked = dataTypes.ReadNextBool(packetData); + + if (protocolVersion >= MC_1_9_Version) + trackingPosition = dataTypes.ReadNextBool(packetData); + } + else + { + if (protocolVersion >= MC_1_9_Version) + trackingPosition = dataTypes.ReadNextBool(packetData); + + if (protocolVersion >= MC_1_14_Version) + locked = dataTypes.ReadNextBool(packetData); + } + + var iconCount = 0; + List icons = new(); + + // 1,9 + = needs tracking position to be true to get the icons + if (protocolVersion <= MC_1_16_5_Version || trackingPosition) + { + iconCount = dataTypes.ReadNextVarInt(packetData); + + for (var i = 0; i < iconCount; i++) + { + MapIcon mapIcon = new(); + + switch (protocolVersion) + { + // 1.8 - 1.13 + case < MC_1_13_2_Version: + { + var directionAndType = dataTypes.ReadNextByte(packetData); + byte direction, type; + + // 1.12.2+ + if (protocolVersion >= MC_1_12_2_Version) + { + direction = (byte)(directionAndType & 0xF); + type = (byte)(directionAndType >> 4 & 0xF); + } + else // 1.8 - 1.12 + { + direction = (byte)(directionAndType >> 4 & 0xF); + type = (byte)(directionAndType & 0xF); + } + + mapIcon.Type = (MapIconType)type; + mapIcon.Direction = direction; + break; + } + // 1.13.2+ + case >= MC_1_13_2_Version: + mapIcon.Type = (MapIconType)dataTypes.ReadNextVarInt(packetData); + break; + } + + mapIcon.X = dataTypes.ReadNextByte(packetData); + mapIcon.Z = dataTypes.ReadNextByte(packetData); + + // 1.13.2+ + if (protocolVersion >= MC_1_13_2_Version) + { + mapIcon.Direction = dataTypes.ReadNextByte(packetData); + + if (dataTypes.ReadNextBool(packetData)) // Has Display Name? + mapIcon.DisplayName = + ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + + icons.Add(mapIcon); + } + } + + var columnsUpdated = dataTypes.ReadNextByte(packetData); // width + byte rowsUpdated = 0; // height + byte mapColumnX = 0; + byte mapRowZ = 0; + byte[]? colors = null; + + if (columnsUpdated > 0) + { + rowsUpdated = dataTypes.ReadNextByte(packetData); // height + mapColumnX = dataTypes.ReadNextByte(packetData); + mapRowZ = dataTypes.ReadNextByte(packetData); + colors = dataTypes.ReadNextByteArray(packetData); + } + + handler.OnMapData(mapId, scale, trackingPosition, locked, icons, columnsUpdated, + rowsUpdated, mapColumnX, mapRowZ, colors); + break; + case PacketTypesIn.TradeList: + if (protocolVersion >= MC_1_14_Version && handler.GetInventoryEnabled()) + { + // MC 1.14 or greater + var windowId = dataTypes.ReadNextVarInt(packetData); + int size = dataTypes.ReadNextByte(packetData); + + List trades = new(); + for (var tradeId = 0; tradeId < size; tradeId++) + { + var trade = dataTypes.ReadNextTrade(packetData, itemPalette); + trades.Add(trade); + } + + VillagerInfo villagerInfo = new() + { + Level = dataTypes.ReadNextVarInt(packetData), + Experience = dataTypes.ReadNextVarInt(packetData), + IsRegularVillager = dataTypes.ReadNextBool(packetData), + CanRestock = dataTypes.ReadNextBool(packetData) + }; + handler.OnTradeList(windowId, trades, villagerInfo); + } + + break; + case PacketTypesIn.Title: + if (protocolVersion >= MC_1_8_Version) + { + var action2 = dataTypes.ReadNextVarInt(packetData); + var titleText = string.Empty; + var subtitleText = string.Empty; + var actionBarText = string.Empty; + var json = string.Empty; + var fadein = -1; + var stay = -1; + var fadeout = -1; + + if (protocolVersion >= MC_1_10_Version) + { + switch (action2) + { + case 0: + json = titleText; + titleText = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + break; + case 1: + json = subtitleText; + subtitleText = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + break; + case 2: + json = actionBarText; + actionBarText = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + break; + case 3: + fadein = dataTypes.ReadNextInt(packetData); + stay = dataTypes.ReadNextInt(packetData); + fadeout = dataTypes.ReadNextInt(packetData); + break; + } + } + else + { + switch (action2) + { + case 0: + json = titleText; + titleText = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + break; + case 1: + json = subtitleText; + subtitleText = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + break; + case 2: + fadein = dataTypes.ReadNextInt(packetData); + stay = dataTypes.ReadNextInt(packetData); + fadeout = dataTypes.ReadNextInt(packetData); + break; + } + } + + handler.OnTitle(action2, titleText, subtitleText, actionBarText, fadein, stay, fadeout, + json); + } + + break; + case PacketTypesIn.MultiBlockChange: + if (handler.GetTerrainEnabled()) + { + if (protocolVersion >= MC_1_16_2_Version) + { + var chunkSection = dataTypes.ReadNextLong(packetData); + var sectionX = (int)(chunkSection >> 42); + var sectionY = (int)(chunkSection << 44 >> 44); + var sectionZ = (int)(chunkSection << 22 >> 42); + + if (protocolVersion < MC_1_20_Version) + dataTypes.ReadNextBool(packetData); // Useless boolean (Related to light update) + + var blocksSize = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < blocksSize; i++) + { + var chunkSectionPosition = (ulong)dataTypes.ReadNextVarLong(packetData); + var blockId = (int)(chunkSectionPosition >> 12); + var localX = (int)(chunkSectionPosition >> 8 & 0x0F); + var localZ = (int)(chunkSectionPosition >> 4 & 0x0F); + var localY = (int)(chunkSectionPosition & 0x0F); + + var block = new Block((ushort)blockId); + var blockX = sectionX * 16 + localX; + var blockY = sectionY * 16 + localY; + var blockZ = sectionZ * 16 + localZ; + + Location location = new(blockX, blockY, blockZ); + + handler.OnBlockChange(location, block); + } + } + else + { + var chunkX = dataTypes.ReadNextInt(packetData); + var chunkZ = dataTypes.ReadNextInt(packetData); + var recordCount = protocolVersion < MC_1_8_Version + ? dataTypes.ReadNextShort(packetData) + : dataTypes.ReadNextVarInt(packetData); + + for (var i = 0; i < recordCount; i++) + { + byte locationXZ; + ushort blockIdMeta; + int blockY; + + if (protocolVersion < MC_1_8_Version) + { + blockIdMeta = dataTypes.ReadNextUShort(packetData); + blockY = dataTypes.ReadNextByte(packetData); + locationXZ = dataTypes.ReadNextByte(packetData); + } + else + { + locationXZ = dataTypes.ReadNextByte(packetData); + blockY = dataTypes.ReadNextByte(packetData); + blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData); + } + + var blockX = locationXZ >> 4; + var blockZ = locationXZ & 0x0F; + + Location location = new(chunkX, chunkZ, blockX, blockY, blockZ); + Block block = new(blockIdMeta); + handler.OnBlockChange(location, block); + } + } + } + + break; + case PacketTypesIn.ServerData: + var motd = "-"; + + var hasMotd = false; + if (protocolVersion < MC_1_19_4_Version) + { + hasMotd = dataTypes.ReadNextBool(packetData); + + if (hasMotd) + motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else + { + hasMotd = true; + motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + + var iconBase64 = "-"; + var hasIcon = dataTypes.ReadNextBool(packetData); + if (hasIcon) + iconBase64 = dataTypes.ReadNextString(packetData); + + var previewsChat = false; + if (protocolVersion < MC_1_19_3_Version) + previewsChat = dataTypes.ReadNextBool(packetData); + + handler.OnServerDataRecived(hasMotd, motd, hasIcon, iconBase64, previewsChat); + break; + case PacketTypesIn.BlockChange: + if (handler.GetTerrainEnabled()) + { + if (protocolVersion < MC_1_8_Version) + { + var blockX = dataTypes.ReadNextInt(packetData); + var blockY = dataTypes.ReadNextByte(packetData); + var blockZ = dataTypes.ReadNextInt(packetData); + var blockId = (short)dataTypes.ReadNextVarInt(packetData); + var blockMeta = dataTypes.ReadNextByte(packetData); + + Location location = new(blockX, blockY, blockZ); + Block block = new(blockId, blockMeta); + handler.OnBlockChange(location, block); + } + else + { + var location = dataTypes.ReadNextLocation(packetData); + Block block = new((ushort)dataTypes.ReadNextVarInt(packetData)); + handler.OnBlockChange(location, block); + } + } + + break; + case PacketTypesIn.SetDisplayChatPreview: + handler.OnChatPreviewSettingUpdate(dataTypes.ReadNextBool(packetData)); // Preview Chat Settings + break; + case PacketTypesIn.ChatSuggestions: + break; + case PacketTypesIn.MapChunkBulk: + if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) + { + int chunkCount; + bool hasSkyLight; + var chunkData = packetData; + + //Read global fields + if (protocolVersion < MC_1_8_Version) + { + chunkCount = dataTypes.ReadNextShort(packetData); + var compressedDataSize = dataTypes.ReadNextInt(packetData); + hasSkyLight = dataTypes.ReadNextBool(packetData); + var compressed = dataTypes.ReadData(compressedDataSize, packetData); + var decompressed = ZlibUtils.Decompress(compressed); + chunkData = new Queue(decompressed); + } + else + { + hasSkyLight = dataTypes.ReadNextBool(packetData); + chunkCount = dataTypes.ReadNextVarInt(packetData); + } + + //Read chunk records + var chunkXs = new int[chunkCount]; + var chunkZs = new int[chunkCount]; + var chunkMasks = new ushort[chunkCount]; + var addBitmaps = new ushort[chunkCount]; + for (var chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) + { + chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); + chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); + chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData); + addBitmaps[chunkColumnNo] = protocolVersion < MC_1_8_Version + ? dataTypes.ReadNextUShort(packetData) + : (ushort)0; + } + + //Process chunk records + for (var chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) + { + pTerrain.ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], + chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, + currentDimension, chunkData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + } + } + + break; + case PacketTypesIn.UnloadChunk: + if (protocolVersion >= MC_1_9_Version && handler.GetTerrainEnabled()) + { + var chunkX = dataTypes.ReadNextInt(packetData); + var chunkZ = dataTypes.ReadNextInt(packetData); + + // Warning: It is legal to include unloaded chunks in the UnloadChunk packet. + // Since chunks that have not been loaded are not recorded, this may result + // in loading chunks that should be unloaded and inaccurate statistics. + if (handler.GetWorld()[chunkX, chunkZ] != null) + Interlocked.Decrement(ref handler.GetWorld().chunkCnt); + + handler.GetWorld()[chunkX, chunkZ] = null; + } + + break; + case PacketTypesIn.ChangeGameState: + if (protocolVersion >= MC_1_15_2_Version) + { + var reason = dataTypes.ReadNextByte(packetData); + var state = dataTypes.ReadNextFloat(packetData); + handler.OnGameEvent(reason, state); + } + + break; + case PacketTypesIn.PlayerInfo: + if (protocolVersion >= MC_1_19_3_Version) + { + var actionBitset = dataTypes.ReadNextByte(packetData); + var numberOfActions = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < numberOfActions; i++) + { + var playerUuid = dataTypes.ReadNextUUID(packetData); + + PlayerInfo player; + if ((actionBitset & 1 << 0) > 0) // Actions bit 0: add player + { + var name = dataTypes.ReadNextString(packetData); + var numberOfProperties = dataTypes.ReadNextVarInt(packetData); + for (var j = 0; j < numberOfProperties; ++j) + { + dataTypes.SkipNextString(packetData); + dataTypes.SkipNextString(packetData); + if (dataTypes.ReadNextBool(packetData)) + dataTypes.SkipNextString(packetData); + } + + player = new(name, playerUuid); + handler.OnPlayerJoin(player); + } + else + { + var playerGet = handler.GetPlayerInfo(playerUuid); + if (playerGet == null) + { + player = new(string.Empty, playerUuid); + handler.OnPlayerJoin(player); + } + else + { + player = playerGet; + } + } + + if ((actionBitset & 1 << 1) > 0) // Actions bit 1: initialize chat + { + var hasSignatureData = dataTypes.ReadNextBool(packetData); + + if (hasSignatureData) + { + var chatUuid = dataTypes.ReadNextUUID(packetData); + var publicKeyExpiryTime = dataTypes.ReadNextLong(packetData); + var encodedPublicKey = dataTypes.ReadNextByteArray(packetData); + var publicKeySignature = dataTypes.ReadNextByteArray(packetData); + player.SetPublicKey(chatUuid, publicKeyExpiryTime, encodedPublicKey, + publicKeySignature); + + if (playerUuid == handler.GetUserUuid()) + { + log.Debug($"Receive ChatUuid = {chatUuid}"); + this.chatUuid = chatUuid; + } + } + else + { + player.ClearPublicKey(); + + if (playerUuid == handler.GetUserUuid()) + log.Debug("Receive ChatUuid = Empty"); + } + + if (playerUuid == handler.GetUserUuid()) + { + receivePlayerInfo = true; + if (receiveDeclareCommands) + handler.SetCanSendMessage(true); + } + } + + if ((actionBitset & 1 << 2) > 0) // Actions bit 2: update gamemode + handler.OnGamemodeUpdate(playerUuid, dataTypes.ReadNextVarInt(packetData)); + + if ((actionBitset & 1 << 3) > 0) // Actions bit 3: update listed + player.Listed = dataTypes.ReadNextBool(packetData); + + if ((actionBitset & 1 << 4) > 0) // Actions bit 4: update latency + { + var latency = dataTypes.ReadNextVarInt(packetData); + handler.OnLatencyUpdate(playerUuid, latency); //Update latency; + } + + // Actions bit 5: update display name + if ((actionBitset & 1 << 5) <= 0) continue; + player.DisplayName = dataTypes.ReadNextBool(packetData) + ? dataTypes.ReadNextString(packetData) + : null; + } + } + else if (protocolVersion >= MC_1_8_Version) + { + var action = dataTypes.ReadNextVarInt(packetData); // Action Name + var numberOfPlayers = dataTypes.ReadNextVarInt(packetData); // Number Of Players + + for (var i = 0; i < numberOfPlayers; i++) + { + var uuid = dataTypes.ReadNextUUID(packetData); // Player UUID + + switch (action) + { + case 0x00: //Player Join (Add player since 1.19) + var name = dataTypes.ReadNextString(packetData); // Player name + var propNum = + dataTypes.ReadNextVarInt( + packetData); // Number of properties in the following array + + // Property: Tuple[propNum] : null; + for (var p = 0; p < propNum; p++) + { + var propertyName = + dataTypes.ReadNextString(packetData); // Name: String (32767) + var val = + dataTypes.ReadNextString(packetData); // Value: String (32767) + string? propertySignature = null; + if (dataTypes.ReadNextBool(packetData)) // Is Signed + propertySignature = + dataTypes.ReadNextString( + packetData); // Signature: String (32767) + if (useProperty) + properties![p] = new(propertyName, val, propertySignature); + } +#pragma warning restore CS0162 // Unreachable code detected + + var gameMode = dataTypes.ReadNextVarInt(packetData); // Gamemode + handler.OnGamemodeUpdate(uuid, gameMode); + + var ping = dataTypes.ReadNextVarInt(packetData); // Ping + + string? displayName = null; + if (dataTypes.ReadNextBool(packetData)) // Has display name + displayName = dataTypes.ReadNextString(packetData); // Display name + + // 1.19 Additions + long? keyExpiration = null; + byte[]? publicKey = null, signature = null; + if (protocolVersion >= MC_1_19_Version) + { + if (dataTypes.ReadNextBool( + packetData)) // Has Sig Data (if true, red the following fields) + { + keyExpiration = dataTypes.ReadNextLong(packetData); // Timestamp + + var publicKeyLength = + dataTypes.ReadNextVarInt(packetData); // Public Key Length + if (publicKeyLength > 0) + publicKey = dataTypes.ReadData(publicKeyLength, + packetData); // Public key + + var signatureLength = + dataTypes.ReadNextVarInt(packetData); // Signature Length + if (signatureLength > 0) + signature = dataTypes.ReadData(signatureLength, + packetData); // Public key + } + } + + handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, + displayName, keyExpiration, publicKey, signature)); + break; + case 0x01: // Update Gamemode + handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData)); + break; + case 0x02: // Update latency + var latency = dataTypes.ReadNextVarInt(packetData); + handler.OnLatencyUpdate(uuid, latency); // Update latency; + break; + case 0x03: // Update display name + if (dataTypes.ReadNextBool(packetData)) + { + var player = handler.GetPlayerInfo(uuid); + if (player != null) + player.DisplayName = dataTypes.ReadNextString(packetData); + else + dataTypes.SkipNextString(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 + { + var name = dataTypes.ReadNextString(packetData); + var online = dataTypes.ReadNextBool(packetData); + var ping = dataTypes.ReadNextShort(packetData); + var fakeUuid = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16) + .ToArray()); + if (online) + handler.OnPlayerJoin(new PlayerInfo(name, fakeUuid)); + else handler.OnPlayerLeave(fakeUuid); + } + + break; + case PacketTypesIn.PlayerRemove: + var numberOfLeavePlayers = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < numberOfLeavePlayers; ++i) + { + var playerUuid = dataTypes.ReadNextUUID(packetData); + handler.OnPlayerLeave(playerUuid); + } + + break; + case PacketTypesIn.TabComplete: + var oldTransactionId = autocomplete_transaction_id; + if (protocolVersion >= MC_1_13_Version) + { + autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData); + dataTypes.ReadNextVarInt(packetData); // Start of text to replace + dataTypes.ReadNextVarInt(packetData); // Length of text to replace + } + + var autocompleteCount = dataTypes.ReadNextVarInt(packetData); + var autocompleteResult = new string[autocompleteCount]; + for (var i = 0; i < autocompleteCount; i++) + { + autocompleteResult[i] = dataTypes.ReadNextString(packetData); + if (protocolVersion < MC_1_13_Version) continue; + + // Skip optional tooltip for each tab-complete resul`t + if (dataTypes.ReadNextBool(packetData)) + dataTypes.SkipNextString(packetData); + } + + handler.OnAutoCompleteDone(oldTransactionId, autocompleteResult); + break; + case PacketTypesIn.PluginMessage: + var channel = dataTypes.ReadNextString(packetData); + // Length is unneeded as the whole remaining packetData is the entire payload of the packet. + if (protocolVersion < MC_1_8_Version) + pForge.ReadNextVarShort(packetData); + handler.OnPluginChannelMessage(channel, packetData.ToArray()); + return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); + case PacketTypesIn.Disconnect: + handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, + ChatParser.ParseText(dataTypes.ReadNextString(packetData))); + return false; + case PacketTypesIn.SetCompression: + if (protocolVersion is >= MC_1_8_Version and < MC_1_9_Version) + compression_treshold = dataTypes.ReadNextVarInt(packetData); + break; + case PacketTypesIn.OpenWindow: + if (handler.GetInventoryEnabled()) + { + if (protocolVersion < MC_1_14_Version) + { + // MC 1.13 or lower + var windowId = dataTypes.ReadNextByte(packetData); + var type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "") + .ToUpper(); + var inventoryType = + (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type); + var title = dataTypes.ReadNextString(packetData); + var slots = dataTypes.ReadNextByte(packetData); + Container inventory = new(windowId, inventoryType, ChatParser.ParseText(title)); + handler.OnInventoryOpen(windowId, inventory); + } + else + { + // MC 1.14 or greater + var windowId = dataTypes.ReadNextVarInt(packetData); + var windowType = dataTypes.ReadNextVarInt(packetData); + var title = dataTypes.ReadNextString(packetData); + Container inventory = new(windowId, windowType, ChatParser.ParseText(title)); + handler.OnInventoryOpen(windowId, inventory); + } + } + + break; + case PacketTypesIn.CloseWindow: + if (handler.GetInventoryEnabled()) + { + var windowId = dataTypes.ReadNextByte(packetData); + lock (window_actions) + { + window_actions[windowId] = 0; + } + + handler.OnInventoryClose(windowId); + } + + break; + case PacketTypesIn.WindowItems: + if (handler.GetInventoryEnabled()) + { + var windowId = dataTypes.ReadNextByte(packetData); + var stateId = -1; + var elements = 0; + + if (protocolVersion >= MC_1_17_1_Version) + { + // State ID and Elements as VarInt - 1.17.1 and above + stateId = dataTypes.ReadNextVarInt(packetData); + elements = dataTypes.ReadNextVarInt(packetData); + } + else + { + // Elements as Short - 1.17.0 and below + dataTypes.ReadNextShort(packetData); + } + + Dictionary inventorySlots = new(); + for (var slotId = 0; slotId < elements; slotId++) + { + var item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + if (item != null) + inventorySlots[slotId] = item; + } + + if (protocolVersion >= MC_1_17_1_Version) // Carried Item - 1.17.1 and above + dataTypes.ReadNextItemSlot(packetData, itemPalette); + + handler.OnWindowItems(windowId, inventorySlots, stateId); + } + + break; + case PacketTypesIn.WindowProperty: + var containerId = dataTypes.ReadNextByte(packetData); + var propertyId = dataTypes.ReadNextShort(packetData); + var propertyValue = dataTypes.ReadNextShort(packetData); + handler.OnWindowProperties(containerId, propertyId, propertyValue); + break; + case PacketTypesIn.SetSlot: + if (handler.GetInventoryEnabled()) + { + var windowId = dataTypes.ReadNextByte(packetData); + var stateId = -1; + if (protocolVersion >= MC_1_17_1_Version) + stateId = dataTypes.ReadNextVarInt(packetData); // State ID - 1.17.1 and above + var slotId = dataTypes.ReadNextShort(packetData); + var item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnSetSlot(windowId, slotId, item, stateId); + } + + break; + case PacketTypesIn.WindowConfirmation: + if (handler.GetInventoryEnabled()) + { + var windowId = dataTypes.ReadNextByte(packetData); + var actionId = dataTypes.ReadNextShort(packetData); + var accepted = dataTypes.ReadNextBool(packetData); + if (!accepted) + SendWindowConfirmation(windowId, actionId, true); + } + + break; + case PacketTypesIn.ResourcePackSend: + var url = dataTypes.ReadNextString(packetData); + var hash = dataTypes.ReadNextString(packetData); + var forced = true; // Assume forced for MC 1.16 and below + if (protocolVersion >= MC_1_17_Version) + { + forced = dataTypes.ReadNextBool(packetData); + var hasPromptMessage = + dataTypes.ReadNextBool(packetData); // Has Prompt Message (Boolean) - 1.17 and above + if (hasPromptMessage) + dataTypes.SkipNextString( + packetData); // Prompt Message (Optional Chat) - 1.17 and above + } + + // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056) + if (!url.StartsWith("http") && hash.Length != 40) // Some server may have null hash value + break; + + //Send back "accepted" and "successfully loaded" responses for plugins or server config making use of resource pack mandatory + var responseHeader = Array.Empty(); + if (protocolVersion < + MC_1_10_Version) //MC 1.10 does not include resource pack hash in responses + responseHeader = dataTypes.ConcatBytes(DataTypes.GetVarInt(hash.Length), + Encoding.UTF8.GetBytes(hash)); + SendPacket(PacketTypesOut.ResourcePackStatus, + dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(3))); // Accepted pack + SendPacket(PacketTypesOut.ResourcePackStatus, + dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(0))); // Successfully loaded + break; + case PacketTypesIn.SpawnEntity: + if (handler.GetEntityHandlingEnabled()) + { + var entity = dataTypes.ReadNextEntity(packetData, entityPalette, false); + handler.OnSpawnEntity(entity); + } + + break; + case PacketTypesIn.EntityEquipment: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + if (protocolVersion >= MC_1_16_Version) + { + bool hasNext; + do + { + var bitsData = dataTypes.ReadNextByte(packetData); + // Top bit set if another entry follows, and otherwise unset if this is the last item in the array + hasNext = bitsData >> 7 == 1; + var slot2 = bitsData >> 1; + var item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnEntityEquipment(entityId, slot2, item); + } while (hasNext); + } + else + { + var slot2 = protocolVersion < MC_1_9_Version + ? dataTypes.ReadNextShort(packetData) + : dataTypes.ReadNextVarInt(packetData); + + var item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnEntityEquipment(entityId, slot2, item); + } + } + + break; + case PacketTypesIn.SpawnLivingEntity: + if (handler.GetEntityHandlingEnabled()) + { + var 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 PacketTypesIn.SpawnPlayer: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + var uuid = dataTypes.ReadNextUUID(packetData); + double x, y, z; + + if (protocolVersion < MC_1_9_Version) + { + x = dataTypes.ReadNextInt(packetData) / 32.0D; + y = dataTypes.ReadNextInt(packetData) / 32.0D; + z = dataTypes.ReadNextInt(packetData) / 32.0D; + } + else + { + x = dataTypes.ReadNextDouble(packetData); + y = dataTypes.ReadNextDouble(packetData); + z = dataTypes.ReadNextDouble(packetData); + } + + var yaw = dataTypes.ReadNextByte(packetData); + var pitch = dataTypes.ReadNextByte(packetData); + handler.OnSpawnPlayer(entityId, uuid, new(x, y, z), yaw, pitch); + } + + break; + case PacketTypesIn.EntityEffect: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + var effectId = protocolVersion >= MC_1_18_2_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData); + + if (Enum.TryParse(effectId.ToString(), out Effects effect)) + { + var amplifier = dataTypes.ReadNextByte(packetData); + var duration = dataTypes.ReadNextVarInt(packetData); + var flags = dataTypes.ReadNextByte(packetData); + var hasFactorData = false; + Dictionary? factorCodec = null; + + if (protocolVersion >= MC_1_19_Version) + { + hasFactorData = dataTypes.ReadNextBool(packetData); + if (hasFactorData) + factorCodec = dataTypes.ReadNextNbt(packetData); + } + + handler.OnEntityEffect(entityId, effect, amplifier, duration, flags, hasFactorData, + factorCodec); + } + } + + break; + case PacketTypesIn.DestroyEntities: + if (handler.GetEntityHandlingEnabled()) + { + var entityCount = 1; // 1.17.0 has only one entity per packet + if (protocolVersion != MC_1_17_Version) + entityCount = + dataTypes.ReadNextVarInt(packetData); // All other versions have a "count" field + + var entityList = new int[entityCount]; + for (var i = 0; i < entityCount; i++) + entityList[i] = dataTypes.ReadNextVarInt(packetData); + + handler.OnDestroyEntities(entityList); + } + + break; + case PacketTypesIn.EntityPosition: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + double deltaX, deltaY, deltaZ; + + if (protocolVersion < MC_1_9_Version) + { + deltaX = Convert.ToDouble(dataTypes.ReadNextByte(packetData)); + deltaY = Convert.ToDouble(dataTypes.ReadNextByte(packetData)); + deltaZ = Convert.ToDouble(dataTypes.ReadNextByte(packetData)); + } + else + { + deltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + deltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + deltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + } + + var isOnGround = dataTypes.ReadNextBool(packetData); + deltaX = deltaX / (128 * 32); + deltaY = deltaY / (128 * 32); + deltaZ = deltaZ / (128 * 32); + + handler.OnEntityPosition(entityId, deltaX, deltaY, deltaZ, isOnGround); + } + + break; + case PacketTypesIn.EntityPositionAndRotation: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + double deltaX, deltaY, deltaZ; + + if (protocolVersion < MC_1_9_Version) + { + deltaX = dataTypes.ReadNextByte(packetData) / 32.0D; + deltaY = dataTypes.ReadNextByte(packetData) / 32.0D; + deltaZ = dataTypes.ReadNextByte(packetData) / 32.0D; + } + else + { + deltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + deltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + deltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + } + + + var yaw = dataTypes.ReadNextByte(packetData) * (1F / 256) * 360; + var pitch = dataTypes.ReadNextByte(packetData) * (1F / 256) * 360; + var isOnGround = dataTypes.ReadNextBool(packetData); + deltaX = deltaX / (128 * 32); + deltaY = deltaY / (128 * 32); + deltaZ = deltaZ / (128 * 32); + + handler.OnEntityPosition(entityId, deltaX, deltaY, deltaZ, yaw, pitch, isOnGround); + } + + break; + case PacketTypesIn.EntityRotation: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + var yaw = dataTypes.ReadNextByte(packetData) * (1F / 256) * 360; + var pitch = dataTypes.ReadNextByte(packetData) * (1F / 256) * 360; + var isOnGround = dataTypes.ReadNextBool(packetData); + + handler.OnEntityRotation(entityId, yaw, pitch, isOnGround); + } + + break; + case PacketTypesIn.EntityProperties: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + var numberOfProperties = protocolVersion >= MC_1_17_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextInt(packetData); + + Dictionary keys = new(); + for (var i = 0; i < numberOfProperties; i++) + { + var propertyKey = dataTypes.ReadNextString(packetData); + var propertyValue2 = dataTypes.ReadNextDouble(packetData); + + List op0 = new(); + List op1 = new(); + List op2 = new(); + + var numberOfModifiers = dataTypes.ReadNextVarInt(packetData); + for (var j = 0; j < numberOfModifiers; j++) + { + dataTypes.ReadNextUUID(packetData); + var amount = dataTypes.ReadNextDouble(packetData); + var 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) propertyValue2 += op0.Sum(); + if (op1.Count > 0) propertyValue2 *= 1 + op1.Sum(); + if (op2.Count > 0) propertyValue2 *= op2.Aggregate((a, _x) => a * _x); + keys.Add(propertyKey, propertyValue2); + } + + handler.OnEntityProperties(entityId, keys); + } + + break; + case PacketTypesIn.EntityMetadata: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + var metadata = dataTypes.ReadNextMetadata(packetData, itemPalette, entityMetadataPalette); + + // Also make a palette for field? Will be a lot of work + var healthField = protocolVersion switch + { + > MC_1_20_2_Version => throw new NotImplementedException(Translations + .exception_palette_healthfield), + // 1.17 and above + >= MC_1_17_Version => 9, + // 1.14 and above + >= MC_1_14_Version => 8, + // 1.10 and above + >= MC_1_10_Version => 7, + // 1.8 and above + >= MC_1_8_Version => 6, + _ => throw new NotImplementedException(Translations.exception_palette_healthfield) + }; + + if (metadata.TryGetValue(healthField, out var healthObj) && healthObj is float healthObj2) + handler.OnEntityHealth(entityId, healthObj2); + + handler.OnEntityMetadata(entityId, metadata); + } + + break; + case PacketTypesIn.EntityStatus: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextInt(packetData); + var status = dataTypes.ReadNextByte(packetData); + handler.OnEntityStatus(entityId, status); + } + + break; + case PacketTypesIn.TimeUpdate: + var worldAge = dataTypes.ReadNextLong(packetData); + var timeOfDay = dataTypes.ReadNextLong(packetData); + handler.OnTimeUpdate(worldAge, timeOfDay); + break; + case PacketTypesIn.EntityTeleport: + if (handler.GetEntityHandlingEnabled()) + { + var entityId = dataTypes.ReadNextVarInt(packetData); + double x, y, z; + + if (protocolVersion < MC_1_9_Version) + { + x = dataTypes.ReadNextInt(packetData) / 32.0D; + y = dataTypes.ReadNextInt(packetData) / 32.0D; + z = dataTypes.ReadNextInt(packetData) / 32.0D; + } + else + { + x = dataTypes.ReadNextDouble(packetData); + y = dataTypes.ReadNextDouble(packetData); + z = dataTypes.ReadNextDouble(packetData); + } + + var entityYaw = dataTypes.ReadNextByte(packetData); + var entityPitch = dataTypes.ReadNextByte(packetData); + var isOnGround = dataTypes.ReadNextBool(packetData); + handler.OnEntityTeleport(entityId, x, y, z, isOnGround); + } + + break; + case PacketTypesIn.UpdateHealth: + var health = dataTypes.ReadNextFloat(packetData); + var food = protocolVersion >= MC_1_8_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextShort(packetData); + dataTypes.ReadNextFloat(packetData); // Food Saturation + handler.OnUpdateHealth(health, food); + break; + case PacketTypesIn.SetExperience: + var experienceBar = dataTypes.ReadNextFloat(packetData); + var level = dataTypes.ReadNextVarInt(packetData); + var totalExperience = dataTypes.ReadNextVarInt(packetData); + handler.OnSetExperience(experienceBar, level, totalExperience); + break; + case PacketTypesIn.Explosion: + Location explosionLocation; + if (protocolVersion >= MC_1_19_3_Version) + explosionLocation = new(dataTypes.ReadNextDouble(packetData), + dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData)); + else + explosionLocation = new(dataTypes.ReadNextFloat(packetData), + dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData)); + + var explosionStrength = dataTypes.ReadNextFloat(packetData); + var explosionBlockCount = protocolVersion >= MC_1_17_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextInt(packetData); + + for (var i = 0; i < explosionBlockCount; i++) + dataTypes.ReadData(3, packetData); + + // Maybe use in the future when the physics are implemented + var playerVelocityX = dataTypes.ReadNextFloat(packetData); + var playerVelocityY = dataTypes.ReadNextFloat(packetData); + var playerVelocityZ = dataTypes.ReadNextFloat(packetData); + + handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); + break; + case PacketTypesIn.HeldItemChange: + handler.OnHeldItemChange(dataTypes.ReadNextByte(packetData)); // Slot + break; + case PacketTypesIn.ScoreboardObjective: + var objectiveName2 = dataTypes.ReadNextString(packetData); + var mode = dataTypes.ReadNextByte(packetData); + var objectiveValue = string.Empty; + var type2 = -1; + if (mode is 0 or 2) + { + objectiveValue = dataTypes.ReadNextString(packetData); + type2 = dataTypes.ReadNextVarInt(packetData); + } + + handler.OnScoreboardObjective(objectiveName2, mode, objectiveValue, type2); + break; + case PacketTypesIn.UpdateScore: + var entityName = dataTypes.ReadNextString(packetData); + var action3 = protocolVersion >= MC_1_18_2_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData); + var objectiveName3 = string.Empty; + var value = -1; + if (action3 != 1 || protocolVersion >= MC_1_8_Version) + objectiveName3 = dataTypes.ReadNextString(packetData); + if (action3 != 1) + value = dataTypes.ReadNextVarInt(packetData); + handler.OnUpdateScore(entityName, action3, objectiveName3, value); + break; + case PacketTypesIn.BlockChangedAck: + handler.OnBlockChangeAck(dataTypes.ReadNextVarInt(packetData)); + break; + case PacketTypesIn.BlockBreakAnimation: + if (handler.GetEntityHandlingEnabled() && handler.GetTerrainEnabled()) + { + var playerId = dataTypes.ReadNextVarInt(packetData); + var blockLocation = dataTypes.ReadNextLocation(packetData); + var stage = dataTypes.ReadNextByte(packetData); + handler.OnBlockBreakAnimation(playerId, blockLocation, stage); + } + + break; + case PacketTypesIn.EntityAnimation: + if (handler.GetEntityHandlingEnabled()) + { + var playerId = dataTypes.ReadNextVarInt(packetData); + var animation = dataTypes.ReadNextByte(packetData); + handler.OnEntityAnimation(playerId, animation); + } + + break; + + case PacketTypesIn.OpenSignEditor: + var signLocation = dataTypes.ReadNextLocation(packetData); + var isFrontText = true; + + if (protocolVersion >= MC_1_20_Version) + isFrontText = dataTypes.ReadNextBool(packetData); + + // TODO: Use + break; + + // Temporarily disabled until I find a fix + /*case PacketTypesIn.BlockEntityData: + var location_ = dataTypes.ReadNextLocation(packetData); + var type_ = dataTypes.ReadNextInt(packetData); + var nbt = dataTypes.ReadNextNbt(packetData); + var nbtJson = JsonConvert.SerializeObject(nbt["messages"]); + + //log.Info($"BLOCK ENTITY DATA -> {location_.ToString()} [{type_}] -> NBT: {nbtJson}"); + + break;*/ + + default: + return false; //Ignored packet + } + + return true; //Packet processed } /// @@ -2536,36 +2626,30 @@ namespace MinecraftClient.Protocol.Handlers /// /// Send a packet to the server. Compression and encryption will be handled automatically. /// - /// packet ID + /// packet ID /// packet Data - private void SendPacket(int packetID, IEnumerable packetData) + private void SendPacket(int packetId, IEnumerable packetData) { if (handler.GetNetworkPacketCaptureEnabled()) { - List clone = packetData.ToList(); - handler.OnNetworkPacket(packetID, clone, login_phase, false); + var clone = packetData.ToList(); + handler.OnNetworkPacket(packetId, clone, currentState == CurrentState.Login, false); } - // log.Info("[C -> S] Sending packet " + packetID + " > " + dataTypes.ByteArrayToString(packetData.ToArray())); + + //log.Info($"[C -> S] Sending packet {packetId:X} > {dataTypes.ByteArrayToString(packetData.ToArray())}"); //The inner packet - byte[] the_packet = dataTypes.ConcatBytes(DataTypes.GetVarInt(packetID), packetData.ToArray()); + var thePacket = 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); - } + thePacket = thePacket.Length >= compression_treshold + ? dataTypes.ConcatBytes(DataTypes.GetVarInt(thePacket.Length), ZlibUtils.Compress(thePacket)) + : dataTypes.ConcatBytes(DataTypes.GetVarInt(0), thePacket); } - //log.Debug("[C -> S] Sending packet " + packetID + " > " + dataTypes.ByteArrayToString(dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), the_packet))); - socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(the_packet.Length), the_packet)); + //log.Debug("[C -> S] Sending packet " + packetId + " > " + dataTypes.ByteArrayToString(dataTypes.ConcatBytes(dataTypes.GetVarInt(thePacket.Length), thePacket))); + socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(thePacket.Length), thePacket)); } /// @@ -2574,19 +2658,27 @@ namespace MinecraftClient.Protocol.Handlers /// True if login successful public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session) { - byte[] protocol_version = DataTypes.GetVarInt(protocolVersion); - string server_address = pForge.GetServerAddress(handler.GetServerHost()); - byte[] server_port = dataTypes.GetUShort((ushort)handler.GetServerPort()); - 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); + // 1. Send the handshake packet + SendPacket(0x00, dataTypes.ConcatBytes( + // Protocol Version + DataTypes.GetVarInt(protocolVersion), + // Server Address + dataTypes.GetString(pForge.GetServerAddress(handler.GetServerHost())), + + // Server Port + dataTypes.GetUShort((ushort)handler.GetServerPort()), + + // Next State + DataTypes.GetVarInt(2)) // 2 is for the Login state + ); + + // 2. Send the Login Start packet List fullLoginPacket = new(); fullLoginPacket.AddRange(dataTypes.GetString(handler.GetUsername())); // Username // 1.19 - 1.19.2 - if (protocolVersion >= MC_1_19_Version && protocolVersion < MC_1_19_3_Version) + if (protocolVersion is >= MC_1_19_Version and < MC_1_19_3_Version) { if (playerKeyPair == null) fullLoginPacket.AddRange(dataTypes.GetBool(false)); // Has Sig Data @@ -2635,41 +2727,54 @@ namespace MinecraftClient.Protocol.Handlers SendPacket(0x00, fullLoginPacket); + // 3. Encryption Request - 9. Login Acknowledged while (true) { - (int packetID, Queue packetData) = ReadNextPacket(); - if (packetID == 0x00) //Login rejected - { - handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, - ChatParser.ParseText(dataTypes.ReadNextString(packetData))); - return false; - } - else if (packetID == 0x01) //Encryption request - { - isOnlineMode = true; - string serverID = dataTypes.ReadNextString(packetData); - byte[] serverPublicKey = dataTypes.ReadNextByteArray(packetData); - byte[] token = dataTypes.ReadNextByteArray(packetData); - return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(), token, serverID, - serverPublicKey, playerKeyPair, session); - } - else if (packetID == 0x02) //Login successful - { - log.Info("§8" + Translations.mcc_server_offline); - login_phase = false; + var (packetId, packetData) = ReadNextPacket(); - SendPacket(0x03, new List()); - - if (!pForge.CompleteForgeHandshake()) - { - log.Error("§8" + Translations.error_forge); + switch (packetId) + { + // Login rejected + case 0x00: + handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, + ChatParser.ParseText(dataTypes.ReadNextString(packetData))); return false; + + // Encryption request + case 0x01: + { + isOnlineMode = true; + var serverId = dataTypes.ReadNextString(packetData); + var serverPublicKey = dataTypes.ReadNextByteArray(packetData); + var token = dataTypes.ReadNextByteArray(packetData); + return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(), token, serverId, + serverPublicKey, playerKeyPair, session); } - StartUpdating(); - return true; //No need to check session or start encryption + // Login successful + case 0x02: + { + log.Info($"§8{Translations.mcc_server_offline}"); + currentState = protocolVersion < MC_1_20_2_Version + ? CurrentState.Play + : CurrentState.Configuration; + + if (protocolVersion >= MC_1_20_2_Version) + SendPacket(0x03, new List()); + + if (!pForge.CompleteForgeHandshake()) + { + log.Error($"§8{Translations.error_forge}"); + return false; + } + + StartUpdating(); + return true; //No need to check session or start encryption + } + default: + HandlePacket(packetId, packetData); + break; } - else HandlePacket(packetID, packetData); } } @@ -2680,28 +2785,28 @@ namespace MinecraftClient.Protocol.Handlers private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, byte[] serverPublicKey, PlayerKeyPair? playerKeyPair, SessionToken session) { - RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!; - byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey(); + var RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!; + var secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey(); - log.Debug("§8" + Translations.debug_crypto); + log.Debug($"§8{Translations.debug_crypto}"); if (serverIDhash != "-") { log.Info(Translations.mcc_session); - bool needCheckSession = true; - if (session.ServerPublicKey != null && session.SessionPreCheckTask != null - && serverIDhash == session.ServerIDhash && - Enumerable.SequenceEqual(serverPublicKey, session.ServerPublicKey)) + var needCheckSession = true; + if (session is { ServerPublicKey: not null, SessionPreCheckTask: not null } + && serverIDhash == session.ServerIDhash && + serverPublicKey.SequenceEqual(session.ServerPublicKey)) { session.SessionPreCheckTask.Wait(); - if (session.SessionPreCheckTask.Result) // PreCheck Successed + if (session.SessionPreCheckTask.Result) // PreCheck Success needCheckSession = false; } if (needCheckSession) { - string serverHash = CryptoHandler.GetServerHash(serverIDhash, serverPublicKey, secretKey); + var serverHash = CryptoHandler.GetServerHash(serverIDhash, serverPublicKey, secretKey); if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash)) { @@ -2722,7 +2827,7 @@ namespace MinecraftClient.Protocol.Handlers encryptionResponse.AddRange(dataTypes.GetArray(RSAService.Encrypt(secretKey, false))); // Shared Secret // 1.19 - 1.19.2 - if (protocolVersion >= MC_1_19_Version && protocolVersion < MC_1_19_3_Version) + if (protocolVersion is >= MC_1_19_Version and < MC_1_19_3_Version) { if (playerKeyPair == null) { @@ -2731,8 +2836,8 @@ namespace MinecraftClient.Protocol.Handlers } else { - byte[] salt = GenerateSalt(); - byte[] messageSignature = playerKeyPair.PrivateKey.SignData(dataTypes.ConcatBytes(token, salt)); + var salt = GenerateSalt(); + var messageSignature = playerKeyPair.PrivateKey.SignData(dataTypes.ConcatBytes(token, salt)); encryptionResponse.AddRange(dataTypes.GetBool(false)); // Has Verify Token encryptionResponse.AddRange(salt); // Salt @@ -2746,64 +2851,72 @@ namespace MinecraftClient.Protocol.Handlers SendPacket(0x01, encryptionResponse); - //Start client-side encryption + // Start client-side encryption socketWrapper.SwitchToEncrypted(secretKey); // pre switch - //Process the next packet - int loopPrevention = UInt16.MaxValue; + // Process the next packet + int loopPrevention = ushort.MaxValue; while (true) { - (int packetID, Queue packetData) = ReadNextPacket(); - if (packetID < 0 || loopPrevention-- < 0) // Failed to read packet or too many iterations (issue #1150) + var (packetId, packetData) = ReadNextPacket(); + if (packetId < 0 || loopPrevention-- < 0) // Failed to read packet or too many iterations (issue #1150) { handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, - "§8" + Translations.error_invalid_encrypt); + $"§8{Translations.error_invalid_encrypt}"); return false; } - else if (packetID == 0x00) //Login rejected + + switch (packetId) { - handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, - ChatParser.ParseText(dataTypes.ReadNextString(packetData))); - return false; - } - else if (packetID == 0x02) //Login successful - { - Guid uuidReceived; - if (protocolVersion >= Protocol18Handler.MC_1_16_Version) - uuidReceived = dataTypes.ReadNextUUID(packetData); - else - uuidReceived = Guid.Parse(dataTypes.ReadNextString(packetData)); - string userName = dataTypes.ReadNextString(packetData); - Tuple[]? playerProperty = null; - if (protocolVersion >= Protocol18Handler.MC_1_19_Version) - { - int count = dataTypes.ReadNextVarInt(packetData); // Number Of Properties - playerProperty = new Tuple[count]; - for (int i = 0; i < count; ++i) - { - string name = dataTypes.ReadNextString(packetData); - string value = dataTypes.ReadNextString(packetData); - bool isSigned = dataTypes.ReadNextBool(packetData); - string signature = isSigned ? dataTypes.ReadNextString(packetData) : String.Empty; - playerProperty[i] = new Tuple(name, value, signature); - } - } - - SendPacket(0x03, new List()); - handler.OnLoginSuccess(uuidReceived, userName, playerProperty); - - login_phase = false; - - if (!pForge.CompleteForgeHandshake()) - { - log.Error("§8" + Translations.error_forge_encrypt); + //Login rejected + case 0x00: + handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, + ChatParser.ParseText(dataTypes.ReadNextString(packetData))); return false; - } + //Login successful + case 0x02: + { + var uuidReceived = protocolVersion >= MC_1_16_Version + ? dataTypes.ReadNextUUID(packetData) + : Guid.Parse(dataTypes.ReadNextString(packetData)); + var userName = dataTypes.ReadNextString(packetData); + Tuple[]? playerProperty = null; + if (protocolVersion >= MC_1_19_Version) + { + var count = dataTypes.ReadNextVarInt(packetData); // Number Of Properties + playerProperty = new Tuple[count]; + for (var i = 0; i < count; ++i) + { + var name = dataTypes.ReadNextString(packetData); + var value = dataTypes.ReadNextString(packetData); + var isSigned = dataTypes.ReadNextBool(packetData); + var signature = isSigned ? dataTypes.ReadNextString(packetData) : string.Empty; + playerProperty[i] = new Tuple(name, value, signature); + } + } - StartUpdating(); - return true; + currentState = protocolVersion < MC_1_20_2_Version + ? CurrentState.Play + : CurrentState.Configuration; + + if (protocolVersion >= MC_1_20_2_Version) + SendPacket(0x03, new List()); + + handler.OnLoginSuccess(uuidReceived, userName, playerProperty); + + if (!pForge.CompleteForgeHandshake()) + { + log.Error($"§8{Translations.error_forge_encrypt}"); + return false; + } + + StartUpdating(); + return true; + } + default: + HandlePacket(packetId, packetData); + break; } - else HandlePacket(packetID, packetData); } } @@ -2825,37 +2938,35 @@ namespace MinecraftClient.Protocol.Handlers if (string.IsNullOrEmpty(BehindCursor)) return -1; - byte[] transaction_id = DataTypes.GetVarInt(autocomplete_transaction_id); - byte[] assume_command = new byte[] { 0x00 }; - byte[] has_position = new byte[] { 0x00 }; + var transactionId = DataTypes.GetVarInt(autocomplete_transaction_id); + var assumeCommand = new byte[] { 0x00 }; + var hasPosition = new byte[] { 0x00 }; + var tabCompletePacket = Array.Empty(); - byte[] tabcomplete_packet = Array.Empty(); - - if (protocolVersion >= MC_1_8_Version) + switch (protocolVersion) { - if (protocolVersion >= MC_1_13_Version) - { - tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, transaction_id); - tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, + case >= MC_1_8_Version and >= MC_1_13_Version: + tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, transactionId); + tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, dataTypes.GetString(BehindCursor.Replace(' ', (char)0x00))); - } - else + break; + case >= MC_1_8_Version: { - tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor)); + tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, dataTypes.GetString(BehindCursor)); if (protocolVersion >= MC_1_9_Version) - tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, assume_command); + tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, assumeCommand); - tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, has_position); + tabCompletePacket = dataTypes.ConcatBytes(tabCompletePacket, hasPosition); + break; } - } - else - { - tabcomplete_packet = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor)); + default: + tabCompletePacket = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor)); + break; } ConsoleIO.AutoCompleteDone = false; - SendPacket(PacketTypesOut.TabComplete, tabcomplete_packet); + SendPacket(PacketTypesOut.TabComplete, tabCompletePacket); return autocomplete_transaction_id; } @@ -2865,87 +2976,90 @@ namespace MinecraftClient.Protocol.Handlers /// 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); + var version = ""; + var tcp = ProxyHandler.NewTcpClient(host, port); tcp.ReceiveTimeout = 30000; // 30 seconds tcp.ReceiveBufferSize = 1024 * 1024; SocketWrapper socketWrapper = new(tcp); DataTypes dataTypes = new(MC_1_8_Version); - 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); + var serverPort = BitConverter.GetBytes((ushort)port); + Array.Reverse(serverPort); - socketWrapper.SendDataRAW(tosend); + // Ping Packet + var pingPacket = dataTypes.ConcatBytes( + // Packet Id + DataTypes.GetVarInt(0), - byte[] status_request = DataTypes.GetVarInt(0); - byte[] request_packet = dataTypes.ConcatBytes(DataTypes.GetVarInt(status_request.Length), status_request); + // Protocol Version + DataTypes.GetVarInt(-1), - socketWrapper.SendDataRAW(request_packet); + // Server IP (Host) + dataTypes.GetString(host), - int packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper); - if (packetLength > 0) //Read Response length + // Server port + serverPort, + + // Next State + DataTypes.GetVarInt(1)); + + socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingPacket.Length), pingPacket)); + + // Status Request Packet + var statusRequest = DataTypes.GetVarInt(0); + socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest)); + + // Read Response length + var packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper); + if (packetLength <= 0) return false; + + // Read the Packet Id + var packetData = new Queue(socketWrapper.ReadDataRAW(packetLength)); + if (dataTypes.ReadNextVarInt(packetData) != 0x00) return false; + + var result = dataTypes.ReadNextString(packetData); // Get the Json data + + if (Config.Logging.DebugMessages) { - Queue packetData = new(socketWrapper.ReadDataRAW(packetLength)); - if (dataTypes.ReadNextVarInt(packetData) == 0x00) //Read Packet ID - { - string result = dataTypes.ReadNextString(packetData); //Get the Json data - - if (Config.Logging.DebugMessages) - { - // May contain formatting codes, cannot use WriteLineFormatted - Console.ForegroundColor = ConsoleColor.DarkGray; - ConsoleIO.WriteLine(result); - Console.ForegroundColor = ConsoleColor.Gray; - } - - 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 = int.Parse(versionData.Properties["protocol"].StringValue, - NumberStyles.Any, CultureInfo.CurrentCulture); - - // Check for forge on the server. - Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo); - - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version, - protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : ""))); - - return true; - } - } - } + // May contain formatting codes, cannot use WriteLineFormatted + Console.ForegroundColor = ConsoleColor.DarkGray; + ConsoleIO.WriteLine(result); + Console.ForegroundColor = ConsoleColor.Gray; } - return false; + if (string.IsNullOrEmpty(result) || !result.StartsWith("{") || !result.EndsWith("}")) return false; + + var jsonData = Json.ParseJson(result); + if (jsonData.Type != Json.JSONData.DataType.Object || !jsonData.Properties.ContainsKey("version")) + return false; + + var versionData = jsonData.Properties["version"]; + + //Retrieve display name of the Minecraft version + if (versionData.Properties.TryGetValue("name", out var property)) + version = property.StringValue; + + //Retrieve protocol version number for handling this server + if (versionData.Properties.TryGetValue("protocol", out var dataProperty)) + protocolVersion = int.Parse(dataProperty.StringValue, + NumberStyles.Any, CultureInfo.CurrentCulture); + + // Check for forge on the server. + Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo); + + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version, + protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : ""))); + + return true; } /// /// Get max length for chat messages /// /// Max length, in characters - public int GetMaxChatMessageLength() - { - return protocolVersion > MC_1_10_Version - ? 256 - : 100; - } + public int GetMaxChatMessageLength() => protocolVersion > MC_1_10_Version + ? 256 + : 100; /// /// Get the current protocol version. @@ -2968,7 +3082,7 @@ namespace MinecraftClient.Protocol.Handlers { try { - byte[] fields = dataTypes.GetAcknowledgment(acknowledgment, + var fields = dataTypes.GetAcknowledgment(acknowledgment, isOnlineMode && Config.Signature.LoginWithSecureProfile); SendPacket(PacketTypesOut.MessageAcknowledgment, fields); @@ -2998,10 +3112,8 @@ namespace MinecraftClient.Protocol.Handlers { try { - byte[] fields = DataTypes.GetVarInt(messageCount); - + var fields = DataTypes.GetVarInt(messageCount); SendPacket(PacketTypesOut.MessageAcknowledgment, fields); - return true; } catch (SocketException) @@ -3027,29 +3139,24 @@ namespace MinecraftClient.Protocol.Handlers public void Acknowledge(ChatMessage message) { - LastSeenMessageList.AcknowledgedMessage? entry = message.ToLastSeenMessageEntry(); + var entry = message.ToLastSeenMessageEntry(); + if (entry == null) return; - if (entry != null) + if (protocolVersion >= MC_1_19_3_Version) { - if (protocolVersion >= MC_1_19_3_Version) - { - if (lastSeenMessagesCollector.Add_1_19_3(entry, true)) - { - if (lastSeenMessagesCollector.messageCount > 64) - { - int messageCount = lastSeenMessagesCollector.ResetMessageCount(); - if (messageCount > 0) - SendMessageAcknowledgment(messageCount); - } - } - } - else - { - lastSeenMessagesCollector.Add_1_19_2(entry); - lastReceivedMessage = null; - if (pendingAcknowledgments++ > 64) - SendMessageAcknowledgment(ConsumeAcknowledgment()); - } + if (!lastSeenMessagesCollector.Add_1_19_3(entry, true)) return; + if (lastSeenMessagesCollector.messageCount <= 64) return; + + var messageCount = lastSeenMessagesCollector.ResetMessageCount(); + if (messageCount > 0) + SendMessageAcknowledgment(messageCount); + } + else + { + lastSeenMessagesCollector.Add_1_19_2(entry); + lastReceivedMessage = null; + if (pendingAcknowledgments++ > 64) + SendMessageAcknowledgment(ConsumeAcknowledgment()); } } @@ -3061,29 +3168,28 @@ namespace MinecraftClient.Protocol.Handlers /// True if properly sent public bool SendChatCommand(string command, PlayerKeyPair? playerKeyPair) { - if (String.IsNullOrEmpty(command)) + if (string.IsNullOrEmpty(command)) return true; command = Regex.Replace(command, @"\s+", " "); command = Regex.Replace(command, @"\s$", string.Empty); - log.Debug("chat command = " + command); + log.Debug($"chat command = {command}"); try { List>? needSigned = null; // List< Argument Name, Argument Value > if (playerKeyPair != null && isOnlineMode && protocolVersion >= MC_1_19_Version - && Config.Signature.LoginWithSecureProfile && Config.Signature.SignMessageInCommand) + && Config.Signature is { LoginWithSecureProfile: true, SignMessageInCommand: true }) needSigned = DeclareCommands.CollectSignArguments(command); lock (MessageSigningLock) { - LastSeenMessageList.Acknowledgment? acknowledgment_1_19_2 = - (protocolVersion == MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; + var acknowledgment1192 = + protocolVersion == MC_1_19_2_Version ? ConsumeAcknowledgment() : null; - (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, - int messageCount_1_19_3) = - (protocolVersion >= MC_1_19_3_Version) + var (acknowledgment1193, bitset1193, messageCount1193) = + protocolVersion >= MC_1_19_3_Version ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); @@ -3093,7 +3199,7 @@ namespace MinecraftClient.Protocol.Handlers fields.AddRange(dataTypes.GetString(command)); // Timestamp: Instant(Long) - DateTimeOffset timeNow = DateTimeOffset.UtcNow; + var timeNow = DateTimeOffset.UtcNow; fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); if (needSigned == null || needSigned!.Count == 0) @@ -3103,23 +3209,24 @@ namespace MinecraftClient.Protocol.Handlers } else { - Guid uuid = handler.GetUserUuid(); - byte[] salt = GenerateSalt(); + var uuid = handler.GetUserUuid(); + var salt = GenerateSalt(); fields.AddRange(salt); // Salt: Long fields.AddRange(DataTypes.GetVarInt(needSigned.Count)); // Signature Length: VarInt - foreach ((string argName, string message) in needSigned) + + foreach (var (argName, message) in needSigned) { fields.AddRange(dataTypes.GetString(argName)); // Argument name: String - byte[] sign; - if (protocolVersion == MC_1_19_Version) - sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); - else if (protocolVersion == MC_1_19_2_Version) - sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, - acknowledgment_1_19_2!.lastSeen); - else // protocolVersion >= MC_1_19_3_Version - sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, chatUuid, messageIndex++, - timeNow, ref salt, acknowledgment_1_19_3); + var sign = protocolVersion switch + { + MC_1_19_Version => playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, + ref salt), + MC_1_19_2_Version => playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, + ref salt, acknowledgment1192!.lastSeen), + _ => playerKeyPair!.PrivateKey.SignMessage(message, uuid, chatUuid, messageIndex++, + timeNow, ref salt, acknowledgment1193) + }; if (protocolVersion <= MC_1_19_2_Version) fields.AddRange(DataTypes.GetVarInt(sign.Length)); // Signature length: VarInt @@ -3131,19 +3238,20 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion <= MC_1_19_2_Version) fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean - if (protocolVersion == MC_1_19_2_Version) + switch (protocolVersion) { - // Message Acknowledgment (1.19.2) - fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment_1_19_2!, - isOnlineMode && Config.Signature.LoginWithSecureProfile)); - } - else if (protocolVersion >= MC_1_19_3_Version) - { - // message count - fields.AddRange(DataTypes.GetVarInt(messageCount_1_19_3)); + case MC_1_19_2_Version: + // Message Acknowledgment (1.19.2) + fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment1192!, + isOnlineMode && Config.Signature.LoginWithSecureProfile)); + break; + case >= MC_1_19_3_Version: + // message count + fields.AddRange(DataTypes.GetVarInt(messageCount1193)); - // Acknowledged: BitSet - fields.AddRange(bitset_1_19_3); + // Acknowledged: BitSet + fields.AddRange(bitset1193); + break; } SendPacket(PacketTypesOut.ChatCommand, fields); @@ -3191,69 +3299,67 @@ namespace MinecraftClient.Protocol.Handlers { lock (MessageSigningLock) { - LastSeenMessageList.Acknowledgment? acknowledgment_1_19_2 = - (protocolVersion == MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; + var acknowledgment1192 = + protocolVersion == MC_1_19_2_Version ? ConsumeAcknowledgment() : null; - (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, - int messageCount_1_19_3) = - (protocolVersion >= MC_1_19_3_Version) + var (acknowledgment1193, bitset1193, messageCount1193) = + protocolVersion >= MC_1_19_3_Version ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); // Timestamp: Instant(Long) - DateTimeOffset timeNow = DateTimeOffset.UtcNow; + var timeNow = DateTimeOffset.UtcNow; fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); if (!isOnlineMode || playerKeyPair == null || !Config.Signature.LoginWithSecureProfile || !Config.Signature.SignChat) { fields.AddRange(DataTypes.GetLong(0)); // Salt: Long - if (protocolVersion < MC_1_19_3_Version) - fields.AddRange(DataTypes.GetVarInt(0)); // Signature Length: VarInt (1.19 - 1.19.2) - else - fields.AddRange(dataTypes.GetBool(false)); // Has signature: bool (1.19.3) + fields.AddRange(protocolVersion < MC_1_19_3_Version + ? DataTypes.GetVarInt(0) // Signature Length: VarInt (1.19 - 1.19.2) + : dataTypes.GetBool(false)); // Has signature: bool (1.19.3) } else { // Salt: Long - byte[] salt = GenerateSalt(); + var salt = GenerateSalt(); fields.AddRange(salt); // Signature Length & Signature: (VarInt) and Byte Array - Guid playerUuid = handler.GetUserUuid(); - byte[] sign; - if (protocolVersion == MC_1_19_Version) // 1.19.1 or lower - sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt); - else if (protocolVersion == MC_1_19_2_Version) // 1.19.2 - sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt, - acknowledgment_1_19_2!.lastSeen); - else // protocolVersion >= MC_1_19_3_Version - sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, chatUuid, - messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); + var playerUuid = handler.GetUserUuid(); + var sign = protocolVersion switch + { + MC_1_19_Version => playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, + ref salt), + MC_1_19_2_Version => playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, + ref salt, acknowledgment1192!.lastSeen), + _ => playerKeyPair.PrivateKey.SignMessage(message, playerUuid, chatUuid, messageIndex++, + timeNow, ref salt, acknowledgment1193) + }; - if (protocolVersion >= MC_1_19_3_Version) - fields.AddRange(dataTypes.GetBool(true)); - else - fields.AddRange(DataTypes.GetVarInt(sign.Length)); + fields.AddRange(protocolVersion >= MC_1_19_3_Version + ? dataTypes.GetBool(true) + : DataTypes.GetVarInt(sign.Length)); fields.AddRange(sign); } if (protocolVersion <= MC_1_19_2_Version) fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean - if (protocolVersion >= MC_1_19_3_Version) + switch (protocolVersion) { - // message count - fields.AddRange(DataTypes.GetVarInt(messageCount_1_19_3)); + case >= MC_1_19_3_Version: + // message count + fields.AddRange(DataTypes.GetVarInt(messageCount1193)); - // Acknowledged: BitSet - fields.AddRange(bitset_1_19_3); - } - else if (protocolVersion == MC_1_19_2_Version) - { - // Message Acknowledgment - fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment_1_19_2!, - isOnlineMode && Config.Signature.LoginWithSecureProfile)); + // Acknowledged: BitSet + fields.AddRange(bitset1193); + break; + case MC_1_19_2_Version: + // Message Acknowledgment + fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment1192!, + isOnlineMode && Config.Signature.LoginWithSecureProfile)); + break; } } } @@ -3332,18 +3438,13 @@ namespace MinecraftClient.Protocol.Handlers /// True if brand info was successfully sent public bool SendBrandInfo(string brandInfo) { - if (String.IsNullOrEmpty(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 >= MC_1_13_Version) - { - return SendPluginChannelPacket("minecraft:brand", dataTypes.GetString(brandInfo)); - } - else - { - return SendPluginChannelPacket("MC|Brand", dataTypes.GetString(brandInfo)); - } + return SendPluginChannelPacket(protocolVersion >= MC_1_13_Version ? "minecraft:brand" : "MC|Brand", + dataTypes.GetString(brandInfo)); } /// @@ -3366,10 +3467,9 @@ namespace MinecraftClient.Protocol.Handlers fields.AddRange(dataTypes.GetString(language)); fields.Add(viewDistance); - if (protocolVersion >= MC_1_9_Version) - fields.AddRange(DataTypes.GetVarInt(chatMode)); - else - fields.AddRange(new byte[] { chatMode }); + fields.AddRange(protocolVersion >= MC_1_9_Version + ? DataTypes.GetVarInt(chatMode) + : new byte[] { chatMode }); fields.Add(chatColors ? (byte)1 : (byte)0); if (protocolVersion < MC_1_8_Version) @@ -3383,10 +3483,10 @@ namespace MinecraftClient.Protocol.Handlers fields.AddRange(DataTypes.GetVarInt(mainHand)); if (protocolVersion >= MC_1_17_Version) { - if (protocolVersion >= MC_1_18_1_Version) - fields.Add(0); // 1.18 and above - Enable text filtering. (Always false) - else - fields.Add(1); // 1.17 and 1.17.1 - Disable text filtering. (Always true) + fields.Add(protocolVersion >= MC_1_18_1_Version + ? (byte)0 + : (byte)1); // 1.17 and 1.17.1 - Disable text filtering. (Always true) + // 1.18 and above - Enable text filtering. (Always false) } if (protocolVersion >= MC_1_18_1_Version) @@ -3427,15 +3527,15 @@ namespace MinecraftClient.Protocol.Handlers { if (handler.GetTerrainEnabled()) { - byte[] yawpitch = Array.Empty(); - PacketTypesOut packetType = PacketTypesOut.PlayerPosition; + var yawPitch = Array.Empty(); + var packetType = PacketTypesOut.PlayerPosition; if (Config.Main.Advanced.TemporaryFixBadpacket) { if (yaw.HasValue && pitch.HasValue && (forceUpdate || yaw.Value != LastYaw || pitch.Value != LastPitch)) { - yawpitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), + yawPitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), dataTypes.GetFloat(pitch.Value)); packetType = PacketTypesOut.PlayerPositionAndRotation; @@ -3447,7 +3547,7 @@ namespace MinecraftClient.Protocol.Handlers { if (yaw.HasValue && pitch.HasValue) { - yawpitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), + yawPitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), dataTypes.GetFloat(pitch.Value)); packetType = PacketTypesOut.PlayerPositionAndRotation; @@ -3465,8 +3565,9 @@ namespace MinecraftClient.Protocol.Handlers ? dataTypes.GetDouble(location.Y + 1.62) : Array.Empty(), dataTypes.GetDouble(location.Z), - yawpitch, - new byte[] { onGround ? (byte)1 : (byte)0 })); + yawPitch, + new byte[] { onGround ? (byte)1 : (byte)0 }) + ); return true; } catch (SocketException) @@ -3498,7 +3599,7 @@ namespace MinecraftClient.Protocol.Handlers // In 1.8, it must not be. if (protocolVersion < MC_1_8_Version) { - byte[] length = BitConverter.GetBytes((short)data.Length); + var length = BitConverter.GetBytes((short)data.Length); Array.Reverse(length); SendPacket(PacketTypesOut.PluginMessage, @@ -3721,22 +3822,22 @@ namespace MinecraftClient.Protocol.Handlers { if (protocolVersion < MC_1_14_Version) { - Container? playerInventory = handler.GetInventory(0); + var playerInventory = handler.GetInventory(0); if (playerInventory == null) return false; - List packet = new List(); + var packet = new List(); packet.AddRange(dataTypes.GetLocation(location)); packet.Add(dataTypes.GetBlockFace(face)); - Item item = playerInventory.Items[((McClient)handler).GetCurrentSlot()]; + var item = playerInventory.Items[((McClient)handler).GetCurrentSlot()]; packet.AddRange(dataTypes.GetItemSlot(item, itemPalette)); - packet.Add((byte)0); // cursorX - packet.Add((byte)0); // cursorY - packet.Add((byte)0); // cursorZ + packet.Add(0); // cursorX + packet.Add(0); // cursorY + packet.Add(0); // cursorZ SendPacket(PacketTypesOut.PlayerBlockPlacement, packet); return true; @@ -3744,7 +3845,7 @@ namespace MinecraftClient.Protocol.Handlers try { - List packet = new List(); + var packet = new List(); packet.AddRange(DataTypes.GetVarInt(hand)); packet.AddRange(dataTypes.GetLocation(location)); packet.AddRange(DataTypes.GetVarInt(dataTypes.GetBlockFace(face))); @@ -3901,22 +4002,22 @@ namespace MinecraftClient.Protocol.Handlers (byte)windowId // Window ID }; - // 1.18+ - if (protocolVersion >= MC_1_18_1_Version) + switch (protocolVersion) { - packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID - packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID - } - // 1.17.1 - else if (protocolVersion == MC_1_17_1_Version) - { - packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID - packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID - } - // Older - else - { - packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID + // 1.18+ + case >= MC_1_18_1_Version: + packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID + packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID + break; + // 1.17.1 + case MC_1_17_1_Version: + packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID + packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID + break; + // Older + default: + packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID + break; } packet.Add(button); // Button @@ -4009,7 +4110,7 @@ namespace MinecraftClient.Protocol.Handlers { try { - SendPacket(PacketTypesOut.ChunkBatchReceived, new List()); + SendPacket(PacketTypesOut.AcknowledgeConfiguration, new List()); return true; } catch (SocketException) @@ -4054,31 +4155,27 @@ namespace MinecraftClient.Protocol.Handlers { try { - if (animation == 0 || animation == 1) - { - List packet = new(); + if (animation is not (0 or 1)) return false; - if (protocolVersion < MC_1_8_Version) - { + List packet = new(); + + switch (protocolVersion) + { + case < MC_1_8_Version: packet.AddRange(DataTypes.GetInt(playerid)); - packet.Add((byte)1); // Swing arm - } - else if (protocolVersion < MC_1_9_Version) - { + packet.Add(1); // Swing arm + break; + case < MC_1_9_Version: // No fields in 1.8.X - } - else // MC 1.9+ - { + break; + // MC 1.9+ + default: packet.AddRange(DataTypes.GetVarInt(animation)); - } + break; + } - SendPacket(PacketTypesOut.Animation, packet); - return true; - } - else - { - return false; - } + SendPacket(PacketTypesOut.Animation, packet); + return true; } catch (SocketException) { @@ -4129,16 +4226,16 @@ namespace MinecraftClient.Protocol.Handlers if (line1.Length > 23) line1 = line1[..23]; if (line2.Length > 23) - line2 = line1[..23]; + line2 = line2[..23]; if (line3.Length > 23) - line3 = line1[..23]; + line3 = line3[..23]; if (line4.Length > 23) - line4 = line1[..23]; + line4 = line4[..23]; List packet = new(); packet.AddRange(dataTypes.GetLocation(sign)); if (protocolVersion >= MC_1_20_Version) - packet.AddRange(dataTypes.GetBool((isFrontText))); + packet.AddRange(dataTypes.GetBool(isFrontText)); packet.AddRange(dataTypes.GetString(line1)); packet.AddRange(dataTypes.GetString(line2)); packet.AddRange(dataTypes.GetString(line3)); @@ -4349,9 +4446,16 @@ namespace MinecraftClient.Protocol.Handlers private byte[] GenerateSalt() { - byte[] salt = new byte[8]; + var salt = new byte[8]; randomGen.GetNonZeroBytes(salt); return salt; } } + + internal enum CurrentState + { + Login = 0, + Configuration, + Play + } } \ No newline at end of file