/// <summary>
 /// Send a forge plugin channel packet ("FML|HS"). Compression and encryption will be handled automatically.
 /// </summary>
 /// <param name="discriminator">Discriminator to use.</param>
 /// <param name="data">packet Data</param>
 private void SendForgeHandshakePacket(FMLHandshakeDiscriminator discriminator, byte[] data)
 {
     protocol18.SendPluginChannelPacket("FML|HS", dataTypes.ConcatBytes(new byte[] { (byte)discriminator }, data));
 }
Example #2
0
 /// <summary>
 /// Send a forge plugin channel packet ("FML|HS").  Compression and encryption will be handled automatically
 /// </summary>
 /// <param name="discriminator">Discriminator to use.</param>
 /// <param name="data">packet Data</param>
 private void SendForgeHandshakePacket(FMLHandshakeDiscriminator discriminator, byte[] data)
 {
     SendPluginChannelPacket("FML|HS", concatBytes(new byte[] { (byte)discriminator }, data));
 }
        /// <summary>
        /// Handle Forge plugin messages
        /// </summary>
        /// <param name="channel">Plugin message channel</param>
        /// <param name="packetData">Plugin message data</param>
        /// <param name="currentDimension">Current world dimension</param>
        /// <returns>TRUE if the plugin message was recognized and handled</returns>
        public bool HandlePluginMessage(string channel, List <byte> packetData, ref int currentDimension)
        {
            if (ForgeEnabled() && fmlHandshakeState != FMLHandshakeClientState.DONE)
            {
                if (channel == "FML|HS")
                {
                    FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)dataTypes.ReadNextByte(packetData);

                    if (discriminator == FMLHandshakeDiscriminator.HandshakeReset)
                    {
                        fmlHandshakeState = FMLHandshakeClientState.START;
                        return(true);
                    }

                    switch (fmlHandshakeState)
                    {
                    case FMLHandshakeClientState.START:
                        if (discriminator != FMLHandshakeDiscriminator.ServerHello)
                        {
                            return(false);
                        }

                        // Send the plugin channel registration.
                        // REGISTER is somewhat special in that it doesn't actually include length information,
                        // and is also \0-separated.
                        // Also, yes, "FML" is there twice.  Don't ask me why, but that's the way forge does it.
                        string[] channels = { "FML|HS", "FML", "FML|MP", "FML", "FORGE" };
                        protocol18.SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels)));

                        byte fmlProtocolVersion = dataTypes.ReadNextByte(packetData);

                        if (Settings.DebugMessages)
                        {
                            ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion);
                        }

                        if (fmlProtocolVersion >= 1)
                        {
                            currentDimension = dataTypes.ReadNextInt(packetData);
                        }

                        // Tell the server we're running the same version.
                        SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion });

                        // Then tell the server that we're running the same mods.
                        if (Settings.DebugMessages)
                        {
                            ConsoleIO.WriteLineFormatted("§8Sending falsified mod list to server...");
                        }
                        byte[][] mods = new byte[forgeInfo.Mods.Count][];
                        for (int i = 0; i < forgeInfo.Mods.Count; i++)
                        {
                            ForgeInfo.ForgeMod mod = forgeInfo.Mods[i];
                            mods[i] = dataTypes.ConcatBytes(dataTypes.GetString(mod.ModID), dataTypes.GetString(mod.Version));
                        }
                        SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList,
                                                 dataTypes.ConcatBytes(dataTypes.GetVarInt(forgeInfo.Mods.Count), dataTypes.ConcatBytes(mods)));

                        fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERDATA;

                        return(true);

                    case FMLHandshakeClientState.WAITINGSERVERDATA:
                        if (discriminator != FMLHandshakeDiscriminator.ModList)
                        {
                            return(false);
                        }

                        Thread.Sleep(2000);

                        if (Settings.DebugMessages)
                        {
                            ConsoleIO.WriteLineFormatted("§8Accepting server mod list...");
                        }
                        // Tell the server that yes, we are OK with the mods it has
                        // even though we don't actually care what mods it has.

                        SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
                                                 new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA });

                        fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERCOMPLETE;
                        return(false);

                    case FMLHandshakeClientState.WAITINGSERVERCOMPLETE:
                        // The server now will tell us a bunch of registry information.
                        // We need to read it all, though, until it says that there is no more.
                        if (discriminator != FMLHandshakeDiscriminator.RegistryData)
                        {
                            return(false);
                        }

                        if (protocolversion < Protocol18Handler.MC18Version)
                        {
                            // 1.7.10 and below have one registry
                            // with blocks and items.
                            int registrySize = dataTypes.ReadNextVarInt(packetData);

                            if (Settings.DebugMessages)
                            {
                                ConsoleIO.WriteLineFormatted("§8Received registry with " + registrySize + " entries");
                            }

                            fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
                        }
                        else
                        {
                            // 1.8+ has more than one registry.

                            bool   hasNextRegistry = dataTypes.ReadNextBool(packetData);
                            string registryName    = dataTypes.ReadNextString(packetData);
                            int    registrySize    = dataTypes.ReadNextVarInt(packetData);
                            if (Settings.DebugMessages)
                            {
                                ConsoleIO.WriteLineFormatted("§8Received registry " + registryName + " with " + registrySize + " entries");
                            }
                            if (!hasNextRegistry)
                            {
                                fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
                            }
                        }

                        return(false);

                    case FMLHandshakeClientState.PENDINGCOMPLETE:
                        // The server will ask us to accept the registries.
                        // Just say yes.
                        if (discriminator != FMLHandshakeDiscriminator.HandshakeAck)
                        {
                            return(false);
                        }
                        if (Settings.DebugMessages)
                        {
                            ConsoleIO.WriteLineFormatted("§8Accepting server registries...");
                        }
                        SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
                                                 new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE });
                        fmlHandshakeState = FMLHandshakeClientState.COMPLETE;

                        return(true);

                    case FMLHandshakeClientState.COMPLETE:
                        // One final "OK".  On the actual forge source, a packet is sent from
                        // the client to the client saying that the connection was complete, but
                        // we don't need to do that.

                        SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
                                                 new byte[] { (byte)FMLHandshakeClientState.COMPLETE });
                        if (Settings.DebugMessages)
                        {
                            ConsoleIO.WriteLine("Forge server connection complete!");
                        }
                        fmlHandshakeState = FMLHandshakeClientState.DONE;
                        return(true);
                    }
                }
            }
            return(false);
        }
        private bool handlePacket(int packetID, List <byte> packetData)
        {
            if (login_phase)
            {
                switch (packetID) //Packet IDs are different while logging in
                {
                case 0x03:
                    if (protocolversion >= MC18Version)
                    {
                        compression_treshold = readNextVarInt(packetData);
                    }
                    break;

                default:
                    return(false);    //Ignored packet
                }
            }
            switch (getPacketIncomingType(packetID))
            {
            case PacketIncomingType.KeepAlive:
                SendPacket(PacketOutgoingType.KeepAlive, packetData);
                handler.OnKeepAlive();
                break;

            case PacketIncomingType.JoinGame:
                handler.OnGameJoin();
                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);
                break;

            case PacketIncomingType.Respawn:
                this.currentDimension = readNextInt(packetData);
                readNextByte(packetData);
                readNextByte(packetData);
                readNextString(packetData);
                break;

            case PacketIncomingType.PlayerPositionAndLook:
                if (protocolversion >= MC19Version)
                {
                    int teleportID = readNextVarInt(packetData);
                    // Teleport confirm packet
                    SendPacket(PacketOutgoingType.TeleportConfirm, getVarInt(teleportID));
                }
                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);
                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 (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.
                            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);

                            // 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);

                                fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
                            }
                            else
                            {
                                // 1.8+ has more than one registry.

                                bool   hasNextRegistry = readNextBool(packetData);
                                string registryName    = readNextString(packetData);
                                int    registrySize    = readNextVarInt(packetData);
                                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);
                            }
                            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 });
                            fmlHandshakeState = FMLHandshakeClientState.DONE;
                            return(true);
                        }
                    }
                }
                #endregion
                return(false);

            case PacketIncomingType.KickPacket:
                handler.OnConnectionLost(BotUtils.DisconnectReason.InGameKick, 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
        }