diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 20e219309..a29bef0c0 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -551,6 +551,16 @@ void cClientHandle::RemoveFromAllChunks()
 
 
 
+void cClientHandle::HandleNPCTrade(int a_SlotNum)
+{
+	// TODO
+	LOGWARNING("%s: Not implemented yet", __FUNCTION__);
+}
+
+
+
+
+
 void cClientHandle::HandlePing(void)
 {
 	// Somebody tries to retrieve information about the server
@@ -573,7 +583,6 @@ void cClientHandle::HandlePing(void)
 
 bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Username)
 {
-	LOGD("LOGIN %s", a_Username.c_str());
 	m_Username = a_Username;
 
 	if (cRoot::Get()->GetPluginManager()->CallHookLogin(this, a_ProtocolVersion, a_Username))
@@ -676,25 +685,7 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ,
 
 void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message)
 {
-	if (a_Channel == "MC|AdvCdm")
-	{
-		// Command block, set text, Client -> Server
-		HandleCommandBlockMessage(a_Message.c_str(), a_Message.size());
-	}
-	else if (a_Channel == "MC|Brand")
-	{
-		// Client <-> Server branding exchange
-		SendPluginMessage("MC|Brand", "MCServer");
-	}
-	else if (a_Channel == "MC|Beacon")
-	{
-		HandleBeaconSelection(a_Message.c_str(), a_Message.size());
-	}
-	else if (a_Channel == "MC|ItemName")
-	{
-		HandleAnvilItemName(a_Message.c_str(), a_Message.size());
-	}
-	else if (a_Channel == "REGISTER")
+	if (a_Channel == "REGISTER")
 	{
 		if (HasPluginChannel(a_Channel))
 		{
@@ -777,15 +768,8 @@ void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList
 
 
 
-void cClientHandle::HandleBeaconSelection(const char * a_Data, size_t a_Length)
+void cClientHandle::HandleBeaconSelection(int a_PrimaryEffect, int a_SecondaryEffect)
 {
-	if (a_Length < 14)
-	{
-		SendChat("Failure setting beacon selection; bad request", mtFailure);
-		LOGD("Malformed MC|Beacon packet.");
-		return;
-	}
-
 	cWindow * Window = m_Player->GetWindow();
 	if ((Window == NULL) || (Window->GetWindowType() != cWindow::wtBeacon))
 	{
@@ -798,23 +782,15 @@ void cClientHandle::HandleBeaconSelection(const char * a_Data, size_t a_Length)
 		return;
 	}
 
-	cByteBuffer Buffer(a_Length);
-	Buffer.Write(a_Data, a_Length);
-
-	int PrimaryEffectID, SecondaryEffectID;
-	Buffer.ReadBEInt(PrimaryEffectID);
-	Buffer.ReadBEInt(SecondaryEffectID);
-
 	cEntityEffect::eType PrimaryEffect = cEntityEffect::effNoEffect;
-	if ((PrimaryEffectID >= 0) && (PrimaryEffectID <= (int)cEntityEffect::effSaturation))
+	if ((a_PrimaryEffect >= 0) && (a_PrimaryEffect <= (int)cEntityEffect::effSaturation))
 	{
-		PrimaryEffect = (cEntityEffect::eType)PrimaryEffectID;
+		PrimaryEffect = (cEntityEffect::eType)a_PrimaryEffect;
 	}
-
 	cEntityEffect::eType SecondaryEffect = cEntityEffect::effNoEffect;
-	if ((SecondaryEffectID >= 0) && (SecondaryEffectID <= (int)cEntityEffect::effSaturation))
+	if ((a_SecondaryEffect >= 0) && (a_SecondaryEffect <= (int)cEntityEffect::effSaturation))
 	{
-		SecondaryEffect = (cEntityEffect::eType)SecondaryEffectID;
+		SecondaryEffect = (cEntityEffect::eType)a_SecondaryEffect;
 	}
 
 	Window->SetSlot(*m_Player, 0, cItem());
@@ -841,52 +817,12 @@ void cClientHandle::HandleBeaconSelection(const char * a_Data, size_t a_Length)
 
 
 
-void cClientHandle::HandleCommandBlockMessage(const char * a_Data, size_t a_Length)
+void cClientHandle::HandleCommandBlockBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_NewCommand)
 {
-	if (a_Length < 14)
-	{
-		SendChat("Failure setting command block command; bad request", mtFailure);
-		LOGD("Malformed MC|AdvCdm packet.");
-		return;
-	}
-
-	cByteBuffer Buffer(a_Length);
-	Buffer.Write(a_Data, a_Length);
-
-	int BlockX, BlockY, BlockZ;
-
-	AString Command;
-
-	char Mode;
-
-	Buffer.ReadChar(Mode);
-
-	switch (Mode)
-	{
-		case 0x00:
-		{
-			Buffer.ReadBEInt(BlockX);
-			Buffer.ReadBEInt(BlockY);
-			Buffer.ReadBEInt(BlockZ);
-
-			Buffer.ReadVarUTF8String(Command);
-			break;
-		}
-
-		default:
-		{
-			SendChat("Failure setting command block command; unhandled mode", mtFailure);
-			LOGD("Unhandled MC|AdvCdm packet mode.");
-			return;
-		}
-	}
-
 	cWorld * World = m_Player->GetWorld();
-
 	if (World->AreCommandBlocksEnabled())
 	{
-		World->SetCommandBlockCommand(BlockX, BlockY, BlockZ, Command);
-		
+		World->SetCommandBlockCommand(a_BlockX, a_BlockY, a_BlockZ, a_NewCommand);
 		SendChat("Successfully set command block command", mtSuccess);
 	}
 	else
@@ -899,22 +835,26 @@ void cClientHandle::HandleCommandBlockMessage(const char * a_Data, size_t a_Leng
 
 
 
-void cClientHandle::HandleAnvilItemName(const char * a_Data, size_t a_Length)
+void cClientHandle::HandleCommandBlockEntityChange(int a_EntityID, const AString & a_NewCommand)
 {
-	if (a_Length < 1)
-	{
-		return;
-	}
+	// TODO
+	LOGWARNING("%s: Not implemented yet", __FUNCTION__);
+}
 
+
+
+
+
+void cClientHandle::HandleAnvilItemName(const AString & a_ItemName)
+{
 	if ((m_Player->GetWindow() == NULL) || (m_Player->GetWindow()->GetWindowType() != cWindow::wtAnvil))
 	{
 		return;
 	}
 
-	AString Name(a_Data, a_Length);
-	if (Name.length() <= 30)
+	if (a_ItemName.length() <= 30)
 	{
-		((cAnvilWindow *)m_Player->GetWindow())->SetRepairedItemName(Name, m_Player);
+		((cAnvilWindow *)m_Player->GetWindow())->SetRepairedItemName(a_ItemName, m_Player);
 	}
 }
 
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 10cf6ae28..1f22762c0 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -222,6 +222,13 @@ public:
 	
 	bool HasPluginChannel(const AString & a_PluginChannel);
 	
+	/** Called by the protocol when it receives the MC|Brand plugin message. Also callable by plugins.
+	Simply stores the string value. */
+	void SetClientBrand(const AString & a_ClientBrand) { m_ClientBrand = a_ClientBrand; }
+	
+	/** Returns the client brand received in the MC|Brand plugin message or set by a plugin. */
+	const AString & GetClientBrand(void) const { return m_ClientBrand; }
+	
 	// tolua_end
 	
 	/** Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) */
@@ -236,12 +243,31 @@ public:
 	void PacketError(unsigned char a_PacketType);
 
 	// Calls that cProtocol descendants use for handling packets:
-	void HandleAnimation        (char a_Animation);
-	void HandleChat             (const AString & a_Message);
-	void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem);
-	void HandleEntityCrouch     (int a_EntityID, bool a_IsCrouching);
-	void HandleEntityLeaveBed   (int a_EntityID);
-	void HandleEntitySprinting  (int a_EntityID, bool a_IsSprinting);
+	void HandleAnimation(char a_Animation);
+	
+	/** Called when the protocol receives a MC|ItemName plugin message, indicating that the player named
+	an item in the anvil UI. */
+	void HandleAnvilItemName(const AString & a_ItemName);
+	
+	/** Called when the protocol receives a MC|Beacon plugin message, indicating that the player set an effect
+	in the beacon UI. */
+	void HandleBeaconSelection(int a_PrimaryEffect, int a_SecondaryEffect);
+	
+	/** Called when the protocol detects a chat packet. */
+	void HandleChat(const AString & a_Message);
+	
+	/** Called when the protocol receives a MC|AdvCdm plugin message, indicating that the player set a new
+	command in the command block UI, for a block-based commandblock. */
+	void HandleCommandBlockBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_NewCommand);
+	
+	/** Called when the protocol receives a MC|AdvCdm plugin message, indicating that the player set a new
+	command in the command block UI, for an entity-based commandblock (minecart?). */
+	void HandleCommandBlockEntityChange(int a_EntityID, const AString & a_NewCommand);
+	
+	void HandleCreativeInventory      (short a_SlotNum, const cItem & a_HeldItem);
+	void HandleEntityCrouch           (int a_EntityID, bool a_IsCrouching);
+	void HandleEntityLeaveBed         (int a_EntityID);
+	void HandleEntitySprinting        (int a_EntityID, bool a_IsSprinting);
 	
 	/** Called when the protocol handshake has been received (for protocol versions that support it;
 	otherwise the first instant when a username is received).
@@ -251,6 +277,11 @@ public:
 	
 	void HandleKeepAlive        (int a_KeepAliveID);
 	void HandleLeftClick        (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, char a_Status);
+	
+	/** Called when the protocol receives a MC|TrSel packet, indicating that the player used a trade in
+	the NPC UI. */
+	void HandleNPCTrade(int a_SlotNum);
+	
 	void HandlePing             (void);
 	void HandlePlayerAbilities  (bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed);
 	void HandlePlayerLook       (float a_Rotation, float a_Pitch, bool a_IsOnGround);
@@ -392,6 +423,9 @@ private:
 	
 	/** The plugin channels that the client has registered. */
 	cChannels m_PluginChannels;
+	
+	/** The brand identification of the client, as received in the MC|Brand plugin message or set from a plugin. */
+	AString m_ClientBrand;
 
 
 	/** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */
@@ -421,15 +455,6 @@ private:
 	/** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */
 	void UnregisterPluginChannels(const AStringVector & a_ChannelList);
 
-	/** Handles the "MC|Beacon" plugin message */
-	void HandleBeaconSelection(const char * a_Data, size_t a_Length);
-
-	/** Handles the "MC|AdvCdm" plugin message */
-	void HandleCommandBlockMessage(const char * a_Data, size_t a_Length);
-
-	/** Handles the "MC|ItemName" plugin message */
-	void HandleAnvilItemName(const char * a_Data, size_t a_Length);
-	
 	// cSocketThreads::cCallback overrides:
 	virtual bool DataReceived   (const char * a_Data, size_t a_Size) override;  // Data is received from the client
 	virtual void GetOutgoingData(AString & a_Data) override;  // Data can be sent to client
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index e4c33908a..07338f395 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -2064,6 +2064,22 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
 {
 	HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Channel);
 	HANDLE_READ(a_ByteBuffer, ReadBEShort,       short,   Length);
+	if (Length + 1 != (int)a_ByteBuffer.GetReadableSpace())
+	{
+		LOGD("Invalid plugin message packet, payload length doesn't match packet length (exp %d, got %d)",
+			(int)a_ByteBuffer.GetReadableSpace() - 1, Length
+		);
+		return;
+	}
+	
+	// If the plugin channel is recognized vanilla, handle it directly:
+	if (Channel.substr(0, 3) == "MC|")
+	{
+		HandleVanillaPluginMessage(a_ByteBuffer, Channel, Length);
+		return;
+	}
+	
+	// Read the plugin message and relay to clienthandle:
 	AString Data;
 	if (!a_ByteBuffer.ReadString(Data, Length))
 	{
@@ -2217,6 +2233,82 @@ void cProtocol172::HandlePacketWindowClose(cByteBuffer & a_ByteBuffer)
 
 
 
+void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel, short a_PayloadLength)
+{
+	if (a_Channel == "MC|AdvCdm")
+	{
+		HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Mode)
+		switch (Mode)
+		{
+			case 0x00:
+			{
+				// Block-based commandblock update:
+				HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockX);
+				HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockY);
+				HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockZ);
+				HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Command);
+				m_Client->HandleCommandBlockBlockChange(BlockX, BlockY, BlockZ, Command);
+				break;
+			}
+
+			// TODO: Entity-based commandblock update
+			
+			default:
+			{
+				m_Client->SendChat(Printf("Failure setting command block command; unhandled mode %d", Mode), mtFailure);
+				LOG("Unhandled MC|AdvCdm packet mode.");
+				return;
+			}
+		}  // switch (Mode)
+		return;
+	}
+	else if (a_Channel == "MC|Brand")
+	{
+		// Read the client's brand:
+		AString Brand;
+		if (a_ByteBuffer.ReadString(Brand, a_PayloadLength))
+		{
+			m_Client->SetClientBrand(Brand);
+		}
+		
+		// Send back our brand:
+		SendPluginMessage("MC|Brand", "MCServer");
+		return;
+	}
+	else if (a_Channel == "MC|Beacon")
+	{
+		HANDLE_READ(a_ByteBuffer, ReadBEInt, int, Effect1);
+		HANDLE_READ(a_ByteBuffer, ReadBEInt, int, Effect2);
+		m_Client->HandleBeaconSelection(Effect1, Effect2);
+		return;
+	}
+	else if (a_Channel == "MC|ItemName")
+	{
+		AString ItemName;
+		if (a_ByteBuffer.ReadString(ItemName, a_PayloadLength))
+		{
+			m_Client->HandleAnvilItemName(ItemName);
+		}
+		return;
+	}
+	else if (a_Channel == "MC|TrSel")
+	{
+		HANDLE_READ(a_ByteBuffer, ReadBEInt, int, SlotNum);
+		m_Client->HandleNPCTrade(SlotNum);
+		return;
+	}
+	LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str());
+	
+	// Read the payload and send it through to the clienthandle:
+	AString Message;
+	VERIFY(a_ByteBuffer.ReadString(Message, a_PayloadLength));
+	m_Client->HandlePluginMessage(a_Channel, Message);
+}
+
+
+
+
+
 void cProtocol172::SendData(const char * a_Data, size_t a_Size)
 {
 	if (m_IsEncrypted)
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index 0bc86a72a..7709df59d 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -295,6 +295,9 @@ protected:
 	void HandlePacketWindowClick            (cByteBuffer & a_ByteBuffer);
 	void HandlePacketWindowClose            (cByteBuffer & a_ByteBuffer);
 	
+	/** Parses Vanilla plugin messages into specific ClientHandle calls.
+	The message payload is still in the bytebuffer, to be read by this function. */
+	void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel, short a_PayloadLength);
 	
 	/** Sends the data to the client, encrypting them if needed. */
 	virtual void SendData(const char * a_Data, size_t a_Size) override;
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index 1070a8434..acdb48cf7 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -989,10 +989,6 @@ void cProtocol180::SendPluginMessage(const AString & a_Channel, const AString &
 	
 	cPacketizer Pkt(*this, 0x3f);
 	Pkt.WriteString(a_Channel);
-	if (a_Channel.substr(0, 3) == "MC|")
-	{
-		Pkt.WriteVarInt((UInt32)a_Message.size());
-	}
 	Pkt.WriteBuf(a_Message.data(), a_Message.size());
 }
 
@@ -2324,18 +2320,17 @@ void cProtocol180::HandlePacketPlayerPosLook(cByteBuffer & a_ByteBuffer)
 void cProtocol180::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
 {
 	HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Channel);
-	AString Data;
+	
+	// If the plugin channel is recognized vanilla, handle it directly:
 	if (Channel.substr(0, 3) == "MC|")
 	{
-		// Vanilla sends the payload length within the payload itself, so skip it:
-		HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, DataLen);
-		if (DataLen != a_ByteBuffer.GetReadableSpace() - 1)
-		{
-			ASSERT(!"Bad plugin message payload length");
-			return;
-		}
+		HandleVanillaPluginMessage(a_ByteBuffer, Channel);
+		return;
 	}
-	a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1);  // Always succeeds
+
+	// Read the plugin message and relay to clienthandle:
+	AString Data;
+	VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1));  // Always succeeds
 	m_Client->HandlePluginMessage(Channel, Data);
 }
 
@@ -2524,6 +2519,71 @@ void cProtocol180::HandlePacketWindowClose(cByteBuffer & a_ByteBuffer)
 
 
 
+void cProtocol180::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel)
+{
+	if (a_Channel == "MC|AdvCdm")
+	{
+		HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Mode)
+		switch (Mode)
+		{
+			case 0x00:
+			{
+				HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockX);
+				HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockY);
+				HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockZ);
+				HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Command);
+				m_Client->HandleCommandBlockBlockChange(BlockX, BlockY, BlockZ, Command);
+				break;
+			}
+
+			default:
+			{
+				m_Client->SendChat(Printf("Failure setting command block command; unhandled mode %d", Mode), mtFailure);
+				LOG("Unhandled MC|AdvCdm packet mode.");
+				return;
+			}
+		}  // switch (Mode)
+		return;
+	}
+	else if (a_Channel == "MC|Brand")
+	{
+		HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Brand);
+		m_Client->SetClientBrand(Brand);
+		// Send back our brand, including the length:
+		SendPluginMessage("MC|Brand", "\x08MCServer");
+		return;
+	}
+	else if (a_Channel == "MC|Beacon")
+	{
+		HANDLE_READ(a_ByteBuffer, ReadBEInt, int, Effect1);
+		HANDLE_READ(a_ByteBuffer, ReadBEInt, int, Effect2);
+		m_Client->HandleBeaconSelection(Effect1, Effect2);
+		return;
+	}
+	else if (a_Channel == "MC|ItemName")
+	{
+		HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, ItemName);
+		m_Client->HandleAnvilItemName(ItemName);
+		return;
+	}
+	else if (a_Channel == "MC|TrSel")
+	{
+		HANDLE_READ(a_ByteBuffer, ReadBEInt, int, SlotNum);
+		m_Client->HandleNPCTrade(SlotNum);
+		return;
+	}
+	LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str());
+	
+	// Read the payload and send it through to the clienthandle:
+	AString Message;
+	VERIFY(a_ByteBuffer.ReadString(Message, a_ByteBuffer.GetReadableSpace() - 1));
+	m_Client->HandlePluginMessage(a_Channel, Message);
+}
+
+
+
+
+
 void cProtocol180::SendData(const char * a_Data, size_t a_Size)
 {
 	if (m_IsEncrypted)
diff --git a/src/Protocol/Protocol18x.h b/src/Protocol/Protocol18x.h
index acc167a6d..8c0b77a21 100644
--- a/src/Protocol/Protocol18x.h
+++ b/src/Protocol/Protocol18x.h
@@ -312,6 +312,10 @@ protected:
 	void HandlePacketWindowClick            (cByteBuffer & a_ByteBuffer);
 	void HandlePacketWindowClose            (cByteBuffer & a_ByteBuffer);
 	
+	/** Parses Vanilla plugin messages into specific ClientHandle calls.
+	The message payload is still in the bytebuffer, the handler reads it specifically for each handled channel */
+	void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel);
+	
 	
 	/** Sends the data to the client, encrypting them if needed. */
 	virtual void SendData(const char * a_Data, size_t a_Size) override;