GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/whoisonline.cpp Lines: 68 438 15.5 %
Date: 2017-11-29 Branches: 36 534 6.7 %

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-2017  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
2
WhoIsOnline::WhoIsOnline() :
96
    // TRANSLATORS: who is online window name
97
2
    Window(_("Who Is Online - Updating"),
98
        Modal_false,
99
        nullptr,
100
        "whoisonline.xml"),
101
    mUpdateTimer(0),
102
    mThread(nullptr),
103
    mMemoryBuffer(nullptr),
104
2
    mCurlError(new char[CURL_ERROR_SIZE]),
105
    mBrowserBox(new StaticBrowserBox(this, Opaque_true,
106

2
        "onlinebrowserbox.xml")),
107

2
    mScrollArea(new ScrollArea(this, mBrowserBox, Opaque_false)),
108
    // TRANSLATORS: who is online. button.
109

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

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




70
    mServerSideList(serverFeatures->haveServerOnlineList())
127
{
128
2
    mCurlError[0] = 0;
129
10
    setWindowName("WhoIsOnline");
130
2
}
131
132
2
void WhoIsOnline::postInit()
133
{
134
2
    Window::postInit();
135
2
    const int h = 350;
136
2
    const int w = 200;
137
2
    setDefaultSize(w, h, ImagePosition::CENTER);
138
139
2
    setVisible(Visible_false);
140
2
    setCloseButton(true);
141
2
    setResizable(true);
142
2
    setStickyButtonLock(true);
143
4
    setSaveVisible(true);
144
145
2
    if (setupWindow != nullptr)
146
        setupWindow->registerWindowForReset(this);
147
148
4
    mUpdateButton->setEnabled(false);
149
4
    mUpdateButton->setDimension(Rect(5, 5, w - 10, 20 + 5));
150
151
4
    mBrowserBox->setOpaque(Opaque_false);
152
4
    mScrollArea->setDimension(Rect(5, 20 + 10, w - 10, h - 10 - 30));
153
2
    mScrollArea->setSize(w - 10, h - 10 - 30);
154
4
    mScrollArea->setSelectable(false);
155
2
    mBrowserBox->setLinkHandler(this);
156
157
2
    add(mUpdateButton);
158
2
    add(mScrollArea);
159
160
4
    setLocationRelativeTo(getParent());
161
162
2
    loadWindowState();
163
4
    enableVisibleSound(true);
164
165
2
    download();
166
167
4
    widgetResized(Event(nullptr));
168

8
    config.addListener("updateOnlineList", this);
169

8
    config.addListener("groupFriends", this);
170

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

6
}