Chat protocol rewrite (#5117)
authorLoïc Blot <nerzhul@users.noreply.github.com>
Sun, 16 Jul 2017 08:47:31 +0000 (10:47 +0200)
committerGitHub <noreply@github.com>
Sun, 16 Jul 2017 08:47:31 +0000 (10:47 +0200)
* New TOCLIENT_CHAT_MESSAGE packet

* Rename old packet to TOCLIENT_CHAT_MESSAGE_OLD for compat
* Handle TOCLIENT_CHAT_MESSAGE new structure client side
* Client chat queue should use a specific object
* SendChatMessage: use the right packet depending on protocol version (not complete yet)
* Add chatmessage(type) objects and handle them client side (partially)
* Use ChatMessage instead of std::wstring server side

* Update with timestamp support

16 files changed:
src/chatmessage.h [new file with mode: 0644]
src/client.cpp
src/client.h
src/clientiface.cpp
src/clientiface.h
src/game.cpp
src/network/clientopcodes.cpp
src/network/clientpackethandler.cpp
src/network/networkpacket.cpp
src/network/networkpacket.h
src/network/networkprotocol.h
src/network/serveropcodes.cpp
src/network/serverpackethandler.cpp
src/script/lua_api/l_client.cpp
src/server.cpp
src/server.h

diff --git a/src/chatmessage.h b/src/chatmessage.h
new file mode 100644 (file)
index 0000000..3ef8550
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+Minetest
+Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MT_CHATMESSAGE_H
+#define MT_CHATMESSAGE_H
+
+#include <string>
+#include <ctime>
+
+enum ChatMessageType
+{
+       CHATMESSAGE_TYPE_RAW = 0,
+       CHATMESSAGE_TYPE_NORMAL = 1,
+       CHATMESSAGE_TYPE_ANNOUNCE = 2,
+       CHATMESSAGE_TYPE_SYSTEM = 3,
+       CHATMESSAGE_TYPE_MAX = 4,
+};
+
+struct ChatMessage
+{
+       ChatMessage(const std::wstring &m = L"") : message(m) {}
+
+       ChatMessage(ChatMessageType t, const std::wstring &m, const std::wstring &s = L"",
+                       std::time_t ts = std::time(0))
+           : type(t), message(m), sender(s), timestamp(ts)
+       {
+       }
+
+       ChatMessageType type = CHATMESSAGE_TYPE_RAW;
+       std::wstring message = L"";
+       std::wstring sender = L"";
+       std::time_t timestamp = std::time(0);
+};
+
+#endif
index 0f689a7145971eb08797caf45a5a26cd391bce5b..443059c36a1c7bb01a55256522b4ba7098e23cbe 100644 (file)
@@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "guiscalingfilter.h"
 #include "script/scripting_client.h"
 #include "game.h"
+#include "chatmessage.h"
 
 extern gui::IGUIEnvironment* guienv;
 
@@ -1518,12 +1519,34 @@ u16 Client::getHP()
        return player->hp;
 }
 
-bool Client::getChatMessage(std::wstring &message)
+bool Client::getChatMessage(std::wstring &res)
 {
-       if(m_chat_queue.size() == 0)
+       if (m_chat_queue.empty())
                return false;
-       message = m_chat_queue.front();
+
+       ChatMessage *chatMessage = m_chat_queue.front();
        m_chat_queue.pop();
+
+       res = L"";
+
+       switch (chatMessage->type) {
+               case CHATMESSAGE_TYPE_RAW:
+               case CHATMESSAGE_TYPE_ANNOUNCE:
+               case CHATMESSAGE_TYPE_SYSTEM:
+                       res = chatMessage->message;
+                       break;
+               case CHATMESSAGE_TYPE_NORMAL: {
+                       if (!chatMessage->sender.empty())
+                               res = L"<" + chatMessage->sender + L"> " + chatMessage->message;
+                       else
+                               res = chatMessage->message;
+                       break;
+               }
+               default:
+                       break;
+       }
+
+       delete chatMessage;
        return true;
 }
 
@@ -1542,14 +1565,13 @@ void Client::typeChatMessage(const std::wstring &message)
        sendChatMessage(message);
 
        // Show locally
-       if (message[0] != L'/')
-       {
+       if (message[0] != L'/') {
                // compatibility code
                if (m_proto_ver < 29) {
                        LocalPlayer *player = m_env.getLocalPlayer();
                        assert(player);
                        std::wstring name = narrow_to_wide(player->getName());
-                       pushToChatQueue((std::wstring)L"<" + name + L"> " + message);
+                       pushToChatQueue(new ChatMessage(CHATMESSAGE_TYPE_NORMAL, message, name));
                }
        }
 }
@@ -1806,7 +1828,8 @@ void Client::makeScreenshot()
                        } else {
                                sstr << "Failed to save screenshot '" << filename << "'";
                        }
-                       pushToChatQueue(narrow_to_wide(sstr.str()));
+                       pushToChatQueue(new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                                       narrow_to_wide(sstr.str())));
                        infostream << sstr.str() << std::endl;
                        image->drop();
                }
index 0255b2803337f2c361edd41e7b03db2febe6eb94..0f0cd8a5eed343e895c5fc1bd480ed4e9f2eb2c0 100644 (file)
@@ -43,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f
 
 struct MeshMakeData;
+struct ChatMessage;
 class MapBlockMesh;
 class IWritableTextureSource;
 class IWritableShaderSource;
@@ -328,7 +329,8 @@ public:
        void handleCommand_BlockData(NetworkPacket* pkt);
        void handleCommand_Inventory(NetworkPacket* pkt);
        void handleCommand_TimeOfDay(NetworkPacket* pkt);
-       void handleCommand_ChatMessage(NetworkPacket* pkt);
+       void handleCommand_ChatMessageOld(NetworkPacket *pkt);
+       void handleCommand_ChatMessage(NetworkPacket *pkt);
        void handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt);
        void handleCommand_ActiveObjectMessages(NetworkPacket* pkt);
        void handleCommand_Movement(NetworkPacket* pkt);
@@ -520,9 +522,9 @@ public:
 
        void makeScreenshot();
 
-       inline void pushToChatQueue(const std::wstring &input)
+       inline void pushToChatQueue(ChatMessage *cec)
        {
-               m_chat_queue.push(input);
+               m_chat_queue.push(cec);
        }
 
        ClientScripting *getScript() { return m_script; }
@@ -629,10 +631,10 @@ private:
        // 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT
        //s32 m_daynight_i;
        //u32 m_daynight_ratio;
-       std::queue<std::wstring> m_chat_queue;
        std::queue<std::wstring> m_out_chat_queue;
        u32 m_last_chat_message_sent;
        float m_chat_message_allowance = 5.0f;
+       std::queue<ChatMessage *> m_chat_queue;
 
        // The authentication methods we can use to enter sudo mode (=change password)
        u32 m_sudo_auth_methods;
index a629ccee7b18df1424cc1ec6396bf73b14c73cc4..361315c3bcb6411456e61afd202a61d1c17a84cc 100644 (file)
@@ -693,6 +693,32 @@ void ClientInterface::sendToAll(NetworkPacket *pkt)
        }
 }
 
+void ClientInterface::sendToAllCompat(NetworkPacket *pkt, NetworkPacket *legacypkt,
+               u16 min_proto_ver)
+{
+       MutexAutoLock clientslock(m_clients_mutex);
+       for (std::unordered_map<u16, RemoteClient*>::iterator i = m_clients.begin();
+                       i != m_clients.end(); ++i) {
+               RemoteClient *client = i->second;
+               NetworkPacket *pkt_to_send = nullptr;
+
+               if (client->net_proto_version >= min_proto_ver) {
+                       pkt_to_send = pkt;
+               } else if (client->net_proto_version != 0) {
+                       pkt_to_send = legacypkt;
+               } else {
+                       warningstream << "Client with unhandled version to handle: '"
+                               << client->net_proto_version << "'";
+                       continue;
+               }
+
+               m_con->Send(client->peer_id,
+                       clientCommandFactoryTable[pkt_to_send->getCommand()].channel,
+                       pkt_to_send,
+                       clientCommandFactoryTable[pkt_to_send->getCommand()].reliable);
+       }
+}
+
 RemoteClient* ClientInterface::getClientNoEx(u16 peer_id, ClientState state_min)
 {
        MutexAutoLock clientslock(m_clients_mutex);
index 09855a3725327ff9fc9b00bc1e051e89b4752e88..ce74cfeced0ee42b4fa64278f5d0312dde9f5ccd 100644 (file)
@@ -434,6 +434,7 @@ public:
 
        /* send to all clients */
        void sendToAll(NetworkPacket *pkt);
+       void sendToAllCompat(NetworkPacket *pkt, NetworkPacket *legacypkt, u16 min_proto_ver);
 
        /* delete a client */
        void DeleteClient(u16 peer_id);
index 88ae9c2b766c6a3c29bb7328190dcd9b675bcac4..639cc29c39c343de6874785c29aa4016501d46cb 100644 (file)
@@ -976,8 +976,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
        }
 
        // Get new messages from client
-       std::wstring message;
-
+       std::wstring message = L"";
        while (client.getChatMessage(message)) {
                chat_backend.addUnparsedMessage(message);
        }
index cb504b373a9e62e9ba8ef5f90cdbf7b68723addd..f39dd6db642fcab5a554658d9e258ef31ec816c6 100644 (file)
@@ -71,8 +71,8 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
        null_command_handler,
        null_command_handler,
        null_command_handler,
-       null_command_handler,
-       { "TOCLIENT_CHAT_MESSAGE",             TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x30
+       { "TOCLIENT_CHAT_MESSAGE",             TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x2F
+       { "TOCLIENT_CHAT_MESSAGE_OLD",         TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessageOld }, // 0x30
        { "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectRemoveAdd }, // 0x31
        { "TOCLIENT_ACTIVE_OBJECT_MESSAGES",   TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectMessages }, // 0x32
        { "TOCLIENT_HP",                       TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HP }, // 0x33
index e6f0d7092fbfacf5d4df6f9a626082c99843f794..bb4db6f47b26ab607df539a7aabde0f647f93033 100644 (file)
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "client.h"
 
 #include "util/base64.h"
+#include "chatmessage.h"
 #include "clientmedia.h"
 #include "log.h"
 #include "map.h"
@@ -142,7 +143,9 @@ void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
 }
 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
 {
-       pushToChatQueue(L"Password change denied. Password NOT changed.");
+       ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                       L"Password change denied. Password NOT changed.");
+       pushToChatQueue(chatMessage);
        // reset everything and be sad
        deleteAuthData();
 }
@@ -395,7 +398,7 @@ void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
                        << " dr=" << dr << std::endl;
 }
 
-void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
+void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
 {
        /*
                u16 command
@@ -413,8 +416,43 @@ void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
        }
 
        // If chat message not consummed by client lua API
+       // @TODO send this to CSM using ChatMessage object
        if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
-               pushToChatQueue(message);
+               pushToChatQueue(new ChatMessage(message));
+       }
+}
+
+void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
+{
+       /*
+               u8 version
+               u8 message_type
+               u16 sendername length
+               wstring sendername
+               u16 length
+               wstring message
+        */
+
+       ChatMessage *chatMessage = new ChatMessage();
+       u8 version, message_type;
+       *pkt >> version >> message_type;
+
+       if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
+               delete chatMessage;
+               return;
+       }
+
+       *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
+
+       chatMessage->type = (ChatMessageType) message_type;
+
+       // @TODO send this to CSM using ChatMessage object
+       if (!moddingEnabled() || !m_script->on_receiving_message(
+                       wide_to_utf8(chatMessage->message))) {
+               pushToChatQueue(chatMessage);
+       } else {
+               // Message was consumed by CSM and should not handled by client, destroying
+               delete chatMessage;
        }
 }
 
index 48cf3a374b64058015e89a144afb657f242b9d09..78c73d253183652e80f3f0a710c6de8b5470a37d 100644 (file)
@@ -275,6 +275,12 @@ NetworkPacket& NetworkPacket::operator<<(u64 src)
        return *this;
 }
 
+NetworkPacket& NetworkPacket::operator<<(std::time_t src)
+{
+       *this << (u64) src;
+       return *this;
+}
+
 NetworkPacket& NetworkPacket::operator<<(float src)
 {
        checkDataSize(4);
@@ -360,6 +366,16 @@ NetworkPacket& NetworkPacket::operator>>(u64& dst)
        return *this;
 }
 
+NetworkPacket& NetworkPacket::operator>>(std::time_t& dst)
+{
+       checkReadOffset(m_read_offset, 8);
+
+       dst = readU64(&m_data[m_read_offset]);
+
+       m_read_offset += 8;
+       return *this;
+}
+
 NetworkPacket& NetworkPacket::operator>>(float& dst)
 {
        checkReadOffset(m_read_offset, 4);
index 3058a22ca22a5e197e4dfb808625e4e982e71403..7eb8cc3ff4d6961c4836611ae8647d5143de0bea 100644 (file)
@@ -84,6 +84,9 @@ public:
                NetworkPacket& operator>>(u64& dst);
                NetworkPacket& operator<<(u64 src);
 
+               NetworkPacket& operator>>(std::time_t& dst);
+               NetworkPacket& operator<<(std::time_t src);
+
                NetworkPacket& operator>>(float& dst);
                NetworkPacket& operator<<(float src);
 
index 8304d3003c589de6cfc935fbf49d7c851ead09f6..7593348397ecf597c2564861779a6b859f063e94 100644 (file)
@@ -160,9 +160,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
                        instead of guessing based on the active object list.
        PROTOCOL VERSION 34:
                Add sound pitch
+       PROTOCOL VERSION 35:
+               Rename TOCLIENT_CHAT_MESSAGE to TOCLIENT_CHAT_MESSAGE_OLD (0x30)
+               Add TOCLIENT_CHAT_MESSAGE (0x2F)
+                       This chat message is a signalisation message containing various informations:
+                       * timestamp
+                       * sender
+                       * type (RAW, NORMAL, ANNOUNCE, SYSTEM)
+                       * content
 */
 
-#define LATEST_PROTOCOL_VERSION 34
+#define LATEST_PROTOCOL_VERSION 35
 
 // Server's supported network protocol range
 #define SERVER_PROTOCOL_VERSION_MIN 24
@@ -307,7 +315,17 @@ enum ToClientCommand
 
        // (oops, there is some gap here)
 
-       TOCLIENT_CHAT_MESSAGE = 0x30,
+       TOCLIENT_CHAT_MESSAGE = 0x2F,
+       /*
+               u8 version
+               u8 message_type
+               u16 sendername length
+               wstring sendername
+               u16 length
+               wstring message
+       */
+
+       TOCLIENT_CHAT_MESSAGE_OLD = 0x30, // Deprecated by proto v35
        /*
                u16 length
                wstring message
index 3f9706d6aba3fb921b37d4c28fbe751a1220e6e6..208214369c70ff1e79d9084e35d840a3236c9de7 100644 (file)
@@ -160,8 +160,8 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
        null_command_factory,
        null_command_factory,
        null_command_factory,
-       null_command_factory,
-       { "TOCLIENT_CHAT_MESSAGE",             0, true }, // 0x30
+       { "TOCLIENT_CHAT_MESSAGE",             0, true }, // 0x2F
+       { "TOCLIENT_CHAT_MESSAGE_OLD",         0, true }, // 0x30
        { "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", 0, true }, // 0x31
        { "TOCLIENT_ACTIVE_OBJECT_MESSAGES",   0, true }, // 0x32 Special packet, sent by 0 (rel) and 1 (unrel) channel
        { "TOCLIENT_HP",                       0, true }, // 0x33
index a2882b2e70198eec089eb3c910750c10d6722166..b87daba6bb01894ddc2ef8d1ef04767cefa57d6e 100644 (file)
@@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include <chatmessage.h>
 #include "server.h"
 #include "log.h"
 
@@ -644,8 +645,10 @@ void Server::handleCommand_Init2(NetworkPacket* pkt)
 
        // Warnings about protocol version can be issued here
        if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) {
-               SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S "
-                               L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
+               SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                               L"# Server: WARNING: YOUR CLIENT'S VERSION MAY NOT BE FULLY COMPATIBLE "
+                               L"WITH THIS SERVER!"));
+
        }
 }
 
@@ -1077,11 +1080,11 @@ void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
        std::string name = player->getName();
        std::wstring wname = narrow_to_wide(name);
 
-       std::wstring answer_to_sender = handleChat(name, wname, message,
-               true, dynamic_cast<RemotePlayer *>(player));
+       std::wstring answer_to_sender = handleChat(name, wname, message, true, player);
        if (!answer_to_sender.empty()) {
                // Send the answer to sender
-               SendChatMessage(pkt->getPeerId(), answer_to_sender);
+               SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_NORMAL,
+                               answer_to_sender, wname));
        }
 }
 
@@ -1171,7 +1174,8 @@ void Server::handleCommand_Password(NetworkPacket* pkt)
                infostream<<"Server: " << player->getName() <<
                                " supplied invalid password hash" << std::endl;
                // Wrong old password supplied!!
-               SendChatMessage(pkt->getPeerId(), L"Invalid new password hash supplied. Password NOT changed.");
+               SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                               L"Invalid new password hash supplied. Password NOT changed."));
                return;
        }
 
@@ -1186,18 +1190,21 @@ void Server::handleCommand_Password(NetworkPacket* pkt)
        if (oldpwd != checkpwd) {
                infostream << "Server: invalid old password" << std::endl;
                // Wrong old password supplied!!
-               SendChatMessage(pkt->getPeerId(), L"Invalid old password supplied. Password NOT changed.");
+               SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                               L"Invalid old password supplied. Password NOT changed."));
                return;
        }
 
        bool success = m_script->setPassword(playername, newpwd);
        if (success) {
                actionstream << player->getName() << " changes password" << std::endl;
-               SendChatMessage(pkt->getPeerId(), L"Password change successful.");
+               SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                               L"Password change successful."));
        } else {
                actionstream << player->getName() << " tries to change password but "
                                << "it fails" << std::endl;
-               SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable.");
+               SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                               L"Password change failed or unavailable."));
        }
 }
 
@@ -1853,11 +1860,13 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
                bool success = m_script->setPassword(playername, pw_db_field);
                if (success) {
                        actionstream << playername << " changes password" << std::endl;
-                       SendChatMessage(pkt->getPeerId(), L"Password change successful.");
+                       SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                                       L"Password change successful."));
                } else {
                        actionstream << playername << " tries to change password but "
                                << "it fails" << std::endl;
-                       SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable.");
+                       SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                                       L"Password change failed or unavailable."));
                }
        }
 }
index 1077d5f2d86568ccc782b58865abd038bfd1e4a7..e7d75cce79b8134c1501193e7198e2ec3e74e69f 100644 (file)
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "l_client.h"
+#include "chatmessage.h"
 #include "clientenvironment.h"
 #include "common/c_content.h"
 #include "common/c_converter.h"
@@ -80,7 +81,7 @@ int ModApiClient::l_display_chat_message(lua_State *L)
                return 0;
 
        std::string message = luaL_checkstring(L, 1);
-       getClient(L)->pushToChatQueue(utf8_to_wide(message));
+       getClient(L)->pushToChatQueue(new ChatMessage(utf8_to_wide(message)));
        lua_pushboolean(L, true);
        return 1;
 }
index 95d2371ff1153591957ab906f54f3edfa4e441ff..33384816c34d8632e454b78d76ab86e07d52be82 100644 (file)
@@ -60,6 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/sha1.h"
 #include "util/hex.h"
 #include "database.h"
+#include "chatmessage.h"
 
 class ClientNotFoundException : public BaseException
 {
@@ -304,7 +305,8 @@ Server::~Server()
        infostream<<"Server destructing"<<std::endl;
 
        // Send shutdown message
-       SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
+       SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
+                       L"*** Server shutting down"));
 
        {
                MutexAutoLock envlock(m_env_mutex);
@@ -1107,7 +1109,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
        // Note things in chat if not in simple singleplayer mode
        if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
                // Send information about server to player in chat
-               SendChatMessage(peer_id, getStatusString());
+               SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
        }
        Address addr = getPeerAddress(player->peer_id);
        std::string ip_str = addr.serializeString();
@@ -1615,21 +1617,29 @@ void Server::SendInventory(PlayerSAO* playerSAO)
        Send(&pkt);
 }
 
-void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
+void Server::SendChatMessage(u16 peer_id, const ChatMessage &message)
 {
        DSTACK(FUNCTION_NAME);
+
+       NetworkPacket legacypkt(TOCLIENT_CHAT_MESSAGE_OLD, 0, peer_id);
+       legacypkt << message.message;
+
+       NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
+       u8 version = 1;
+       u8 type = message.type;
+       pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
+
        if (peer_id != PEER_ID_INEXISTENT) {
-               NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
+               RemotePlayer *player = m_env->getPlayer(peer_id);
+               if (!player)
+                       return;
 
-               if (m_clients.getProtocolVersion(peer_id) < 27)
-                       pkt << unescape_enriched(message);
+               if (player->protocol_version < 35)
+                       Send(&legacypkt);
                else
-                       pkt << message;
-
-               Send(&pkt);
+                       Send(&pkt);
        } else {
-               for (u16 id : m_clients.getClientIDs())
-                       SendChatMessage(id, message);
+               m_clients.sendToAllCompat(&pkt, &legacypkt, 35);
        }
 }
 
@@ -2811,8 +2821,10 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
        }
 
        // Send leave chat message to all remaining clients
-       if(message.length() != 0)
-               SendChatMessage(PEER_ID_INEXISTENT,message);
+       if (!message.empty()) {
+               SendChatMessage(PEER_ID_INEXISTENT,
+                               ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
+       }
 }
 
 void Server::UpdateCrafting(RemotePlayer *player)
@@ -2934,7 +2946,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
                for (u16 i = 0; i < clients.size(); i++) {
                        u16 cid = clients[i];
                        if (cid != peer_id_to_avoid_sending)
-                               SendChatMessage(cid, line);
+                               SendChatMessage(cid, ChatMessage(line));
                }
        }
        return L"";
@@ -3096,7 +3108,7 @@ void Server::notifyPlayer(const char *name, const std::wstring &msg)
        if (player->peer_id == PEER_ID_INEXISTENT)
                return;
 
-       SendChatMessage(player->peer_id, msg);
+       SendChatMessage(player->peer_id, ChatMessage(msg));
 }
 
 bool Server::showFormspec(const char *playername, const std::string &formspec,
@@ -3271,7 +3283,7 @@ bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
 
 void Server::notifyPlayers(const std::wstring &msg)
 {
-       SendChatMessage(PEER_ID_INEXISTENT,msg);
+       SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
 }
 
 void Server::spawnParticle(const std::string &playername, v3f pos,
index b60482a75b3bcce518aa858f0d9fc729b405aa85..112a9486371a86491a269ddc89dc1b1555c7cb7e 100644 (file)
@@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "clientiface.h"
 #include "remoteplayer.h"
 #include "network/networkpacket.h"
+#include "chatmessage.h"
 #include <string>
 #include <list>
 #include <map>
@@ -388,7 +389,7 @@ private:
        void SetBlocksNotSent(std::map<v3s16, MapBlock *>& block);
 
 
-       void SendChatMessage(u16 peer_id, const std::wstring &message);
+       void SendChatMessage(u16 peer_id, const ChatMessage &message);
        void SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed);
        void SendPlayerHP(u16 peer_id);