Minecraft Forge Handshake

This page documents the current Minecraft Forge protocol, including how to connect to servers running Minecraft Forge and what changes it makes to the vanilla protocol, for Minecraft 1.7.10, 1.8, and 1.9.

Largely based off of Pokechu22's work on getting Minecraft Console Client to connect to Forge servers.

Changes to Server List Ping
When forge is installed, the Server List Ping changes with an additional mod information. Example for 1.13.2:

Pre-1.13
1.12.2 and earlier use a different JSON structure:

So, to test whether forge is installed, look for the  key and then a   of

The  contains each installed mod's version and ID.

This is injected in ServerStatusResponse by calling FMLNetworkHandler.enhanceStatusQuery.

Connection to a forge server
Attempting to connect to a forge server without making any changes results in getting immediately disconnected with the message " ". This message is displayed in FMLCommonHandler and is injected in a modified 0x00 Handshake packet.

If  is added to the end of the server's address (where   is a null character) (again, in the 0x00 handshake packet), the server will not immediately reject you. However, you still will not complete the connection.

This occurs because Minecraft Forge has an additional set of handshakes (for determining mods and block IDs), that must be completed before the server allows logging in.

Forge handshake
Forge's handshake occurs in the middle of the normal login sequence, right after 0x02 Login Success is received by the client.

In addition to the normal packet debugging for the server, if  is passed to the forge server, some more information about the current handshake status will be outputted.


 * S→C:  forge's plugin channels: FML|HS, FML, FML|MP, FML, FORGE (yes, FML is there twice; technically, it does not need to be there twice but that is the way that Forge sends it and sending it that way will act more similar to forge)
 * S→C: A ServerHello packet is sent on  including the player's dimension (0 if it's the first login)
 * C→S:  the plugin channels: FML|HS, FML, FML|MP, FML, FORGE.
 * C→S: A ClientHello packet is sent on the  channel.
 * C→S: A ModList packet is sent.
 * Server checks that the mod list is compatible. If incompatible, the player is disconnected with an explanatory message.  Otherwise, continue.
 * S→C: A ModList packet is sent.
 * Client checks that the mod list is compatible. If incompatible, it disconnects.  Otherwise, continue.
 * C→S: A HandshakeAck packet is sent, with the phase being  (2).
 * S→C: A series of RegistryData packets is sent, with hasMore being true for all packets except the last.
 * Client reads and stores each RegistryData, continuing reading until hasMore is false. Once hasMore is false, it checks whether it has all of the needed blocks/items.  If it does not, it disconnects.  Otherwise, continue.
 * C→S: A HandshakeAck packet is sent with phase being  (3).
 * S→C: A HandshakeAck packet is sent with phase being  (2).
 * C→S: A HandshakeAck packet is sent with phase being  (4).
 * S→C: A HandshakeAck packet is sent with phase being  (3).
 * The server sends a "CompleteHandshake" packet to itself. (Not sure why)
 * C→S: A HandshakeAck packet is sent with phase being  (5).
 * The client sends a "CompleteHandshake" packet to itself. (Not sure why)

Definitions
Minecraft Forge uses an additional type not covered in data types for some things -- a varshort. This is essentially the same as a regular short, except that if the top byte (what is normally sign) is set, it is followed by an additional byte. This allows forge to retain backwards compatibility but extend the length of certain numbers -- the varshort is only used in places where, in vanilla Minecraft, the sign bit would not have been set. It is implemented in ByteBufUtils.

Discriminator bytes are the way that Minecraft Forge allows sending multiple messages on one plugin channel. They aren't part of the original specification, but a similar concept is used with the plugin channel (used for command blocks). That packet decides whether a block location is being sent or an entity ID (minecart command blocks) is being sent based off of the value of the first byte in the packet.

In the same way, forge uses the first byte of the plugin message to specify which packet to use. It uses FMLIndexedMessageToMessageCodec to automatically switch the value.

Discriminator bytes change depending on the channel: 0 for  means something different from 0 for.

Packet structure
is used to perform the handshake. Discriminator bytes are found in FMLHandshakeCodec.

=== ServerHello ===

Starts the handshake.

=== ClientHello ===

Response from the client to the ServerHello packet.

=== ModList ===

Contains a list of all mods installed on the server or client. Sent from the client to the server first, and then the server responds with its mod list. The server's mod list matches the one sent in the ping.

=== RegistryData ===

The server sends several of this packet, one for each registry. It'll keep sending them until the hasMore value is no longer true.

=== HandshakeAck ===

Confirms that the client is able to use the settings sent previously.

=== HandshakeReset ===

Causes the client to recomplete the entire handshake from the start. There is no payload beyond the discriminator byte.

The normal forge server does not ever use this packet, but it is used when connecting through a BungeeCord instance, specifically when transitioning from a vanilla server to a modded one or from a modded server to another modded server.

and multipart packets
Forge supports splitting large clientbound plugin message packets into smaller multipart packets. This functionality does not exist serverbound. This system is available in all versions.

This system starts by sending an  packet to indicate what the actual packet is (note that this is within the plugin message itself):

This is followed by a series of additional  packets which write the payload. Each packet is prefixed with the part number as an unsigned byte (starting at 0), followed by the rest of the packet containing up to 1048495 bytes of the actual message. Each packet must be sent in order.

Up to 52424750 bytes (~52 megabytes) may be sent through this system.

Differences from Forge 1.7.10
Forge for Minecraft 1.8 made some changes from the 1.7.10 version.

The most important thing to keep track of isn't (entirely) a forge change. In 1.7.10, plugin channel packets are length prefixed, while in 1.8 they are not. However, forge makes some more changes to the server to client packet (but not the client to server packet): Rather than using a short for the length, a varshort is used. Due to the way that varshorts work, this is backwards compatible, but if this is not accounted for you may receive forge packets that seem corrupted as there is an extra byte that appears seemingly randomly.

Additionally, in 1.7.10 the registries packet is instead "ModIdData":

=== ModIdData ===

Contains numeric ID mapping for blocks and items.