From 0f1f7583aeea65335b2ee051585a857b1142a927 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sat, 15 Feb 2014 23:16:44 +0100 Subject: [PATCH] Implemented cCompositeChat. This allows plugins to send composite chat messages, containing URLs, commands to run and cmdline suggestions. Fixes #678. --- src/Bindings/AllToLua.pkg | 1 + src/ClientHandle.cpp | 12 +- src/ClientHandle.h | 4 +- src/CompositeChat.cpp | 206 ++++++++++++++++++++++++++++ src/CompositeChat.h | 172 +++++++++++++++++++++++ src/Defines.h | 9 +- src/Entities/Player.h | 1 + src/Protocol/Protocol.h | 2 + src/Protocol/Protocol125.cpp | 38 +++++ src/Protocol/Protocol125.h | 1 + src/Protocol/Protocol17x.cpp | 153 +++++++++++++++++++++ src/Protocol/Protocol17x.h | 38 +++-- src/Protocol/ProtocolRecognizer.cpp | 10 ++ src/Protocol/ProtocolRecognizer.h | 1 + src/Root.cpp | 14 +- src/Root.h | 6 +- src/World.cpp | 20 ++- src/World.h | 4 +- 18 files changed, 671 insertions(+), 21 deletions(-) create mode 100644 src/CompositeChat.cpp create mode 100644 src/CompositeChat.h diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index f65aed9bb..6c295102f 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -70,6 +70,7 @@ $cfile "../Generating/ChunkDesc.h" $cfile "../CraftingRecipes.h" $cfile "../UI/Window.h" $cfile "../Mobs/Monster.h" +$cfile "../CompositeChat.h" diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 1b3ebc3d4..f8a707324 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -31,6 +31,7 @@ #include "MersenneTwister.h" #include "Protocol/ProtocolRecognizer.h" +#include "CompositeChat.h" @@ -1729,7 +1730,7 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock -void cClientHandle::SendChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const AString & a_AdditionalData) +void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData) { bool ShouldAppendChatPrefixes = true; @@ -1840,6 +1841,15 @@ void cClientHandle::SendChat(const AString & a_Message, ChatPrefixCodes a_ChatPr +void cClientHandle::SendChat(const cCompositeChat & a_Message) +{ + m_Protocol->SendChat(a_Message); +} + + + + + void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { ASSERT(m_Player != NULL); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index d9a86d983..034fe07c2 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -34,6 +34,7 @@ class cWindow; class cFallingBlock; class cItemHandler; class cWorld; +class cCompositeChat; @@ -89,7 +90,8 @@ public: void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage); void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); - void SendChat (const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const AString & a_AdditionalData = ""); + void SendChat (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); + void SendChat (const cCompositeChat & a_Message); void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); void SendDestroyEntity (const cEntity & a_Entity); diff --git a/src/CompositeChat.cpp b/src/CompositeChat.cpp new file mode 100644 index 000000000..16ae58f56 --- /dev/null +++ b/src/CompositeChat.cpp @@ -0,0 +1,206 @@ + +// CompositeChat.cpp + +// Implements the cCompositeChat class used to wrap a chat message with multiple parts (text, url, cmd) + +#include "Globals.h" +#include "CompositeChat.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat: + +cCompositeChat::cCompositeChat(void) : + m_MessageType(mtCustom) +{ +} + + + + + +cCompositeChat::cCompositeChat(const AString & a_ParseText) : + m_MessageType(mtCustom) +{ + ParseText(a_ParseText); +} + + + + + +cCompositeChat::~cCompositeChat() +{ + Clear(); +} + + + + + +void cCompositeChat::Clear(void) +{ + for (cParts::iterator itr = m_Parts.begin(), end = m_Parts.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Parts[] + m_Parts.clear(); +} + + + + + +void cCompositeChat::AddTextPart(const AString & a_Message, const AString & a_Style) +{ + m_Parts.push_back(new cTextPart(a_Message, a_Style)); +} + + + + + +void cCompositeChat::AddClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style) +{ + m_Parts.push_back(new cClientTranslatedPart(a_TranslationID, a_Parameters, a_Style)); +} + + + + + +void cCompositeChat::AddUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style) +{ + m_Parts.push_back(new cUrlPart(a_Text, a_Url, a_Style)); +} + + + + + +void cCompositeChat::AddRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style) +{ + m_Parts.push_back(new cRunCommandPart(a_Text, a_Command, a_Style)); +} + + + + + +void cCompositeChat::AddSuggestCommandPart(const AString & a_Text, const AString & a_SuggestedCommand, const AString & a_Style) +{ + m_Parts.push_back(new cSuggestCommandPart(a_Text, a_SuggestedCommand, a_Style)); +} + + + + + +void cCompositeChat::ParseText(const AString & a_ParseText) +{ + // TODO +} + + + + + +void cCompositeChat::SetMessageType(eMessageType a_MessageType) +{ + m_MessageType = a_MessageType; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cBasePart: + +cCompositeChat::cBasePart::cBasePart(cCompositeChat::ePartType a_PartType, const AString & a_Text, const AString & a_Style) : + m_PartType(a_PartType), + m_Text(a_Text), + m_Style(a_Style) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cTextPart: + +cCompositeChat::cTextPart::cTextPart(const AString & a_Text, const AString &a_Style) : + super(ptText, a_Text, a_Style) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cClientTranslatedPart: + +cCompositeChat::cClientTranslatedPart::cClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style) : + super(ptClientTranslated, a_TranslationID, a_Style), + m_Parameters(a_Parameters) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cUrlPart: + +cCompositeChat::cUrlPart::cUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style) : + super(ptUrl, a_Text, a_Style), + m_Url(a_Url) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cCommandPart: + +cCompositeChat::cCommandPart::cCommandPart(ePartType a_PartType, const AString & a_Text, const AString & a_Command, const AString & a_Style) : + super(a_PartType, a_Text, a_Style), + m_Command(a_Command) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cRunCommandPart: + +cCompositeChat::cRunCommandPart::cRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style) : + super(ptRunCommand, a_Text, a_Command, a_Style) +{ +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cSuggestCommandPart: + +cCompositeChat::cSuggestCommandPart::cSuggestCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style) : + super(ptSuggestCommand, a_Text, a_Command, a_Style) +{ +} + + + + diff --git a/src/CompositeChat.h b/src/CompositeChat.h new file mode 100644 index 000000000..e220f6345 --- /dev/null +++ b/src/CompositeChat.h @@ -0,0 +1,172 @@ + +// CompositeChat.h + +// Declares the cCompositeChat class used to wrap a chat message with multiple parts (text, url, cmd) + +#include "Defines.h" + + + + + +// tolua_begin +/** Container for a single chat message composed of multiple functional parts. +Each part corresponds roughly to the behavior supported by the client messaging: + - plain text, optionaly colorized / styled + - clickable URLs + - clickable commands (run) + - clickable commands (suggest) +Each part has a text assigned to it that can be styled. The style is specified using a string, +each character / character combination in the string specifies the style to use: + - b = bold + - i = italic + - u = underlined + - s = strikethrough + - o = obfuscated + - @X = color X (X is 0 - 9 or a - f, same as dye meta +If the protocol version doesn't support all the features, it degrades gracefully. +*/ +class cCompositeChat +{ +public: + // tolua_end + + enum ePartType + { + ptText, + ptClientTranslated, + ptUrl, + ptRunCommand, + ptSuggestCommand, + } ; + + class cBasePart + { + public: + ePartType m_PartType; + AString m_Text; + AString m_Style; + + cBasePart(ePartType a_PartType, const AString & a_Text, const AString & a_Style = ""); + } ; + + class cTextPart : + public cBasePart + { + typedef cBasePart super; + public: + cTextPart(const AString & a_Text, const AString & a_Style = ""); + } ; + + class cClientTranslatedPart : + public cBasePart + { + typedef cBasePart super; + public: + AStringVector m_Parameters; + + cClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style = ""); + } ; + + class cUrlPart : + public cBasePart + { + typedef cBasePart super; + public: + AString m_Url; + + cUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style = ""); + } ; + + class cCommandPart : + public cBasePart + { + typedef cBasePart super; + public: + AString m_Command; + + cCommandPart(ePartType a_PartType, const AString & a_Text, const AString & a_Command, const AString & a_Style = ""); + } ; + + class cRunCommandPart : + public cCommandPart + { + typedef cCommandPart super; + public: + cRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = ""); + } ; + + class cSuggestCommandPart : + public cCommandPart + { + typedef cCommandPart super; + public: + cSuggestCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = ""); + } ; + + typedef std::vector cParts; + + // tolua_begin + + /** Creates a new empty chat message */ + cCompositeChat(void); + + /** Creates a new chat message and parses the text into parts. + Recognizes "http:" and "https:" links and @color-codes. + Uses ParseText() for the actual parsing. */ + cCompositeChat(const AString & a_ParseText); + + ~cCompositeChat(); + + /** Removes all parts from the object. */ + void Clear(void); + + /** Adds a plain text part, with optional style. + The default style is plain white text. */ + void AddTextPart(const AString & a_Message, const AString & a_Style = ""); + + // tolua_end + + /** Adds a part that is translated client-side, with the formatting parameters and optional style. + Exported in ManualBindings due to AStringVector usage - Lua uses an array-table of strings. */ + void AddClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style = ""); + + // tolua_begin + + /** Adds a part that opens an URL when clicked. + The default style is underlined light blue text. */ + void AddUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style = "u@c"); + + /** Adds a part that runs a command when clicked. + The default style is underlined light green text. */ + void AddRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = "u@a"); + + /** Adds a part that suggests a command (enters it into the chat message area, but doesn't send) when clicked. + The default style is underlined yellow text. */ + void AddSuggestCommandPart(const AString & a_Text, const AString & a_SuggestedCommand, const AString & a_Style = "u@b"); + + /** Parses text into various parts, adds those. + Recognizes "http:" and "https:" URLs and @color-codes. */ + void ParseText(const AString & a_ParseText); + + /** Sets the message type, which is indicated by prefixes added to the message when serializing. */ + void SetMessageType(eMessageType a_MessageType); + + /** Returns the message type set previously by SetMessageType(). */ + eMessageType GetMessageType(void) const { return m_MessageType; } + + // tolua_end + + const cParts & GetParts(void) const { return m_Parts; } + +protected: + /** All the parts that */ + cParts m_Parts; + + /** The message type, as indicated by prefixes. */ + eMessageType m_MessageType; +} ; // tolua_export + + + + diff --git a/src/Defines.h b/src/Defines.h index 290f862ef..f33d1ae56 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -441,7 +441,10 @@ inline float GetSpecialSignf( float a_Val ) -enum ChatPrefixCodes + +// tolua_begin + +enum eMessageType { // http://forum.mc-server.org/showthread.php?tid=1212 // MessageType... @@ -458,7 +461,9 @@ enum ChatPrefixCodes mtLeave, // A player has left the server }; -// tolua_begin + + + /** Normalizes an angle in degrees to the [-180, +180) range: */ inline double NormalizeAngleDegrees(const double a_Degrees) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 7db9544cb..53e4b56db 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -203,6 +203,7 @@ public: void SendMessageWarning (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtWarning); } void SendMessageFatal (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtFailure); } void SendMessagePrivateMsg(const AString & a_Message, const AString & a_Sender) { m_ClientHandle->SendChat(a_Message, mtPrivateMessage, a_Sender); } + void SendMessage (const cCompositeChat & a_Message) { m_ClientHandle->SendChat(a_Message); } const AString & GetName(void) const { return m_PlayerName; } void SetName(const AString & a_Name) { m_PlayerName = a_Name; } diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 791082537..f5b9fd403 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -28,6 +28,7 @@ class cWorld; class cMonster; class cChunkDataSerializer; class cFallingBlock; +class cCompositeChat; @@ -58,6 +59,7 @@ public: virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0; virtual void SendChat (const AString & a_Message) = 0; + virtual void SendChat (const cCompositeChat & a_Message) = 0; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 73d21161c..7020699d1 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -32,6 +32,8 @@ Documentation: #include "../Mobs/IncludeAllMonsters.h" +#include "../CompositeChat.h" + @@ -233,6 +235,42 @@ void cProtocol125::SendChat(const AString & a_Message) +void cProtocol125::SendChat(const cCompositeChat & a_Message) +{ + // This version doesn't support composite messages, just extract each part's text and use it: + AString Msg; + const cCompositeChat::cParts & Parts = a_Message.GetParts(); + for (cCompositeChat::cParts::const_iterator itr = Parts.begin(), end = Parts.end(); itr != end; ++itr) + { + switch ((*itr)->m_PartType) + { + case cCompositeChat::ptText: + case cCompositeChat::ptClientTranslated: + case cCompositeChat::ptRunCommand: + case cCompositeChat::ptSuggestCommand: + { + Msg.append((*itr)->m_Text); + break; + } + case cCompositeChat::ptUrl: + { + Msg.append(((cCompositeChat::cUrlPart *)(*itr))->m_Url); + break; + } + } // switch (PartType) + } // for itr - Parts[] + + // Send the message: + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_CHAT); + WriteString(Msg); + Flush(); +} + + + + + void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { cCSLock Lock(m_CSPacket); diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index cd15ab518..1a3209333 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -33,6 +33,7 @@ public: virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; virtual void SendChat (const AString & a_Message) override; + virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 7eaf106cf..5aa18300c 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -8,6 +8,7 @@ Implements the 1.7.x protocol classes: */ #include "Globals.h" +#include "json/json.h" #include "Protocol17x.h" #include "ChunkDataSerializer.h" #include "../ClientHandle.h" @@ -25,6 +26,7 @@ Implements the 1.7.x protocol classes: #include "../Mobs/IncludeAllMonsters.h" #include "../UI/Window.h" #include "../BlockEntities/CommandBlockEntity.h" +#include "../CompositeChat.h" @@ -200,6 +202,78 @@ void cProtocol172::SendChat(const AString & a_Message) +void cProtocol172::SendChat(const cCompositeChat & a_Message) +{ + // Compose the complete Json string to send: + Json::Value msg; + msg["text"] = ""; // The client crashes without this + const cCompositeChat::cParts & Parts = a_Message.GetParts(); + for (cCompositeChat::cParts::const_iterator itr = Parts.begin(), end = Parts.end(); itr != end; ++itr) + { + Json::Value Part; + switch ((*itr)->m_PartType) + { + case cCompositeChat::ptText: + { + Part["text"] = (*itr)->m_Text; + AddChatPartStyle(Part, (*itr)->m_Style); + break; + } + + case cCompositeChat::ptClientTranslated: + { + const cCompositeChat::cClientTranslatedPart & p = (const cCompositeChat::cClientTranslatedPart &)**itr; + Part["translate"] = p.m_Text; + Json::Value With; + for (AStringVector::const_iterator itrW = p.m_Parameters.begin(), endW = p.m_Parameters.end(); itrW != endW; ++itr) + { + With.append(*itrW); + } + if (!p.m_Parameters.empty()) + { + Part["with"] = With; + } + AddChatPartStyle(Part, p.m_Style); + break; + } + + case cCompositeChat::ptUrl: + { + const cCompositeChat::cUrlPart & p = (const cCompositeChat::cUrlPart &)**itr; + Part["text"] = p.m_Text; + Json::Value Url; + Url["action"] = "open_url"; + Url["value"] = p.m_Url; + Part["clickEvent"] = Url; + AddChatPartStyle(Part, p.m_Style); + break; + } + + case cCompositeChat::ptSuggestCommand: + case cCompositeChat::ptRunCommand: + { + const cCompositeChat::cCommandPart & p = (const cCompositeChat::cCommandPart &)**itr; + Part["text"] = p.m_Text; + Json::Value Cmd; + Cmd["action"] = (p.m_PartType == cCompositeChat::ptRunCommand) ? "run_command" : "suggest_command"; + Cmd["value"] = p.m_Command; + Part["clickEvent"] = Cmd; + AddChatPartStyle(Part, p.m_Style); + break; + } + } + msg["extra"].append(Part); + } // for itr - Parts[] + + // Send the message to the client: + cPacketizer Pkt(*this, 0x02); + Pkt.WriteString(msg.toStyledString()); +} + + + + + void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { // Serialize first, before creating the Packetizer (the packetizer locks a CS) @@ -1979,6 +2053,85 @@ void cProtocol172::StartEncryption(const Byte * a_Key) +void cProtocol172::AddChatPartStyle(Json::Value & a_Value, const AString & a_PartStyle) +{ + size_t len = a_PartStyle.length(); + for (size_t i = 0; i < len; i++) + { + switch (a_PartStyle[i]) + { + case 'b': + { + // bold + a_Value["bold"] = Json::Value(true); + break; + } + + case 'i': + { + // italic + a_Value["italic"] = Json::Value(true); + break; + } + + case 'u': + { + // Underlined + a_Value["underlined"] = Json::Value(true); + break; + } + + case 's': + { + // strikethrough + a_Value["strikethrough"] = Json::Value(true); + break; + } + + case 'o': + { + // obfuscated + a_Value["obfuscated"] = Json::Value(true); + break; + } + + case '@': + { + // Color, specified by the next char: + i++; + if (i >= len) + { + // String too short, didn't contain a color + break; + } + switch (a_PartStyle[i]) + { + case '0': a_Value["color"] = Json::Value("black"); break; + case '1': a_Value["color"] = Json::Value("dark_blue"); break; + case '2': a_Value["color"] = Json::Value("dark_green"); break; + case '3': a_Value["color"] = Json::Value("dark_aqua"); break; + case '4': a_Value["color"] = Json::Value("dark_red"); break; + case '5': a_Value["color"] = Json::Value("dark_purple"); break; + case '6': a_Value["color"] = Json::Value("gold"); break; + case '7': a_Value["color"] = Json::Value("gray"); break; + case '8': a_Value["color"] = Json::Value("dark_gray"); break; + case '9': a_Value["color"] = Json::Value("blue"); break; + case 'a': a_Value["color"] = Json::Value("green"); break; + case 'b': a_Value["color"] = Json::Value("aqua"); break; + case 'c': a_Value["color"] = Json::Value("red"); break; + case 'd': a_Value["color"] = Json::Value("light_purple"); break; + case 'e': a_Value["color"] = Json::Value("yellow"); break; + case 'f': a_Value["color"] = Json::Value("white"); break; + } // switch (color) + } // case '@' + } // switch (Style[i]) + } // for i - a_PartStyle[] +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cProtocol172::cPacketizer: diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 6a75e41c8..d19be0f05 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -36,6 +36,16 @@ Declares the 1.7.x protocol classes: +// fwd: +namespace Json +{ + class Value; +} + + + + + class cProtocol172 : public cProtocol { @@ -45,16 +55,17 @@ public: cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); - /// Called when client sends some data: + /** Called when client sends some data: */ virtual void DataReceived(const char * a_Data, int a_Size) override; - /// Sending stuff to clients (alphabetically sorted): + /** Sending stuff to clients (alphabetically sorted): */ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; virtual void SendChat (const AString & a_Message) override; + virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; @@ -117,7 +128,7 @@ public: protected: - /// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed + /** Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed */ class cPacketizer { public: @@ -206,16 +217,16 @@ protected: AString m_AuthServerID; - /// State of the protocol. 1 = status, 2 = login, 3 = game + /** State of the protocol. 1 = status, 2 = login, 3 = game */ UInt32 m_State; - /// Buffer for the received data + /** Buffer for the received data */ cByteBuffer m_ReceivedData; - /// Buffer for composing the outgoing packets, through cPacketizer + /** Buffer for composing the outgoing packets, through cPacketizer */ cByteBuffer m_OutPacketBuffer; - /// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) + /** Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) */ cByteBuffer m_OutPacketLenBuffer; bool m_IsEncrypted; @@ -227,7 +238,7 @@ protected: cFile m_CommLogFile; - /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets + /** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */ void AddReceivedData(const char * a_Data, int a_Size); /** Reads and handles the packet. The packet length and type have already been read. @@ -268,21 +279,24 @@ protected: void HandlePacketWindowClose (cByteBuffer & a_ByteBuffer); - /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. + /** Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. */ void WritePacket(cByteBuffer & a_Packet); - /// Sends the data to the client, encrypting them if needed. + /** Sends the data to the client, encrypting them if needed. */ virtual void SendData(const char * a_Data, int a_Size) override; void SendCompass(const cWorld & a_World); - /// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data + /** Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data */ bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item); - /// Parses item metadata as read by ReadItem(), into the item enchantments. + /** Parses item metadata as read by ReadItem(), into the item enchantments. */ void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); void StartEncryption(const Byte * a_Key); + + /** Adds the chat part's style (represented by the part's stylestring) into the Json object. */ + void AddChatPartStyle(Json::Value & a_Value, const AString & a_PartStyle); } ; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 32409c2aa..6e51ee9cd 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -159,6 +159,16 @@ void cProtocolRecognizer::SendChat(const AString & a_Message) +void cProtocolRecognizer::SendChat(const cCompositeChat & a_Message) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChat(a_Message); +} + + + + + void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { ASSERT(m_Protocol != NULL); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index f58c66d10..800163be6 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -68,6 +68,7 @@ public: virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; virtual void SendChat (const AString & a_Message) override; + virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; diff --git a/src/Root.cpp b/src/Root.cpp index 749fbd288..415a26dd3 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -543,7 +543,7 @@ void cRoot::ReloadGroups(void) -void cRoot::LoopWorldsAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix) +void cRoot::LoopWorldsAndBroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix) { for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) { @@ -555,6 +555,18 @@ void cRoot::LoopWorldsAndBroadcastChat(const AString & a_Message, ChatPrefixCode +void cRoot::BroadcastChat(const cCompositeChat & a_Message) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) + { + itr->second->BroadcastChat(a_Message); + } // for itr - m_WorldsByName[] +} + + + + + bool cRoot::ForEachPlayer(cPlayerListCallback & a_Callback) { for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) diff --git a/src/Root.h b/src/Root.h index 13e208b8d..a1a3ca6a2 100644 --- a/src/Root.h +++ b/src/Root.h @@ -20,7 +20,8 @@ class cPluginManager; class cServer; class cWorld; class cPlayer; -class cCommandOutputCallback ; +class cCommandOutputCallback; +class cCompositeChat; typedef cItemCallback cPlayerListCallback; typedef cItemCallback cWorldListCallback; @@ -108,7 +109,7 @@ public: /// Finds a player from a partial or complete player name and calls the callback - case-insensitive bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - void LoopWorldsAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix); + void LoopWorldsAndBroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix); void BroadcastChatJoin (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtJoin); } void BroadcastChatLeave (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtLeave); } void BroadcastChatDeath (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtDeath); } @@ -122,6 +123,7 @@ public: void BroadcastChatSuccess(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtSuccess); } void BroadcastChatWarning(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtWarning); } void BroadcastChatFatal (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtFailure); } + void BroadcastChat (const cCompositeChat & a_Message); /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); diff --git a/src/World.cpp b/src/World.cpp index cb07caa5d..d1a13794c 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1747,7 +1747,7 @@ void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cons -void cWorld::LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const cClientHandle * a_Exclude) +void cWorld::LoopPlayersAndBroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSPlayers); for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) @@ -1765,6 +1765,24 @@ void cWorld::LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCo +void cWorld::BroadcastChat(const cCompositeChat & a_Message, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendChat(a_Message); + } +} + + + + + void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) { m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); diff --git a/src/World.h b/src/World.h index ca1b7dcc5..2fbc51038 100644 --- a/src/World.h +++ b/src/World.h @@ -46,6 +46,7 @@ class cDispenserEntity; class cFurnaceEntity; class cNoteEntity; class cMobCensus; +class cCompositeChat; typedef std::list< cPlayer * > cPlayerList; @@ -167,7 +168,7 @@ public: void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude - void LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const cClientHandle * a_Exclude = NULL); + void LoopPlayersAndBroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix, const cClientHandle * a_Exclude = NULL); void BroadcastChatDeath (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtDeath, a_Exclude); } // tolua_begin @@ -177,6 +178,7 @@ public: void BroadcastChatSuccess(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtSuccess, a_Exclude); } void BroadcastChatWarning(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtWarning, a_Exclude); } void BroadcastChatFatal (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtFailure, a_Exclude); } + void BroadcastChat (const cCompositeChat & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_end void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL);