GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/tmwa/chatrecv.cpp Lines: 2 156 1.3 %
Date: 2021-03-17 Branches: 2 270 0.7 %

Line Branch Exec Source
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
35
#include "gui/widgets/tabs/chat/gmtab.h"
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
1
    std::string mShopRequestName;
58
}  // namespace ChatRecv
59
60
void ChatRecv::processChat(Net::MessageIn &msg)
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,
81
            ChatMsgType::BY_PLAYER,
82
            GENERAL_CHANNEL,
83
            IgnoreRecord_false,
84
            TryRemoveColors_true);
85
    }
86
87
    const std::string senseStr("You sense the following: ");
88
    if ((actorManager != nullptr) && (chatMsg.find(senseStr) == 0U))
89
    {
90
        actorManager->parseLevels(
91
            chatMsg.substr(senseStr.size()));
92
    }
93
94
    if (pos == std::string::npos &&
95
        !Ea::ChatRecv::mShowMotd &&
96
        Ea::ChatRecv::mSkipping)
97
    {
98
        // skip motd from "new" tmw server
99
        if (Ea::ChatRecv::mMotdTime == 0)
100
        {
101
            Ea::ChatRecv::mMotdTime = cur_time + 1;
102
        }
103
        else if (Ea::ChatRecv::mMotdTime == cur_time ||
104
                 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
125
void ChatRecv::processGmChat(Net::MessageIn &msg)
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
148
void ChatRecv::processWhisper(Net::MessageIn &msg)
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
163
void ChatRecv::processWhisperResponse(Net::MessageIn &msg)
164
{
165
    BLOCK_START("ChatRecv::processWhisperResponse")
166
167
    const uint8_t type = msg.readUInt8("response");
168
    Ea::ChatRecv::processWhisperResponseContinue(msg, type);
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
193
        if (playerRelations.hasPermission(nick, PlayerRelation::WHISPER))
194
        {
195
            const bool tradeBot = config.getBoolValue("tradebot");
196
            const bool showMsg = !config.getBoolValue("hideShopMessages");
197
            if (playerRelations.hasPermission(nick, PlayerRelation::TRADE))
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,
209
                                    ChatMsgType::BY_OTHER);
210
                            }
211
                            shopWindow->giveList(nick, ShopWindow::SELL);
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,
222
                                    ChatMsgType::BY_OTHER);
223
                            }
224
                            shopWindow->giveList(nick, ShopWindow::BUY);
225
                        }
226
                    }
227
                    else if (chatMsg.find("!buyitem ") == 0)
228
                    {
229
                        if (showMsg && chatWindow != nullptr)
230
                        {
231
                            chatWindow->addWhisper(nick,
232
                                chatMsg,
233
                                ChatMsgType::BY_OTHER);
234
                        }
235
                        if (tradeBot)
236
                        {
237
                            shopWindow->processRequest(nick, chatMsg,
238
                                ShopWindow::BUY);
239
                        }
240
                    }
241
                    else if (chatMsg.find("!sellitem ") == 0)
242
                    {
243
                        if (showMsg && chatWindow != nullptr)
244
                        {
245
                            chatWindow->addWhisper(nick,
246
                                chatMsg,
247
                                ChatMsgType::BY_OTHER);
248
                        }
249
                        if (tradeBot)
250
                        {
251
                            shopWindow->processRequest(nick, chatMsg,
252
                                ShopWindow::SELL);
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
                        {
261
                            debugMsg(strprintf(
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,
273
                                ChatMsgType::BY_OTHER);
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,
282
                            ChatMsgType::BY_OTHER);
283
                    }
284
                }
285
                else if (chatWindow != nullptr)
286
                {
287
                    chatWindow->addWhisper(nick,
288
                        chatMsg,
289
                        ChatMsgType::BY_OTHER);
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,
301
                        ChatMsgType::BY_OTHER);
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,
315
                    ChatMsgType::BY_SERVER,
316
                    IgnoreRecord_false,
317
                    TryRemoveColors_true);
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,
328
                ChatMsgType::BY_SERVER,
329
                IgnoreRecord_false,
330
                TryRemoveColors_true);
331
        }
332
    }
333
    BLOCK_END("ChatRecv::processWhisper")
334
}
335
336
void ChatRecv::processBeingChat(Net::MessageIn &msg)
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
361
    if (serverFeatures->haveIncompleteChatMessages())
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),
398
            ChatMsgType::BY_OTHER,
399
            GENERAL_CHANNEL,
400
            IgnoreRecord_false,
401
            TryRemoveColors_true);
402
    }
403
404
    if (allow &&
405
        (being != nullptr) &&
406
        playerRelations.hasPermission(sender_name,
407
        PlayerRelation::SPEECH_FLOAT))
408
    {
409
        being->setSpeech(chatMsg);
410
    }
411
    BLOCK_END("ChatRecv::processBeingChat")
412
}
413
414
void ChatRecv::processScriptMessage(Net::MessageIn &msg)
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,
420
        ChatMsgType::BY_SERVER,
421
        IgnoreRecord_false,
422
        TryRemoveColors_true);
423
}
424
425

3
}  // namespace TmwAthena