From 9841156130a7cc828cd3dfdde5b5a8b3af86506a Mon Sep 17 00:00:00 2001 From: ORelio Date: Sat, 27 Apr 2019 21:04:13 +0200 Subject: [PATCH] Add more debug info in packet parsing errors For diagnosing #710 and other future packet parsing errors --- .../Protocol/Handlers/Protocol18.cs | 894 +++++++++--------- 1 file changed, 454 insertions(+), 440 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 7a89bf00..4d5e6f0a 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -402,457 +402,471 @@ namespace MinecraftClient.Protocol.Handlers /// TRUE if the packet was processed, FALSE if ignored or unknown private bool handlePacket(int packetID, List packetData) { - if (login_phase) + try { - switch (packetID) //Packet IDs are different while logging in + if (login_phase) { - case 0x03: + switch (packetID) //Packet IDs are different while logging in + { + case 0x03: + if (protocolversion >= MC18Version) + compression_treshold = readNextVarInt(packetData); + break; + default: + return false; //Ignored packet + } + } + // Regular in-game packets + switch (getPacketIncomingType(packetID, protocolversion)) + { + case PacketIncomingType.KeepAlive: + SendPacket(PacketOutgoingType.KeepAlive, packetData); + break; + case PacketIncomingType.JoinGame: + handler.OnGameJoined(); + readNextInt(packetData); + readNextByte(packetData); + if (protocolversion >= MC191Version) + this.currentDimension = readNextInt(packetData); + else + this.currentDimension = (sbyte)readNextByte(packetData); + readNextByte(packetData); + readNextByte(packetData); + readNextString(packetData); if (protocolversion >= MC18Version) + readNextBool(packetData); // Reduced debug info - 1.8 and above + break; + case PacketIncomingType.ChatMessage: + string message = readNextString(packetData); + try + { + //Hide system messages or xp bar messages? + byte messageType = readNextByte(packetData); + if ((messageType == 1 && !Settings.DisplaySystemMessages) + || (messageType == 2 && !Settings.DisplayXPBarMessages)) + break; + } + catch (ArgumentOutOfRangeException) { /* No message type */ } + handler.OnTextReceived(message, true); + break; + case PacketIncomingType.Respawn: + this.currentDimension = readNextInt(packetData); + readNextByte(packetData); + readNextByte(packetData); + readNextString(packetData); + break; + case PacketIncomingType.PlayerPositionAndLook: + if (Settings.TerrainAndMovements) + { + double x = readNextDouble(packetData); + double y = readNextDouble(packetData); + double z = readNextDouble(packetData); + float yaw = readNextFloat(packetData); + float pitch = readNextFloat(packetData); + byte locMask = readNextByte(packetData); + + if (protocolversion >= MC18Version) + { + Location location = handler.GetCurrentLocation(); + location.X = (locMask & 1 << 0) != 0 ? location.X + x : x; + location.Y = (locMask & 1 << 1) != 0 ? location.Y + y : y; + location.Z = (locMask & 1 << 2) != 0 ? location.Z + z : z; + handler.UpdateLocation(location, yaw, pitch); + } + else handler.UpdateLocation(new Location(x, y, z), yaw, pitch); + } + + if (protocolversion >= MC19Version) + { + int teleportID = readNextVarInt(packetData); + // Teleport confirm packet + SendPacket(PacketOutgoingType.TeleportConfirm, getVarInt(teleportID)); + } + break; + case PacketIncomingType.ChunkData: + if (Settings.TerrainAndMovements) + { + int chunkX = readNextInt(packetData); + int chunkZ = readNextInt(packetData); + bool chunksContinuous = readNextBool(packetData); + ushort chunkMask = protocolversion >= MC19Version + ? (ushort)readNextVarInt(packetData) + : readNextUShort(packetData); + if (protocolversion < MC18Version) + { + ushort addBitmap = readNextUShort(packetData); + int compressedDataSize = readNextInt(packetData); + byte[] compressed = readData(compressedDataSize, packetData); + byte[] decompressed = ZlibUtils.Decompress(compressed); + ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, new List(decompressed)); + } + else + { + int dataSize = readNextVarInt(packetData); + ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, packetData); + } + } + break; + case PacketIncomingType.MultiBlockChange: + if (Settings.TerrainAndMovements) + { + int chunkX = readNextInt(packetData); + int chunkZ = readNextInt(packetData); + int recordCount = protocolversion < MC18Version + ? (int)readNextShort(packetData) + : readNextVarInt(packetData); + + for (int i = 0; i < recordCount; i++) + { + byte locationXZ; + ushort blockIdMeta; + int blockY; + + if (protocolversion < MC18Version) + { + blockIdMeta = readNextUShort(packetData); + blockY = (ushort)readNextByte(packetData); + locationXZ = readNextByte(packetData); + } + else + { + locationXZ = readNextByte(packetData); + blockY = (ushort)readNextByte(packetData); + blockIdMeta = (ushort)readNextVarInt(packetData); + } + + int blockX = locationXZ >> 4; + int blockZ = locationXZ & 0x0F; + Block block = new Block(blockIdMeta); + handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block); + } + } + break; + case PacketIncomingType.BlockChange: + if (Settings.TerrainAndMovements) + { + if (protocolversion < MC18Version) + { + int blockX = readNextInt(packetData); + int blockY = readNextByte(packetData); + int blockZ = readNextInt(packetData); + short blockId = (short)readNextVarInt(packetData); + byte blockMeta = readNextByte(packetData); + handler.GetWorld().SetBlock(new Location(blockX, blockY, blockZ), new Block(blockId, blockMeta)); + } + else handler.GetWorld().SetBlock(Location.FromLong(readNextULong(packetData)), new Block((ushort)readNextVarInt(packetData))); + } + break; + case PacketIncomingType.MapChunkBulk: + if (protocolversion < MC19Version && Settings.TerrainAndMovements) + { + int chunkCount; + bool hasSkyLight; + List chunkData = packetData; + + //Read global fields + if (protocolversion < MC18Version) + { + chunkCount = readNextShort(packetData); + int compressedDataSize = readNextInt(packetData); + hasSkyLight = readNextBool(packetData); + byte[] compressed = readData(compressedDataSize, packetData); + byte[] decompressed = ZlibUtils.Decompress(compressed); + chunkData = new List(decompressed); + } + else + { + hasSkyLight = readNextBool(packetData); + chunkCount = 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] = readNextInt(packetData); + chunkZs[chunkColumnNo] = readNextInt(packetData); + chunkMasks[chunkColumnNo] = readNextUShort(packetData); + addBitmaps[chunkColumnNo] = protocolversion < MC18Version + ? readNextUShort(packetData) + : (ushort)0; + } + + //Process chunk records + for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) + ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, chunkData); + } + break; + case PacketIncomingType.UnloadChunk: + if (protocolversion >= MC19Version && Settings.TerrainAndMovements) + { + int chunkX = readNextInt(packetData); + int chunkZ = readNextInt(packetData); + handler.GetWorld()[chunkX, chunkZ] = null; + } + break; + case PacketIncomingType.PlayerListUpdate: + if (protocolversion >= MC18Version) + { + int action = readNextVarInt(packetData); + int numActions = readNextVarInt(packetData); + for (int i = 0; i < numActions; i++) + { + Guid uuid = readNextUUID(packetData); + switch (action) + { + case 0x00: //Player Join + string name = readNextString(packetData); + int propNum = readNextVarInt(packetData); + for (int p = 0; p < propNum; p++) + { + string key = readNextString(packetData); + string val = readNextString(packetData); + if (readNextBool(packetData)) + readNextString(packetData); + } + readNextVarInt(packetData); + readNextVarInt(packetData); + if (readNextBool(packetData)) + readNextString(packetData); + handler.OnPlayerJoin(uuid, name); + break; + case 0x01: //Update gamemode + case 0x02: //Update latency + readNextVarInt(packetData); + break; + case 0x03: //Update display name + if (readNextBool(packetData)) + readNextString(packetData); + break; + case 0x04: //Player Leave + handler.OnPlayerLeave(uuid); + break; + default: + //Unknown player list item type + break; + } + } + } + else //MC 1.7.X does not provide UUID in tab-list updates + { + string name = readNextString(packetData); + bool online = readNextBool(packetData); + short ping = readNextShort(packetData); + Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); + if (online) + handler.OnPlayerJoin(FakeUUID, name); + else handler.OnPlayerLeave(FakeUUID); + } + break; + case PacketIncomingType.TabCompleteResult: + if (protocolversion >= MC113Version) + { + autocomplete_transaction_id = readNextVarInt(packetData); + readNextVarInt(packetData); // Start of text to replace + readNextVarInt(packetData); // Length of text to replace + } + + int autocomplete_count = readNextVarInt(packetData); + autocomplete_result.Clear(); + + for (int i = 0; i < autocomplete_count; i++) + { + autocomplete_result.Add(readNextString(packetData)); + if (protocolversion >= MC113Version) + { + // Skip optional tooltip for each tab-complete result + if (readNextBool(packetData)) + readNextString(packetData); + } + } + + autocomplete_received = true; + break; + case PacketIncomingType.PluginMessage: + String channel = readNextString(packetData); + if (protocolversion < MC18Version) + { + if (forgeInfo == null) + { + // 1.7 and lower prefix plugin channel packets with the length. + // We can skip it, though. + readNextShort(packetData); + } + else + { + // Forge does something even weirder with the length. + readNextVarShort(packetData); + } + } + + // The remaining data in the array is the entire payload of the packet. + handler.OnPluginChannelMessage(channel, packetData.ToArray()); + + #region Forge Login + if (forgeInfo != null && fmlHandshakeState != FMLHandshakeClientState.DONE) + { + if (channel == "FML|HS") + { + FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)readNextByte(packetData); + + if (discriminator == FMLHandshakeDiscriminator.HandshakeReset) + { + fmlHandshakeState = FMLHandshakeClientState.START; + return true; + } + + switch (fmlHandshakeState) + { + case FMLHandshakeClientState.START: + if (discriminator != FMLHandshakeDiscriminator.ServerHello) + return false; + + // Send the plugin channel registration. + // REGISTER is somewhat special in that it doesn't actually include length information, + // and is also \0-separated. + // Also, yes, "FML" is there twice. Don't ask me why, but that's the way forge does it. + string[] channels = { "FML|HS", "FML", "FML|MP", "FML", "FORGE" }; + SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels))); + + byte fmlProtocolVersion = readNextByte(packetData); + + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion); + + if (fmlProtocolVersion >= 1) + this.currentDimension = readNextInt(packetData); + + // Tell the server we're running the same version. + SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion }); + + // Then tell the server that we're running the same mods. + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Sending falsified mod list to server..."); + byte[][] mods = new byte[forgeInfo.Mods.Count][]; + for (int i = 0; i < forgeInfo.Mods.Count; i++) + { + ForgeInfo.ForgeMod mod = forgeInfo.Mods[i]; + mods[i] = concatBytes(getString(mod.ModID), getString(mod.Version)); + } + SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList, + concatBytes(getVarInt(forgeInfo.Mods.Count), concatBytes(mods))); + + fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERDATA; + + return true; + case FMLHandshakeClientState.WAITINGSERVERDATA: + if (discriminator != FMLHandshakeDiscriminator.ModList) + return false; + + Thread.Sleep(2000); + + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Accepting server mod list..."); + // Tell the server that yes, we are OK with the mods it has + // even though we don't actually care what mods it has. + + SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, + new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA }); + + fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERCOMPLETE; + return false; + case FMLHandshakeClientState.WAITINGSERVERCOMPLETE: + // The server now will tell us a bunch of registry information. + // We need to read it all, though, until it says that there is no more. + if (discriminator != FMLHandshakeDiscriminator.RegistryData) + return false; + + if (protocolversion < MC18Version) + { + // 1.7.10 and below have one registry + // with blocks and items. + int registrySize = readNextVarInt(packetData); + + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Received registry with " + registrySize + " entries"); + + fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; + } + else + { + // 1.8+ has more than one registry. + + bool hasNextRegistry = readNextBool(packetData); + string registryName = readNextString(packetData); + int registrySize = readNextVarInt(packetData); + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Received registry " + registryName + " with " + registrySize + " entries"); + if (!hasNextRegistry) + fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; + } + + return false; + case FMLHandshakeClientState.PENDINGCOMPLETE: + // The server will ask us to accept the registries. + // Just say yes. + if (discriminator != FMLHandshakeDiscriminator.HandshakeAck) + return false; + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Accepting server registries..."); + SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, + new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE }); + fmlHandshakeState = FMLHandshakeClientState.COMPLETE; + + return true; + case FMLHandshakeClientState.COMPLETE: + // One final "OK". On the actual forge source, a packet is sent from + // the client to the client saying that the connection was complete, but + // we don't need to do that. + + SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, + new byte[] { (byte)FMLHandshakeClientState.COMPLETE }); + if (Settings.DebugMessages) + ConsoleIO.WriteLine("Forge server connection complete!"); + fmlHandshakeState = FMLHandshakeClientState.DONE; + return true; + } + } + } + #endregion + + return false; + case PacketIncomingType.KickPacket: + handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString(packetData))); + return false; + case PacketIncomingType.NetworkCompressionTreshold: + if (protocolversion >= MC18Version && protocolversion < MC19Version) compression_treshold = readNextVarInt(packetData); break; + case PacketIncomingType.ResourcePackSend: + string url = readNextString(packetData); + string hash = readNextString(packetData); + //Send back "accepted" and "successfully loaded" responses for plugins making use of resource pack mandatory + byte[] responseHeader = new byte[0]; + if (protocolversion < MC110Version) //MC 1.10 does not include resource pack hash in responses + responseHeader = concatBytes(getVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); + SendPacket(PacketOutgoingType.ResourcePackStatus, concatBytes(responseHeader, getVarInt(3))); //Accepted pack + SendPacket(PacketOutgoingType.ResourcePackStatus, concatBytes(responseHeader, getVarInt(0))); //Successfully loaded + break; default: return false; //Ignored packet } + return true; //Packet processed } - // Regular in-game packets - switch (getPacketIncomingType(packetID, protocolversion)) + catch (Exception innerException) { - case PacketIncomingType.KeepAlive: - SendPacket(PacketOutgoingType.KeepAlive, packetData); - break; - case PacketIncomingType.JoinGame: - handler.OnGameJoined(); - readNextInt(packetData); - readNextByte(packetData); - if (protocolversion >= MC191Version) - this.currentDimension = readNextInt(packetData); - else - this.currentDimension = (sbyte)readNextByte(packetData); - readNextByte(packetData); - readNextByte(packetData); - readNextString(packetData); - if (protocolversion >= MC18Version) - readNextBool(packetData); // Reduced debug info - 1.8 and above - break; - case PacketIncomingType.ChatMessage: - string message = readNextString(packetData); - try - { - //Hide system messages or xp bar messages? - byte messageType = readNextByte(packetData); - if ((messageType == 1 && !Settings.DisplaySystemMessages) - || (messageType == 2 && !Settings.DisplayXPBarMessages)) - break; - } - catch (ArgumentOutOfRangeException) { /* No message type */ } - handler.OnTextReceived(message, true); - break; - case PacketIncomingType.Respawn: - this.currentDimension = readNextInt(packetData); - readNextByte(packetData); - readNextByte(packetData); - readNextString(packetData); - break; - case PacketIncomingType.PlayerPositionAndLook: - if (Settings.TerrainAndMovements) - { - double x = readNextDouble(packetData); - double y = readNextDouble(packetData); - double z = readNextDouble(packetData); - float yaw = readNextFloat(packetData); - float pitch = readNextFloat(packetData); - byte locMask = readNextByte(packetData); - - if (protocolversion >= MC18Version) - { - Location location = handler.GetCurrentLocation(); - location.X = (locMask & 1 << 0) != 0 ? location.X + x : x; - location.Y = (locMask & 1 << 1) != 0 ? location.Y + y : y; - location.Z = (locMask & 1 << 2) != 0 ? location.Z + z : z; - handler.UpdateLocation(location, yaw, pitch); - } - else handler.UpdateLocation(new Location(x, y, z), yaw, pitch); - } - - if (protocolversion >= MC19Version) - { - int teleportID = readNextVarInt(packetData); - // Teleport confirm packet - SendPacket(PacketOutgoingType.TeleportConfirm, getVarInt(teleportID)); - } - break; - case PacketIncomingType.ChunkData: - if (Settings.TerrainAndMovements) - { - int chunkX = readNextInt(packetData); - int chunkZ = readNextInt(packetData); - bool chunksContinuous = readNextBool(packetData); - ushort chunkMask = protocolversion >= MC19Version - ? (ushort)readNextVarInt(packetData) - : readNextUShort(packetData); - if (protocolversion < MC18Version) - { - ushort addBitmap = readNextUShort(packetData); - int compressedDataSize = readNextInt(packetData); - byte[] compressed = readData(compressedDataSize, packetData); - byte[] decompressed = ZlibUtils.Decompress(compressed); - ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, new List(decompressed)); - } - else - { - int dataSize = readNextVarInt(packetData); - ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, packetData); - } - } - break; - case PacketIncomingType.MultiBlockChange: - if (Settings.TerrainAndMovements) - { - int chunkX = readNextInt(packetData); - int chunkZ = readNextInt(packetData); - int recordCount = protocolversion < MC18Version - ? (int)readNextShort(packetData) - : readNextVarInt(packetData); - - for (int i = 0; i < recordCount; i++) - { - byte locationXZ; - ushort blockIdMeta; - int blockY; - - if (protocolversion < MC18Version) - { - blockIdMeta = readNextUShort(packetData); - blockY = (ushort)readNextByte(packetData); - locationXZ = readNextByte(packetData); - } - else - { - locationXZ = readNextByte(packetData); - blockY = (ushort)readNextByte(packetData); - blockIdMeta = (ushort)readNextVarInt(packetData); - } - - int blockX = locationXZ >> 4; - int blockZ = locationXZ & 0x0F; - Block block = new Block(blockIdMeta); - handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block); - } - } - break; - case PacketIncomingType.BlockChange: - if (Settings.TerrainAndMovements) - { - if (protocolversion < MC18Version) - { - int blockX = readNextInt(packetData); - int blockY = readNextByte(packetData); - int blockZ = readNextInt(packetData); - short blockId = (short)readNextVarInt(packetData); - byte blockMeta = readNextByte(packetData); - handler.GetWorld().SetBlock(new Location(blockX, blockY, blockZ), new Block(blockId, blockMeta)); - } - else handler.GetWorld().SetBlock(Location.FromLong(readNextULong(packetData)), new Block((ushort)readNextVarInt(packetData))); - } - break; - case PacketIncomingType.MapChunkBulk: - if (protocolversion < MC19Version && Settings.TerrainAndMovements) - { - int chunkCount; - bool hasSkyLight; - List chunkData = packetData; - - //Read global fields - if (protocolversion < MC18Version) - { - chunkCount = readNextShort(packetData); - int compressedDataSize = readNextInt(packetData); - hasSkyLight = readNextBool(packetData); - byte[] compressed = readData(compressedDataSize, packetData); - byte[] decompressed = ZlibUtils.Decompress(compressed); - chunkData = new List(decompressed); - } - else - { - hasSkyLight = readNextBool(packetData); - chunkCount = 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] = readNextInt(packetData); - chunkZs[chunkColumnNo] = readNextInt(packetData); - chunkMasks[chunkColumnNo] = readNextUShort(packetData); - addBitmaps[chunkColumnNo] = protocolversion < MC18Version - ? readNextUShort(packetData) - : (ushort)0; - } - - //Process chunk records - for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) - ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, chunkData); - } - break; - case PacketIncomingType.UnloadChunk: - if (protocolversion >= MC19Version && Settings.TerrainAndMovements) - { - int chunkX = readNextInt(packetData); - int chunkZ = readNextInt(packetData); - handler.GetWorld()[chunkX, chunkZ] = null; - } - break; - case PacketIncomingType.PlayerListUpdate: - if (protocolversion >= MC18Version) - { - int action = readNextVarInt(packetData); - int numActions = readNextVarInt(packetData); - for (int i = 0; i < numActions; i++) - { - Guid uuid = readNextUUID(packetData); - switch (action) - { - case 0x00: //Player Join - string name = readNextString(packetData); - int propNum = readNextVarInt(packetData); - for (int p = 0; p < propNum; p++) - { - string key = readNextString(packetData); - string val = readNextString(packetData); - if (readNextBool(packetData)) - readNextString(packetData); - } - readNextVarInt(packetData); - readNextVarInt(packetData); - if (readNextBool(packetData)) - readNextString(packetData); - handler.OnPlayerJoin(uuid, name); - break; - case 0x01: //Update gamemode - case 0x02: //Update latency - readNextVarInt(packetData); - break; - case 0x03: //Update display name - if (readNextBool(packetData)) - readNextString(packetData); - break; - case 0x04: //Player Leave - handler.OnPlayerLeave(uuid); - break; - default: - //Unknown player list item type - break; - } - } - } - else //MC 1.7.X does not provide UUID in tab-list updates - { - string name = readNextString(packetData); - bool online = readNextBool(packetData); - short ping = readNextShort(packetData); - Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); - if (online) - handler.OnPlayerJoin(FakeUUID, name); - else handler.OnPlayerLeave(FakeUUID); - } - break; - case PacketIncomingType.TabCompleteResult: - if (protocolversion >= MC113Version) - { - autocomplete_transaction_id = readNextVarInt(packetData); - readNextVarInt(packetData); // Start of text to replace - readNextVarInt(packetData); // Length of text to replace - } - - int autocomplete_count = readNextVarInt(packetData); - autocomplete_result.Clear(); - - for (int i = 0; i < autocomplete_count; i++) - { - autocomplete_result.Add(readNextString(packetData)); - if (protocolversion >= MC113Version) - { - // Skip optional tooltip for each tab-complete result - if (readNextBool(packetData)) - readNextString(packetData); - } - } - - autocomplete_received = true; - break; - case PacketIncomingType.PluginMessage: - String channel = readNextString(packetData); - if (protocolversion < MC18Version) - { - if (forgeInfo == null) - { - // 1.7 and lower prefix plugin channel packets with the length. - // We can skip it, though. - readNextShort(packetData); - } - else - { - // Forge does something even weirder with the length. - readNextVarShort(packetData); - } - } - - // The remaining data in the array is the entire payload of the packet. - handler.OnPluginChannelMessage(channel, packetData.ToArray()); - - #region Forge Login - if (forgeInfo != null && fmlHandshakeState != FMLHandshakeClientState.DONE) - { - if (channel == "FML|HS") - { - FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)readNextByte(packetData); - - if (discriminator == FMLHandshakeDiscriminator.HandshakeReset) - { - fmlHandshakeState = FMLHandshakeClientState.START; - return true; - } - - switch (fmlHandshakeState) - { - case FMLHandshakeClientState.START: - if (discriminator != FMLHandshakeDiscriminator.ServerHello) - return false; - - // Send the plugin channel registration. - // REGISTER is somewhat special in that it doesn't actually include length information, - // and is also \0-separated. - // Also, yes, "FML" is there twice. Don't ask me why, but that's the way forge does it. - string[] channels = { "FML|HS", "FML", "FML|MP", "FML", "FORGE" }; - SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels))); - - byte fmlProtocolVersion = readNextByte(packetData); - - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion); - - if (fmlProtocolVersion >= 1) - this.currentDimension = readNextInt(packetData); - - // Tell the server we're running the same version. - SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion }); - - // Then tell the server that we're running the same mods. - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Sending falsified mod list to server..."); - byte[][] mods = new byte[forgeInfo.Mods.Count][]; - for (int i = 0; i < forgeInfo.Mods.Count; i++) - { - ForgeInfo.ForgeMod mod = forgeInfo.Mods[i]; - mods[i] = concatBytes(getString(mod.ModID), getString(mod.Version)); - } - SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList, - concatBytes(getVarInt(forgeInfo.Mods.Count), concatBytes(mods))); - - fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERDATA; - - return true; - case FMLHandshakeClientState.WAITINGSERVERDATA: - if (discriminator != FMLHandshakeDiscriminator.ModList) - return false; - - Thread.Sleep(2000); - - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Accepting server mod list..."); - // Tell the server that yes, we are OK with the mods it has - // even though we don't actually care what mods it has. - - SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, - new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA }); - - fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERCOMPLETE; - return false; - case FMLHandshakeClientState.WAITINGSERVERCOMPLETE: - // The server now will tell us a bunch of registry information. - // We need to read it all, though, until it says that there is no more. - if (discriminator != FMLHandshakeDiscriminator.RegistryData) - return false; - - if (protocolversion < MC18Version) - { - // 1.7.10 and below have one registry - // with blocks and items. - int registrySize = readNextVarInt(packetData); - - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Received registry with " + registrySize + " entries"); - - fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; - } - else - { - // 1.8+ has more than one registry. - - bool hasNextRegistry = readNextBool(packetData); - string registryName = readNextString(packetData); - int registrySize = readNextVarInt(packetData); - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Received registry " + registryName + " with " + registrySize + " entries"); - if (!hasNextRegistry) - fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; - } - - return false; - case FMLHandshakeClientState.PENDINGCOMPLETE: - // The server will ask us to accept the registries. - // Just say yes. - if (discriminator != FMLHandshakeDiscriminator.HandshakeAck) - return false; - if (Settings.DebugMessages) - ConsoleIO.WriteLineFormatted("§8Accepting server registries..."); - SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, - new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE }); - fmlHandshakeState = FMLHandshakeClientState.COMPLETE; - - return true; - case FMLHandshakeClientState.COMPLETE: - // One final "OK". On the actual forge source, a packet is sent from - // the client to the client saying that the connection was complete, but - // we don't need to do that. - - SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, - new byte[] { (byte)FMLHandshakeClientState.COMPLETE }); - if (Settings.DebugMessages) - ConsoleIO.WriteLine("Forge server connection complete!"); - fmlHandshakeState = FMLHandshakeClientState.DONE; - return true; - } - } - } - #endregion - - return false; - case PacketIncomingType.KickPacket: - handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString(packetData))); - return false; - case PacketIncomingType.NetworkCompressionTreshold: - if (protocolversion >= MC18Version && protocolversion < MC19Version) - compression_treshold = readNextVarInt(packetData); - break; - case PacketIncomingType.ResourcePackSend: - string url = readNextString(packetData); - string hash = readNextString(packetData); - //Send back "accepted" and "successfully loaded" responses for plugins making use of resource pack mandatory - byte[] responseHeader = new byte[0]; - if (protocolversion < MC110Version) //MC 1.10 does not include resource pack hash in responses - responseHeader = concatBytes(getVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); - SendPacket(PacketOutgoingType.ResourcePackStatus, concatBytes(responseHeader, getVarInt(3))); //Accepted pack - SendPacket(PacketOutgoingType.ResourcePackStatus, concatBytes(responseHeader, getVarInt(0))); //Successfully loaded - break; - default: - return false; //Ignored packet + throw new System.IO.InvalidDataException( + String.Format("Failed to process incoming packet of type {0}. (PacketID: {1}, Protocol: {2}, LoginPhase: {3}, InnerException: {4}).", + getPacketIncomingType(packetID, protocolversion), + packetID, + protocolversion, + login_phase, + innerException.GetType()), + innerException); } - return true; //Packet processed } ///