ManaPlus
chatrecv.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2004-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "net/tmwa/chatrecv.h"
25 
26 #include "actormanager.h"
27 #include "configuration.h"
28 
29 #include "being/localplayer.h"
30 #include "being/playerrelation.h"
31 #include "being/playerrelations.h"
32 
33 #include "const/gui/chat.h"
34 
36 
37 #include "gui/windows/chatwindow.h"
38 #include "gui/windows/shopwindow.h"
39 
40 #include "net/serverfeatures.h"
41 
42 #include "net/ea/chatrecv.h"
43 
44 #include "net/messagein.h"
45 
46 #include "net/tmwa/guildmanager.h"
47 
48 #include "utils/gettext.h"
49 
50 #include "debug.h"
51 
52 namespace TmwAthena
53 {
54 
55 namespace ChatRecv
56 {
57  std::string mShopRequestName;
58 } // namespace ChatRecv
59 
61 {
62  BLOCK_START("ChatRecv::processChat")
63  const int chatMsgLength = msg.readInt16("len") - 4;
64  if (chatMsgLength <= 0)
65  {
66  BLOCK_END("ChatRecv::processChat")
67  return;
68  }
69 
70  processChatContinue(msg.readRawString(chatMsgLength, "message"));
71 }
72 
73 void ChatRecv::processChatContinue(std::string chatMsg)
74 {
75  const size_t pos = chatMsg.find(" : ", 0);
76 
77  bool allow(true);
78  if (chatWindow != nullptr)
79  {
80  allow = chatWindow->resortChatLog(chatMsg,
85  }
86 
87  const std::string senseStr("You sense the following: ");
88  if ((actorManager != nullptr) && (chatMsg.find(senseStr) == 0U))
89  {
91  chatMsg.substr(senseStr.size()));
92  }
93 
94  if (pos == std::string::npos &&
97  {
98  // skip motd from "new" tmw server
99  if (Ea::ChatRecv::mMotdTime == 0)
100  {
102  }
103  else if (Ea::ChatRecv::mMotdTime == cur_time ||
105  {
106  Ea::ChatRecv::mSkipping = false;
107  }
108  BLOCK_END("ChatRecv::processChat")
109  return;
110  }
111 
112  if (pos != std::string::npos)
113  chatMsg.erase(0, pos + 3);
114 
115  trim(chatMsg);
116 
117  if (localPlayer != nullptr)
118  {
119  if ((chatWindow != nullptr || Ea::ChatRecv::mShowMotd) && allow)
120  localPlayer->setSpeech(chatMsg);
121  }
122  BLOCK_END("ChatRecv::processChat")
123 }
124 
126 {
127  BLOCK_START("ChatRecv::processChat")
128  const int chatMsgLength = msg.readInt16("len") - 4;
129  if (chatMsgLength <= 0)
130  {
131  BLOCK_END("ChatRecv::processChat")
132  return;
133  }
134 
135  if (localChatTab != nullptr &&
136  chatWindow != nullptr)
137  {
138  std::string chatMsg = msg.readRawString(chatMsgLength, "message");
139  chatWindow->addGlobalMessage(chatMsg);
140  }
141  else
142  {
143  msg.readRawString(chatMsgLength, "message");
144  }
145  BLOCK_END("ChatRecv::processChat")
146 }
147 
149 {
150  BLOCK_START("ChatRecv::processWhisper")
151  const int chatMsgLength = msg.readInt16("len") - 28;
152  std::string nick = msg.readString(24, "nick");
153 
154  if (chatMsgLength <= 0)
155  {
156  BLOCK_END("ChatRecv::processWhisper")
157  return;
158  }
159 
160  processWhisperContinue(nick, msg.readString(chatMsgLength, "message"));
161 }
162 
164 {
165  BLOCK_START("ChatRecv::processWhisperResponse")
166 
167  const uint8_t type = msg.readUInt8("response");
169 }
170 
171 void ChatRecv::processWhisperContinue(const std::string &nick,
172  std::string chatMsg)
173 {
174  // ignoring future whisper messages
175  if (chatMsg.find("\302\202G") == 0 || chatMsg.find("\302\202A") == 0)
176  {
177  BLOCK_END("ChatRecv::processWhisper")
178  return;
179  }
180  // remove first unicode space if this is may be whisper command.
181  if (chatMsg.find("\302\202!") == 0)
182  chatMsg = chatMsg.substr(2);
183 
184  if (nick != "Server")
185  {
186  if ((guildManager != nullptr) && GuildManager::getEnableGuildBot()
187  && nick == "guild" && guildManager->processGuildMessage(chatMsg))
188  {
189  BLOCK_END("ChatRecv::processWhisper")
190  return;
191  }
192 
194  {
195  const bool tradeBot = config.getBoolValue("tradebot");
196  const bool showMsg = !config.getBoolValue("hideShopMessages");
198  {
199  if (shopWindow != nullptr)
200  { // commands to shop from player
201  if (chatMsg.find("!selllist ") == 0)
202  {
203  if (tradeBot)
204  {
205  if (showMsg && (chatWindow != nullptr))
206  {
207  chatWindow->addWhisper(nick,
208  chatMsg,
210  }
212  }
213  }
214  else if (chatMsg.find("!buylist ") == 0)
215  {
216  if (tradeBot)
217  {
218  if (showMsg && chatWindow != nullptr)
219  {
220  chatWindow->addWhisper(nick,
221  chatMsg,
223  }
225  }
226  }
227  else if (chatMsg.find("!buyitem ") == 0)
228  {
229  if (showMsg && chatWindow != nullptr)
230  {
231  chatWindow->addWhisper(nick,
232  chatMsg,
234  }
235  if (tradeBot)
236  {
237  shopWindow->processRequest(nick, chatMsg,
239  }
240  }
241  else if (chatMsg.find("!sellitem ") == 0)
242  {
243  if (showMsg && chatWindow != nullptr)
244  {
245  chatWindow->addWhisper(nick,
246  chatMsg,
248  }
249  if (tradeBot)
250  {
251  shopWindow->processRequest(nick, chatMsg,
253  }
254  }
255  else if (chatMsg.length() > 3
256  && chatMsg.find("\302\202") == 0)
257  {
258  chatMsg = chatMsg.erase(0, 2);
259  if (mShopRequestName != nick)
260  {
262  // TRANSLATORS: message about spam player
263  _("Detected spam from: %s"),
264  nick.c_str()))
265  BLOCK_END("ChatRecv::processWhisper")
266  return;
267  }
268  mShopRequestName.clear();
269  if (showMsg && chatWindow != nullptr)
270  {
271  chatWindow->addWhisper(nick,
272  chatMsg,
274  }
275  if (chatMsg.find("B1") == 0 || chatMsg.find("S1") == 0)
276  ShopWindow::showList(nick, chatMsg);
277  }
278  else if (chatWindow != nullptr)
279  {
280  chatWindow->addWhisper(nick,
281  chatMsg,
283  }
284  }
285  else if (chatWindow != nullptr)
286  {
287  chatWindow->addWhisper(nick,
288  chatMsg,
290  }
291  }
292  else
293  {
294  if (chatWindow != nullptr &&
295  (showMsg ||
296  (chatMsg.find("!selllist") != 0 &&
297  chatMsg.find("!buylist") != 0)))
298  {
299  chatWindow->addWhisper(nick,
300  chatMsg,
302  }
303  }
304  }
305  }
306  else if (localChatTab != nullptr)
307  {
308  if ((gmChatTab != nullptr) && strStartWith(chatMsg, "[GM] "))
309  {
310  chatMsg = chatMsg.substr(5);
311  const size_t pos = chatMsg.find(": ", 0);
312  if (pos == std::string::npos)
313  {
314  gmChatTab->chatLog(chatMsg,
318  }
319  else
320  {
321  gmChatTab->chatLog(chatMsg.substr(0, pos),
322  chatMsg.substr(pos + 2));
323  }
324  }
325  else
326  {
327  localChatTab->chatLog(chatMsg,
331  }
332  }
333  BLOCK_END("ChatRecv::processWhisper")
334 }
335 
337 {
338  if (actorManager == nullptr)
339  return;
340 
341  BLOCK_START("ChatRecv::processBeingChat")
342  const int chatMsgLength = msg.readInt16("len") - 8;
343  const BeingId beingId = msg.readBeingId("being id");
344  Being *const being = actorManager->findBeing(beingId);
345 
346  if (chatMsgLength <= 0)
347  {
348  BLOCK_END("ChatRecv::processBeingChat")
349  return;
350  }
351 
352  std::string chatMsg = msg.readRawString(chatMsgLength, "message");
353 
354  if ((being != nullptr) && being->getType() == ActorType::Player)
355  being->setTalkTime();
356 
357  const size_t pos = chatMsg.find(" : ", 0);
358  std::string sender_name = ((pos == std::string::npos)
359  ? "" : chatMsg.substr(0, pos));
360 
362  {
363  // work around for "new" tmw server
364  if (being != nullptr)
365  sender_name = being->getName();
366  if (sender_name.empty())
367  {
368  sender_name = "?" + toString(CAST_S32(beingId));
369  const std::string name = actorManager->getSeenPlayerById(beingId);
370  if (!name.empty())
371  sender_name.append(" ").append(name);
372  }
373  }
374  else if ((being != nullptr) &&
375  sender_name != being->getName() &&
376  being->getType() == ActorType::Player)
377  {
378  if (!being->getName().empty())
379  sender_name = being->getName();
380  }
381  else
382  {
383  chatMsg.erase(0, pos + 3);
384  }
385 
386  trim(chatMsg);
387 
388  bool allow(true);
389  // We use getIgnorePlayer instead of ignoringPlayer here
390  // because ignorePlayer' side effects are triggered
391  // right below for Being::IGNORE_SPEECH_FLOAT.
392  if ((playerRelations.checkPermissionSilently(sender_name,
393  PlayerRelation::SPEECH_LOG) != 0U) &&
394  (chatWindow != nullptr))
395  {
396  allow = chatWindow->resortChatLog(
397  removeColors(sender_name).append(" : ").append(chatMsg),
402  }
403 
404  if (allow &&
405  (being != nullptr) &&
406  playerRelations.hasPermission(sender_name,
408  {
409  being->setSpeech(chatMsg);
410  }
411  BLOCK_END("ChatRecv::processBeingChat")
412 }
413 
415 {
416  const int sz = msg.readInt16("len") - 5;
417  msg.readUInt8("message type");
418  const std::string message = msg.readString(sz, "message");
419  localChatTab->chatLog(message,
423 }
424 
425 } // namespace TmwAthena
ActorManager * actorManager
volatile time_t cur_time
Definition: timer.cpp:58
int BeingId
Definition: beingid.h:30
#define CAST_S32
Definition: cast.h:30
ChatTab * localChatTab
Definition: chattab.cpp:62
#define debugMsg(str)
Definition: chattab.h:42
ChatWindow * chatWindow
Definition: chatwindow.cpp:94
Being * findBeing(const BeingId id) const
void parseLevels(std::string levels) const
std::string getSeenPlayerById(const BeingId id) const
Definition: being.h:96
const std::string & getName() const
Definition: being.h:232
void setSpeech(const std::string &text)
Definition: being.cpp:572
ActorTypeT getType() const
Definition: being.h:116
void setTalkTime()
Definition: being.h:712
void chatLog(std::string line, ChatMsgTypeT own, const IgnoreRecord ignoreRecord, const TryRemoveColors tryRemoveColors)
Definition: chattab.cpp:111
void addWhisper(const std::string &nick, const std::string &mes, const ChatMsgTypeT own)
bool resortChatLog(std::string line, ChatMsgTypeT own, const std::string &channel, const IgnoreRecord ignoreRecord, const TryRemoveColors tryRemoveColors)
void addGlobalMessage(const std::string &line)
bool getBoolValue(const std::string &key) const
static bool getEnableGuildBot()
Definition: guildmanager.h:56
bool processGuildMessage(const std::string &msg)
virtual bool haveIncompleteChatMessages() const =0
bool hasPermission(const Being *const being, const unsigned int flags) const
unsigned int checkPermissionSilently(const std::string &player_name, const unsigned int flags) const
void giveList(const std::string &nick, const int mode)
Definition: shopwindow.cpp:759
static void showList(const std::string &nick, std::string data)
Definition: shopwindow.cpp:854
void processRequest(const std::string &nick, std::string data, const int mode)
Definition: shopwindow.cpp:924
Configuration config
const std::string GENERAL_CHANNEL
Definition: chat.h:29
#define _(s)
Definition: gettext.h:35
GmTab * gmChatTab
Definition: gmtab.cpp:34
GuildManager * guildManager
const bool IgnoreRecord_false
Definition: ignorerecord.h:30
LocalPlayer * localPlayer
bool msg(InputEvent &event)
Definition: chat.cpp:39
std::string trim(std::string const &str)
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:1774
void processChatContinue(std::string chatMsg, const ChatMsgTypeT own)
Definition: chatrecv.cpp:279
void processWhisperContinue(const std::string &nick, std::string chatMsg)
Definition: chatrecv.cpp:522
time_t mMotdTime
Definition: chatrecv.cpp:50
void processWhisperResponseContinue(Net::MessageIn &msg, const uint8_t type)
Definition: chatrecv.cpp:81
bool mShowMotd
Definition: chatrecv.cpp:52
bool mSkipping
Definition: chatrecv.cpp:53
void processWhisperContinue(const std::string &nick, std::string chatMsg)
Definition: chatrecv.cpp:171
void processChatContinue(std::string chatMsg)
Definition: chatrecv.cpp:73
void processScriptMessage(Net::MessageIn &msg)
Definition: chatrecv.cpp:414
void processBeingChat(Net::MessageIn &msg)
Definition: chatrecv.cpp:336
void processGmChat(Net::MessageIn &msg)
Definition: chatrecv.cpp:125
void processChat(Net::MessageIn &msg)
Definition: chatrecv.cpp:60
std::string mShopRequestName
Definition: chatrecv.cpp:57
void processWhisper(Net::MessageIn &msg)
Definition: chatrecv.cpp:148
void processWhisperResponse(Net::MessageIn &msg)
Definition: chatrecv.cpp:163
Net::ServerFeatures * serverFeatures
Definition: net.cpp:101
#define BLOCK_END(name)
Definition: perfomance.h:80
#define BLOCK_START(name)
Definition: perfomance.h:79
PlayerRelationsManager playerRelations
ShopWindow * shopWindow
Definition: shopwindow.cpp:101
std::string removeColors(std::string msg)
std::string strprintf(const char *const format,...)
bool strStartWith(const std::string &str1, const std::string &str2)
static const unsigned int SPEECH_FLOAT
static const unsigned int SPEECH_LOG
static const unsigned int WHISPER
static const unsigned int TRADE
const bool TryRemoveColors_true