--- /dev/null
+/*
+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
#include "guiscalingfilter.h"
#include "script/scripting_client.h"
#include "game.h"
+#include "chatmessage.h"
extern gui::IGUIEnvironment* guienv;
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;
}
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));
}
}
}
} 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();
}
#define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f
struct MeshMakeData;
+struct ChatMessage;
class MapBlockMesh;
class IWritableTextureSource;
class IWritableShaderSource;
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);
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; }
// 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;
}
}
+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);
/* 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);
}
// Get new messages from client
- std::wstring message;
-
+ std::wstring message = L"";
while (client.getChatMessage(message)) {
chat_backend.addUnparsedMessage(message);
}
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
#include "client.h"
#include "util/base64.h"
+#include "chatmessage.h"
#include "clientmedia.h"
#include "log.h"
#include "map.h"
}
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();
}
<< " dr=" << dr << std::endl;
}
-void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
+void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
{
/*
u16 command
}
// 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;
}
}
return *this;
}
+NetworkPacket& NetworkPacket::operator<<(std::time_t src)
+{
+ *this << (u64) src;
+ return *this;
+}
+
NetworkPacket& NetworkPacket::operator<<(float src)
{
checkDataSize(4);
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);
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);
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
// (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
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
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <chatmessage.h>
#include "server.h"
#include "log.h"
// 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!"));
+
}
}
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));
}
}
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;
}
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."));
}
}
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."));
}
}
}
*/
#include "l_client.h"
+#include "chatmessage.h"
#include "clientenvironment.h"
#include "common/c_content.h"
#include "common/c_converter.h"
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;
}
#include "util/sha1.h"
#include "util/hex.h"
#include "database.h"
+#include "chatmessage.h"
class ClientNotFoundException : public BaseException
{
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);
// 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();
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);
}
}
}
// 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)
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"";
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,
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,
#include "clientiface.h"
#include "remoteplayer.h"
#include "network/networkpacket.h"
+#include "chatmessage.h"
#include <string>
#include <list>
#include <map>
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);