From d7066f43d3fd592457e69a46f0fed098c80b3190 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 30 Sep 2014 13:33:57 +0200 Subject: Rewritten plugin messages, vanilla are being parsed directly. This should finally fix the compatibility problems between 1.7 and 1.8 protocols with the changes in the vanilla plugin messages. --- src/ClientHandle.cpp | 120 +++++++++++-------------------------------- src/ClientHandle.h | 55 ++++++++++++++------ src/Protocol/Protocol17x.cpp | 92 +++++++++++++++++++++++++++++++++ src/Protocol/Protocol17x.h | 3 ++ src/Protocol/Protocol18x.cpp | 86 ++++++++++++++++++++++++++----- src/Protocol/Protocol18x.h | 4 ++ 6 files changed, 242 insertions(+), 118 deletions(-) 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; -- cgit v1.2.3