GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/whoisonline.cpp Lines: 68 438 15.5 %
Date: 2018-09-20 Branches: 29 514 5.6 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  Andrei Karas
5
 *  Copyright (C) 2011-2018  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "gui/windows/whoisonline.h"
24
25
#include "actormanager.h"
26
#include "configuration.h"
27
#include "guild.h"
28
#include "party.h"
29
#ifdef TMWA_SUPPORT
30
#include "settings.h"
31
#endif  // TMWA_SUPPORT
32
33
#include "gui/onlineplayer.h"
34
#include "gui/popupmanager.h"
35
#include "gui/viewport.h"
36
37
#include "gui/popups/popupmenu.h"
38
39
#include "gui/windows/chatwindow.h"
40
#include "gui/windows/setupwindow.h"
41
#include "gui/windows/socialwindow.h"
42
43
#include "gui/widgets/button.h"
44
#include "gui/widgets/scrollarea.h"
45
#include "gui/widgets/staticbrowserbox.h"
46
47
#include "being/beingflag.h"
48
#include "being/localplayer.h"
49
#include "being/playerrelations.h"
50
51
#ifdef TMWA_SUPPORT
52
#include "net/download.h"
53
#endif  // TMWA_SUPPORT
54
#include "net/packetlimiter.h"
55
#include "net/playerhandler.h"
56
#include "net/serverfeatures.h"
57
58
#include "utils/foreach.h"
59
#include "utils/gettext.h"
60
#include "utils/sdlhelper.h"
61
62
#ifdef TMWA_SUPPORT
63
#include "net/net.h"
64
#else  // TMWA_SUPPORT
65
#include <curl/curl.h>
66
#endif  // TMWA_SUPPORT
67
68
#include "debug.h"
69
70
#ifdef free
71
#undef free
72
#endif  // free
73
74
#ifdef malloc
75
#undef malloc
76
#endif  // malloc
77
78
WhoIsOnline *whoIsOnline = nullptr;
79
80
namespace
81
{
82
    class NameFunctuator final
83
    {
84
        public:
85
            A_DEFAULT_COPY(NameFunctuator)
86
87
            bool operator()(const OnlinePlayer *left,
88
                            const OnlinePlayer *right) const
89
            {
90
                return (compareStrI(left->getNick(), right->getNick()) < 0);
91
            }
92
    } nameCompare;
93
}  // namespace
94
95
1
WhoIsOnline::WhoIsOnline() :
96
    // TRANSLATORS: who is online window name
97
1
    Window(_("Who Is Online - Updating"),
98
        Modal_false,
99
        nullptr,
100
        "whoisonline.xml"),
101
    mUpdateTimer(0),
102
    mThread(nullptr),
103
    mMemoryBuffer(nullptr),
104
1
    mCurlError(new char[CURL_ERROR_SIZE]),
105
    mBrowserBox(new StaticBrowserBox(this, Opaque_true,
106

1
        "onlinebrowserbox.xml")),
107
    mScrollArea(new ScrollArea(this,
108

2
        mBrowserBox, Opaque_false, std::string())),
109
    // TRANSLATORS: who is online. button.
110

2
    mUpdateButton(new Button(this, _("Update"), "update", BUTTON_SKIN, this)),
111
    mOnlinePlayers(),
112
    mOnlineNicks(),
113
    mFriends(),
114
    mNeutral(),
115
    mDisregard(),
116
    mEnemy(),
117
    mDownloadedBytes(0),
118
    mDownloadStatus(UPDATE_LIST),
119
    mDownloadComplete(true),
120
    mAllowUpdate(true),
121
    mShowLevel(false),
122

4
    mUpdateOnlineList(config.getBoolValue("updateOnlineList")),
123
    mGroupFriends(true),
124
#ifdef TMWA_SUPPORT
125
1
    mWebList(Net::getNetworkType() == ServerType::TMWATHENA),
126
#endif
127



33
    mServerSideList(serverFeatures->haveServerOnlineList())
128
{
129
1
    mCurlError[0] = 0;
130
5
    setWindowName("WhoIsOnline");
131
1
}
132
133
1
void WhoIsOnline::postInit()
134
{
135
1
    Window::postInit();
136
1
    const int h = 350;
137
1
    const int w = 200;
138
1
    setDefaultSize(w, h, ImagePosition::CENTER, 0, 0);
139
140
1
    setVisible(Visible_false);
141
1
    setCloseButton(true);
142
1
    setResizable(true);
143
1
    setStickyButtonLock(true);
144
2
    setSaveVisible(true);
145
146
1
    if (setupWindow != nullptr)
147
        setupWindow->registerWindowForReset(this);
148
149
2
    mUpdateButton->setEnabled(false);
150
1
    mUpdateButton->setDimension(Rect(5, 5, w - 10, 20 + 5));
151
152
2
    mBrowserBox->setOpaque(Opaque_false);
153
1
    mScrollArea->setDimension(Rect(5, 20 + 10, w - 10, h - 10 - 30));
154
1
    mScrollArea->setSize(w - 10, h - 10 - 30);
155
2
    mScrollArea->setSelectable(false);
156
1
    mBrowserBox->setLinkHandler(this);
157
158
1
    add(mUpdateButton);
159
1
    add(mScrollArea);
160
161
2
    setLocationRelativeTo(getParent());
162
163
1
    loadWindowState();
164
2
    enableVisibleSound(true);
165
166
1
    download();
167
168
2
    widgetResized(Event(nullptr));
169
4
    config.addListener("updateOnlineList", this);
170
4
    config.addListener("groupFriends", this);
171
4
    mGroupFriends = config.getBoolValue("groupFriends");
172
1
}
173
174
12
WhoIsOnline::~WhoIsOnline()
175
{
176
1
    config.removeListeners(this);
177
    CHECKLISTENERS
178
179
1
    SDL::WaitThread(mThread);
180
1
    mThread = nullptr;
181
1
    free(mMemoryBuffer);
182
1
    mMemoryBuffer = nullptr;
183
184
    // Remove possibly leftover temporary download
185
1
    delete []mCurlError;
186
187
3
    FOR_EACH (std::set<OnlinePlayer*>::iterator, itd, mOnlinePlayers)
188
        delete *itd;
189
2
    mOnlinePlayers.clear();
190
2
    mOnlineNicks.clear();
191
2
}
192
193
void WhoIsOnline::handleLink(const std::string& link, MouseEvent *event)
194
{
195
    if ((event == nullptr) || event->getButton() == MouseButton::LEFT)
196
    {
197
        if (chatWindow != nullptr)
198
        {
199
            const std::string text = decodeLinkText(link);
200
            if (config.getBoolValue("whispertab"))
201
            {
202
                chatWindow->localChatInput("/q " + text);
203
            }
204
            else
205
            {
206
                chatWindow->addInputText(
207
                    std::string("/w \"").append(text).append("\" "),
208
                    true);
209
            }
210
        }
211
    }
212
    else if (event->getButton() == MouseButton::RIGHT)
213
    {
214
        if ((localPlayer != nullptr) && link == localPlayer->getName())
215
            return;
216
217
        if (popupMenu != nullptr)
218
        {
219
            if (actorManager != nullptr)
220
            {
221
                const std::string text = decodeLinkText(link);
222
                Being *const being = actorManager->findBeingByName(
223
                    text, ActorType::Player);
224
225
                if ((being != nullptr) && (popupManager != nullptr))
226
                {
227
                    popupMenu->showPopup(viewport->mMouseX,
228
                        viewport->mMouseY,
229
                        being);
230
                    return;
231
                }
232
            }
233
            popupMenu->showPlayerPopup(link);
234
        }
235
    }
236
}
237
238
void WhoIsOnline::updateWindow(size_t numOnline)
239
{
240
    // Set window caption
241
    // TRANSLATORS: who is online window name
242
    setCaption(_("Who Is Online - ") + toString(CAST_U32(numOnline)));
243
244
    // List the online people
245
    std::sort(mFriends.begin(), mFriends.end(), nameCompare);
246
    std::sort(mNeutral.begin(), mNeutral.end(), nameCompare);
247
    std::sort(mDisregard.begin(), mDisregard.end(), nameCompare);
248
    bool addedFromSection(false);
249
    FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, mFriends)
250
    {
251
        mBrowserBox->addRow((*it)->getText(), false);
252
        addedFromSection = true;
253
    }
254
    if (addedFromSection == true)
255
    {
256
        mBrowserBox->addRow("---", false);
257
        addedFromSection = false;
258
    }
259
    FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, mEnemy)
260
    {
261
        mBrowserBox->addRow((*it)->getText(), false);
262
        addedFromSection = true;
263
    }
264
    if (addedFromSection == true)
265
    {
266
        mBrowserBox->addRow("---", false);
267
        addedFromSection = false;
268
    }
269
    FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, mNeutral)
270
    {
271
        mBrowserBox->addRow((*it)->getText(), false);
272
        addedFromSection = true;
273
    }
274
    if (addedFromSection == true && !mDisregard.empty())
275
        mBrowserBox->addRow("---", false);
276
277
    FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, mDisregard)
278
        mBrowserBox->addRow((*it)->getText(), false);
279
280
    if (mScrollArea->getVerticalMaxScroll() <
281
        mScrollArea->getVerticalScrollAmount())
282
    {
283
        mScrollArea->setVerticalScrollAmount(
284
            mScrollArea->getVerticalMaxScroll());
285
    }
286
    mBrowserBox->updateHeight();
287
}
288
289
void WhoIsOnline::handlerPlayerRelation(const std::string &nick,
290
                                        OnlinePlayer *const player)
291
{
292
    if (player == nullptr)
293
        return;
294
    switch (playerRelations.getRelation(nick))
295
    {
296
        case Relation::NEUTRAL:
297
        default:
298
            setNeutralColor(player);
299
            mNeutral.push_back(player);
300
            break;
301
302
        case Relation::FRIEND:
303
            player->setText("2");
304
            if (mGroupFriends)
305
                mFriends.push_back(player);
306
            else
307
                mNeutral.push_back(player);
308
            break;
309
310
        case Relation::DISREGARDED:
311
        case Relation::BLACKLISTED:
312
            player->setText("8");
313
            mDisregard.push_back(player);
314
            break;
315
316
        case Relation::ENEMY2:
317
            player->setText("1");
318
            mEnemy.push_back(player);
319
            break;
320
321
        case Relation::IGNORED:
322
        case Relation::ERASED:
323
            // Ignore the ignored.
324
            break;
325
    }
326
}
327
328
void WhoIsOnline::loadList(const STD_VECTOR<OnlinePlayer*> &list)
329
{
330
    mBrowserBox->clearRows();
331
    const size_t numOnline = list.size();
332
333
    FOR_EACH (std::set<OnlinePlayer*>::iterator, itd, mOnlinePlayers)
334
        delete *itd;
335
    mOnlinePlayers.clear();
336
    mOnlineNicks.clear();
337
338
    mShowLevel = config.getBoolValue("showlevel");
339
340
    FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, list)
341
    {
342
        OnlinePlayer *player = *it;
343
        const std::string nick = player->getNick();
344
        mOnlinePlayers.insert(player);
345
        mOnlineNicks.insert(nick);
346
347
        if (!mShowLevel)
348
            player->setLevel(0);
349
350
        handlerPlayerRelation(nick, player);
351
    }
352
353
    updateWindow(numOnline);
354
    if (!mOnlineNicks.empty())
355
    {
356
        if (chatWindow != nullptr)
357
            chatWindow->updateOnline(mOnlineNicks);
358
        if (socialWindow != nullptr)
359
            socialWindow->updateActiveList();
360
        if (actorManager != nullptr)
361
            actorManager->updateSeenPlayers(mOnlineNicks);
362
    }
363
    updateSize();
364
    mFriends.clear();
365
    mNeutral.clear();
366
    mDisregard.clear();
367
    mEnemy.clear();
368
}
369
370
#ifdef TMWA_SUPPORT
371
void WhoIsOnline::loadWebList()
372
{
373
    if (mMemoryBuffer == nullptr)
374
        return;
375
376
    // Reallocate and include terminating 0 character
377
    mMemoryBuffer = static_cast<char*>(
378
        realloc(mMemoryBuffer, mDownloadedBytes + 1));
379
    if (mMemoryBuffer == nullptr)
380
        return;
381
382
    mMemoryBuffer[mDownloadedBytes] = '\0';
383
384
    mBrowserBox->clearRows();
385
    bool listStarted(false);
386
    std::string lineStr;
387
    size_t numOnline(0U);
388
389
    // Tokenize and add each line separately
390
    char *line = strtok(mMemoryBuffer, "\n");
391
    const std::string gmText("(GM)");
392
    const std::string gmText2("(gm)");
393
394
    FOR_EACH (std::set<OnlinePlayer*>::iterator, itd, mOnlinePlayers)
395
        delete *itd;
396
397
    mOnlinePlayers.clear();
398
    mOnlineNicks.clear();
399
400
    mShowLevel = config.getBoolValue("showlevel");
401
402
    while (line != nullptr)
403
    {
404
        std::string nick;
405
        lineStr = line;
406
        trim(lineStr);
407
        if (listStarted == true)
408
        {
409
            if (lineStr.find(" users are online.") == std::string::npos)
410
            {
411
                if (lineStr.length() > 24)
412
                {
413
                    nick = lineStr.substr(0, 24);
414
                    lineStr = lineStr.substr(25);
415
                }
416
                else
417
                {
418
                    nick = lineStr;
419
                    lineStr.clear();
420
                }
421
                trim(nick);
422
423
                bool isGM(false);
424
                size_t pos = lineStr.find(gmText, 0);
425
                if (pos != std::string::npos)
426
                {
427
                    lineStr = lineStr.substr(pos + gmText.length());
428
                    isGM = true;
429
                }
430
                else
431
                {
432
                    pos = lineStr.find(gmText2, 0);
433
                    if (pos != std::string::npos)
434
                    {
435
                        lineStr = lineStr.substr(pos + gmText.length());
436
                        isGM = true;
437
                    }
438
                }
439
440
                trim(lineStr);
441
                pos = lineStr.find('/', 0);
442
443
                if (pos != std::string::npos)
444
                    lineStr = lineStr.substr(0, pos);
445
446
                int level = 0;
447
                if (!lineStr.empty())
448
                    level = atoi(lineStr.c_str());
449
450
                if (actorManager != nullptr)
451
                {
452
                    Being *const being = actorManager->findBeingByName(
453
                        nick, ActorType::Player);
454
                    if (being != nullptr)
455
                    {
456
                        if (level > 0)
457
                        {
458
                            being->setLevel(level);
459
                            being->updateName();
460
                        }
461
                        else
462
                        {
463
                            if (being->getLevel() > 1)
464
                                level = being->getLevel();
465
                        }
466
                    }
467
                }
468
469
                if (!mShowLevel)
470
                    level = 0;
471
472
                OnlinePlayer *const player = new OnlinePlayer(nick,
473
                    CAST_U8(255), level,
474
                    Gender::UNSPECIFIED, -1);
475
                mOnlinePlayers.insert(player);
476
                mOnlineNicks.insert(nick);
477
478
                if (isGM)
479
                    player->setIsGM(true);
480
481
                numOnline++;
482
                handlerPlayerRelation(nick, player);
483
            }
484
        }
485
        else if (lineStr.find("------------------------------")
486
                 != std::string::npos)
487
        {
488
            listStarted = true;
489
        }
490
        line = strtok(nullptr, "\n");
491
    }
492
493
    updateWindow(numOnline);
494
    mBrowserBox->updateHeight();
495
496
    // Free the memory buffer now that we don't need it anymore
497
    free(mMemoryBuffer);
498
    mMemoryBuffer = nullptr;
499
    mFriends.clear();
500
    mNeutral.clear();
501
    mDisregard.clear();
502
    mEnemy.clear();
503
}
504
505
size_t WhoIsOnline::memoryWrite(void *restrict ptr,
506
                                size_t size,
507
                                size_t nmemb,
508
                                FILE *restrict stream)
509
{
510
    if (stream == nullptr)
511
        return 0;
512
513
    WhoIsOnline *restrict const wio =
514
        reinterpret_cast<WhoIsOnline *restrict>(stream);
515
    const size_t totalMem = size * nmemb;
516
    wio->mMemoryBuffer = static_cast<char*>(realloc(wio->mMemoryBuffer,
517
        CAST_SIZE(wio->mDownloadedBytes) + totalMem));
518
    if (wio->mMemoryBuffer != nullptr)
519
    {
520
        memcpy(&(wio->mMemoryBuffer[wio->mDownloadedBytes]), ptr, totalMem);
521
        wio->mDownloadedBytes += CAST_S32(totalMem);
522
    }
523
524
    return totalMem;
525
}
526
527
int WhoIsOnline::downloadThread(void *ptr)
528
{
529
    int attempts = 0;
530
    WhoIsOnline *const wio = reinterpret_cast<WhoIsOnline *>(ptr);
531
    if (wio == nullptr)
532
        return 0;
533
    CURLcode res;
534
    const std::string url(settings.onlineListUrl + "/online.txt");
535
536
    while (attempts < 1 && !wio->mDownloadComplete)
537
    {
538
        CURL *curl = curl_easy_init();
539
        if (curl != nullptr)
540
        {
541
            if (!wio->mAllowUpdate)
542
            {
543
                curl_easy_cleanup(curl);
544
                curl = nullptr;
545
                break;
546
            }
547
            wio->mDownloadedBytes = 0;
548
            curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
549
            curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
550
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
551
                                   &WhoIsOnline::memoryWrite);
552
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
553
554
            curl_easy_setopt(curl,
555
                CURLOPT_USERAGENT,
556
                settings.userAgent.c_str());
557
558
            curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, wio->mCurlError);
559
            curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
560
            curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
561
            curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ptr);
562
563
#if LIBCURL_VERSION_NUM >= 0x070a00
564
            curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
565
#endif  // LIBCURL_VERSION_NUM >= 0x070a00
566
            curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 7);
567
            curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
568
            Net::Download::addHeaders(curl);
569
            Net::Download::addProxy(curl);
570
            Net::Download::secureCurl(curl);
571
            Net::Download::addCommonFlags(curl);
572
573
            // Make sure the resources2.txt and news.txt aren't cached,
574
            // in order to always get the latest version.
575
            struct curl_slist *pHeaders = nullptr;
576
            pHeaders = curl_slist_append(
577
                pHeaders, "pragma: no-cache");
578
            pHeaders = curl_slist_append(pHeaders,
579
                "Cache-Control: no-cache");
580
            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pHeaders);
581
582
            if ((res = curl_easy_perform(curl)) != 0)
583
            {
584
                wio->mDownloadStatus = UPDATE_ERROR;
585
                PRAGMA45(GCC diagnostic push)
586
                PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
587
                switch (res)
588
                {
589
                    case CURLE_COULDNT_CONNECT:
590
                    default:
591
                        std::cerr << "curl error "
592
                                  << CAST_U32(res) << ": "
593
                                  << wio->mCurlError << " host: "
594
                                  << url.c_str() << std::endl;
595
                    break;
596
                }
597
                PRAGMA45(GCC diagnostic pop)
598
                attempts++;
599
                curl_easy_cleanup(curl);
600
                curl_slist_free_all(pHeaders);
601
                curl = nullptr;
602
                continue;
603
            }
604
605
            curl_easy_cleanup(curl);
606
            curl_slist_free_all(pHeaders);
607
608
            // It's stored in memory, we're done
609
            wio->mDownloadComplete = true;
610
        }
611
        if (!wio->mAllowUpdate)
612
            break;
613
        attempts++;
614
    }
615
616
    if (!wio->mDownloadComplete)
617
        wio->mDownloadStatus = UPDATE_ERROR;
618
    return 0;
619
}
620
#endif  // TMWA_SUPPORT
621
622
1
void WhoIsOnline::download()
623
{
624
1
    if (mServerSideList)
625
    {
626
        if (PacketLimiter::limitPackets(PacketType::PACKET_ONLINELIST))
627
            playerHandler->requestOnlineList();
628
    }
629
#ifdef TMWA_SUPPORT
630
1
    else if (mWebList)
631
    {
632
        mDownloadComplete = true;
633
        SDL::WaitThread(mThread);
634
        mThread = nullptr;
635
        mDownloadComplete = false;
636
        mThread = SDL::createThread(&WhoIsOnline::downloadThread,
637
            "whoisonline", this);
638
        if (mThread == nullptr)
639
            mDownloadStatus = UPDATE_ERROR;
640
    }
641
#endif  // TMWA_SUPPORT
642
1
}
643
644
void WhoIsOnline::logic()
645
{
646
    BLOCK_START("WhoIsOnline::logic")
647
    mScrollArea->logic();
648
    BLOCK_END("WhoIsOnline::logic")
649
}
650
651
void WhoIsOnline::slowLogic()
652
{
653
    if (!mAllowUpdate)
654
        return;
655
656
    BLOCK_START("WhoIsOnline::slowLogic")
657
    if (mUpdateTimer == 0)
658
        mUpdateTimer = cur_time;
659
660
    const double timeDiff = difftime(cur_time, mUpdateTimer);
661
    const int timeLimit = isWindowVisible() ? 20 : 120;
662
663
    if (mUpdateOnlineList && timeDiff >= timeLimit
664
        && mDownloadStatus != UPDATE_LIST)
665
    {
666
        if (mDownloadComplete == true)
667
        {
668
            // TRANSLATORS: who is online window name
669
            setCaption(_("Who Is Online - Updating"));
670
            mUpdateTimer = 0;
671
            mDownloadStatus = UPDATE_LIST;
672
            download();
673
        }
674
    }
675
676
#ifdef TMWA_SUPPORT
677
    switch (mDownloadStatus)
678
    {
679
        case UPDATE_ERROR:
680
            logger->assertLog("Failed to fetch the online list:");
681
            if (mCurlError != nullptr)
682
                logger->assertLog("%s", mCurlError);
683
            mDownloadStatus = UPDATE_COMPLETE;
684
            // TRANSLATORS: who is online window name
685
            setCaption(_("Who Is Online - error"));
686
            mUpdateButton->setEnabled(true);
687
            mUpdateTimer = cur_time + 240;
688
            mDownloadComplete = true;
689
            updateSize();
690
            break;
691
        case UPDATE_LIST:
692
            if (mDownloadComplete == true)
693
            {
694
                loadWebList();
695
                mDownloadStatus = UPDATE_COMPLETE;
696
                mUpdateButton->setEnabled(true);
697
                mUpdateTimer = 0;
698
                updateSize();
699
                if (!mOnlineNicks.empty())
700
                {
701
                    if (chatWindow != nullptr)
702
                        chatWindow->updateOnline(mOnlineNicks);
703
                    if (socialWindow != nullptr)
704
                        socialWindow->updateActiveList();
705
                    if (actorManager != nullptr)
706
                        actorManager->updateSeenPlayers(mOnlineNicks);
707
                }
708
            }
709
            break;
710
        case UPDATE_COMPLETE:
711
        default:
712
            break;
713
    }
714
#endif  // TMWA_SUPPORT
715
716
    BLOCK_END("WhoIsOnline::slowLogic")
717
}
718
719
void WhoIsOnline::action(const ActionEvent &event)
720
{
721
    if (event.getId() == "update")
722
    {
723
#ifdef TMWA_SUPPORT
724
        if (!mServerSideList)
725
        {
726
            if (mDownloadStatus == UPDATE_COMPLETE)
727
            {
728
                mUpdateTimer = cur_time - 20;
729
                if (mUpdateButton != nullptr)
730
                    mUpdateButton->setEnabled(false);
731
                // TRANSLATORS: who is online window name
732
                setCaption(_("Who Is Online - Update"));
733
                SDL::WaitThread(mThread);
734
                mThread = nullptr;
735
                mDownloadComplete = true;
736
            }
737
        }
738
        else
739
#endif  // TMWA_SUPPORT
740
        {
741
            if (PacketLimiter::limitPackets(PacketType::PACKET_ONLINELIST))
742
            {
743
                mUpdateTimer = cur_time;
744
                playerHandler->requestOnlineList();
745
            }
746
        }
747
    }
748
}
749
750
1
void WhoIsOnline::widgetResized(const Event &event)
751
{
752
2
    Window::widgetResized(event);
753
2
    updateSize();
754
1
}
755
756
2
void WhoIsOnline::updateSize()
757
{
758
4
    const Rect area = getChildrenArea();
759
2
    if (mUpdateButton != nullptr)
760
2
        mUpdateButton->setWidth(area.width - 10);
761
762
2
    if (mScrollArea != nullptr)
763
2
        mScrollArea->setSize(area.width - 10, area.height - 10 - 30);
764
2
}
765
766
const std::string WhoIsOnline::prepareNick(const std::string &restrict nick,
767
                                           const int level,
768
                                           const std::string &restrict
769
                                           color) const
770
{
771
    const std::string text = encodeLinkText(nick);
772
    if (mShowLevel && level > 1)
773
    {
774
        return strprintf("@@%s|##%s%s (%d)@@", text.c_str(),
775
            color.c_str(), text.c_str(), level);
776
    }
777
    return strprintf("@@%s|##%s%[email protected]@", text.c_str(),
778
        color.c_str(), text.c_str());
779
}
780
781
void WhoIsOnline::optionChanged(const std::string &name)
782
{
783
    if (name == "updateOnlineList")
784
        mUpdateOnlineList = config.getBoolValue("updateOnlineList");
785
    else if (name == "groupFriends")
786
        mGroupFriends = config.getBoolValue("groupFriends");
787
}
788
789
void WhoIsOnline::setNeutralColor(OnlinePlayer *const player)
790
{
791
    if (player == nullptr)
792
        return;
793
794
    if ((actorManager != nullptr) && (localPlayer != nullptr))
795
    {
796
        const std::string &nick = player->getNick();
797
        if (nick == localPlayer->getName())
798
        {
799
            player->setText("s");
800
            return;
801
        }
802
        if (localPlayer->isInParty())
803
        {
804
            const Party *const party = localPlayer->getParty();
805
            if (party != nullptr)
806
            {
807
                if (party->getMember(nick) != nullptr)
808
                {
809
                    player->setText("P");
810
                    return;
811
                }
812
            }
813
        }
814
815
        const Being *const being = actorManager->findBeingByName(nick,
816
            ActorType::Player);
817
        if (being != nullptr)
818
        {
819
            const Guild *const guild2 = localPlayer->getGuild();
820
            if (guild2 != nullptr)
821
            {
822
                const Guild *const guild1 = being->getGuild();
823
                if (guild1 != nullptr)
824
                {
825
                    if (guild1->getId() == guild2->getId()
826
                        || (guild2->getMember(nick) != nullptr))
827
                    {
828
                        player->setText("U");
829
                        return;
830
                    }
831
                }
832
                else if (guild2->isMember(nick))
833
                {
834
                    player->setText("U");
835
                    return;
836
                }
837
            }
838
        }
839
        const Guild *const guild3 = Guild::getGuild(1);
840
        if ((guild3 != nullptr) && guild3->isMember(nick))
841
        {
842
            player->setText("U");
843
            return;
844
        }
845
    }
846
    player->setText("0");
847
}
848
849
void WhoIsOnline::getPlayerNames(StringVect &names)
850
{
851
    names.clear();
852
    FOR_EACH (std::set<std::string>::const_iterator, it, mOnlineNicks)
853
        names.push_back(*it);
854
}
855
856
void OnlinePlayer::setText(std::string color)
857
{
858
    mText.clear();
859
860
    if (mStatus != 255 && (actorManager != nullptr))
861
    {
862
        Being *const being = actorManager->findBeingByName(
863
            mNick, ActorType::Player);
864
        if (being != nullptr)
865
        {
866
            being->setState(mStatus);
867
            // for now highlight versions > 3
868
            if (mVersion > 3)
869
                being->setAdvanced(true);
870
        }
871
    }
872
873
    if ((mStatus != 255 && ((mStatus & BeingFlag::GM) != 0)) || mIsGM)
874
        mText.append("(GM) ");
875
876
    if (mLevel > 0)
877
        mText.append(strprintf("%d", mLevel));
878
879
    if (mGender == Gender::FEMALE)
880
        mText.append("\u2640");
881
    else if (mGender == Gender::MALE)
882
        mText.append("\u2642");
883
884
    if (mStatus > 0 && mStatus != 255)
885
    {
886
        if ((mStatus & BeingFlag::SHOP) != 0)
887
            mText.append("$");
888
        if ((mStatus & BeingFlag::AWAY) != 0)
889
        {
890
            // TRANSLATORS: this away status writed in player nick
891
            mText.append(_("A"));
892
        }
893
        if ((mStatus & BeingFlag::INACTIVE) != 0)
894
        {
895
            // TRANSLATORS: this inactive status writed in player nick
896
            mText.append(_("I"));
897
        }
898
899
        if (((mStatus & BeingFlag::GM) != 0) && color == "0")
900
            color = "2";
901
    }
902
    else if (mIsGM && color == "0")
903
    {
904
        color = "2";
905
    }
906
907
    if (mVersion > 0)
908
        mText.append(strprintf(" - %d", mVersion));
909
910
    const std::string text = encodeLinkText(mNick);
911
    mText = strprintf("@@%s|##%s%s %[email protected]@", text.c_str(), color.c_str(),
912
        text.c_str(), mText.c_str());
913

3
}