GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/progs/manaplus/client.cpp Lines: 97 861 11.3 %
Date: 2017-11-29 Branches: 42 1017 4.1 %

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-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 "progs/manaplus/client.h"
24
25
#include "chatlogger.h"
26
#include "configmanager.h"
27
#include "dirs.h"
28
#include "eventsmanager.h"
29
#include "game.h"
30
#include "graphicsmanager.h"
31
#include "main.h"
32
#include "party.h"
33
#include "settings.h"
34
#include "soundmanager.h"
35
#include "spellmanager.h"
36
37
#include "being/localplayer.h"
38
#include "being/playerinfo.h"
39
#include "being/playerrelations.h"
40
41
#include "const/net/net.h"
42
43
#include "enums/being/attributesstrings.h"
44
45
#include "fs/virtfs/fs.h"
46
#include "fs/virtfs/tools.h"
47
48
#include "gui/dialogsmanager.h"
49
#include "gui/gui.h"
50
#include "gui/skin.h"
51
#include "gui/popupmanager.h"
52
#include "gui/windowmanager.h"
53
54
#include "gui/shortcut/dropshortcut.h"
55
#include "gui/shortcut/emoteshortcut.h"
56
#include "gui/shortcut/itemshortcut.h"
57
#include "gui/shortcut/spellshortcut.h"
58
59
#include "gui/windows/changeemaildialog.h"
60
#include "gui/windows/changepassworddialog.h"
61
#include "gui/windows/charselectdialog.h"
62
#include "gui/windows/connectiondialog.h"
63
#include "gui/windows/equipmentwindow.h"
64
#include "gui/windows/logindialog.h"
65
#include "gui/windows/npcdialog.h"
66
#include "gui/windows/okdialog.h"
67
#include "gui/windows/registerdialog.h"
68
#include "gui/windows/serverdialog.h"
69
#include "gui/windows/setupwindow.h"
70
#include "gui/windows/updaterwindow.h"
71
#include "gui/windows/quitdialog.h"
72
#include "gui/windows/worldselectdialog.h"
73
74
#include "gui/widgets/button.h"
75
#include "gui/widgets/createwidget.h"
76
#include "gui/widgets/desktop.h"
77
#include "gui/widgets/windowcontainer.h"
78
79
#include "input/inputmanager.h"
80
#include "input/joystick.h"
81
#include "input/keyboardconfig.h"
82
83
#include "input/touch/touchmanager.h"
84
85
#include "net/charserverhandler.h"
86
#include "net/chathandler.h"
87
#include "net/download.h"
88
#include "net/gamehandler.h"
89
#include "net/generalhandler.h"
90
#include "net/guildhandler.h"
91
#include "net/inventoryhandler.h"
92
#include "net/ipc.h"
93
#include "net/loginhandler.h"
94
#include "net/net.h"
95
#include "net/updatetypeoperators.h"
96
#include "net/useragent.h"
97
#include "net/packetlimiter.h"
98
#include "net/partyhandler.h"
99
100
#ifdef TMWA_SUPPORT
101
#include "net/tmwa/guildmanager.h"
102
#endif  // TMWA_SUPPORT
103
104
#include "particle/particleengine.h"
105
106
#include "resources/dbmanager.h"
107
#include "resources/imagehelper.h"
108
109
#include "resources/dye/dyepalette.h"
110
111
#include "resources/resourcemanager/resourcemanager.h"
112
113
#include "resources/sprite/spritereference.h"
114
115
#include "utils/checkutils.h"
116
#include "utils/cpu.h"
117
#include "utils/delete2.h"
118
#include "utils/dumplibs.h"
119
#include "utils/dumpsizes.h"
120
#include "utils/env.h"
121
#include "utils/fuzzer.h"
122
#include "utils/gettext.h"
123
#include "utils/gettexthelper.h"
124
#include "utils/mrand.h"
125
#ifdef ANDROID
126
#include "fs/paths.h"
127
#endif  // ANDROID
128
#include "utils/sdlcheckutils.h"
129
#include "utils/sdlhelper.h"
130
#include "utils/timer.h"
131
132
#include "utils/translation/translationmanager.h"
133
134
#include "listeners/assertlistener.h"
135
#include "listeners/errorlistener.h"
136
137
#ifdef USE_OPENGL
138
#include "test/testlauncher.h"
139
#include "test/testmain.h"
140
#else  // USE_OPENGL
141
#include "configuration.h"
142
#endif  // USE_OPENGL
143
144
#ifdef WIN32
145
PRAGMA48(GCC diagnostic push)
146
PRAGMA48(GCC diagnostic ignored "-Wshadow")
147
#include <SDL_syswm.h>
148
PRAGMA48(GCC diagnostic pop)
149
#include "fs/specialfolder.h"
150
#undef ERROR
151
#endif  // WIN32
152
153
#ifdef ANDROID
154
#ifndef USE_SDL2
155
PRAGMA48(GCC diagnostic push)
156
PRAGMA48(GCC diagnostic ignored "-Wshadow")
157
#include <SDL_screenkeyboard.h>
158
PRAGMA48(GCC diagnostic pop)
159
#include <fstream>
160
#endif  // USE_SDL2
161
#endif  // ANDROID
162
163
#include <sys/stat.h>
164
165
#ifdef USE_MUMBLE
166
#include "mumblemanager.h"
167
#endif  // USE_MUMBLE
168
169
PRAGMA48(GCC diagnostic push)
170
PRAGMA48(GCC diagnostic ignored "-Wshadow")
171
#ifdef USE_SDL2
172
#include <SDL2_framerate.h>
173
#else  // USE_SDL2
174
#include <SDL_framerate.h>
175
#endif  // USE_SDL2
176
PRAGMA48(GCC diagnostic pop)
177
178
#include "debug.h"
179
180
2
std::string errorMessage;
181
2
LoginData loginData;
182
183
Client *client = nullptr;
184
185
extern FPSmanager fpsManager;
186
extern int evolPacketOffset;
187
188
volatile bool runCounters;
189
bool isSafeMode = false;
190
int serverVersion = 0;
191
int packetVersion = 0;
192
unsigned int tmwServerVersion = 0;
193
time_t start_time;
194
unsigned int mLastHost = 0;
195
unsigned long mSearchHash = 0;
196
int textures_count = 0;
197
volatile bool isTerminate = false;
198
199
namespace
200
{
201
2
    class AccountListener final : public ActionListener
202
    {
203
        public:
204
            AccountListener()
205
4
            { }
206
207
            A_DELETE_COPY(AccountListener)
208
209
            void action(const ActionEvent &event A_UNUSED) override final
210
            {
211
                client->setState(State::CHAR_SELECT);
212
            }
213
2
    } accountListener;
214
215
2
    class LoginListener final : public ActionListener
216
    {
217
        public:
218
            LoginListener()
219
4
            { }
220
221
            A_DELETE_COPY(LoginListener)
222
223
            void action(const ActionEvent &event A_UNUSED) override final
224
            {
225
                client->setState(State::PRE_LOGIN);
226
            }
227
2
    } loginListener;
228
}  // namespace
229
230
384
Client::Client() :
231
    ActionListener(),
232
    mCurrentServer(),
233
    mGame(nullptr),
234
    mCurrentDialog(nullptr),
235
    mQuitDialog(nullptr),
236
    mSetupButton(nullptr),
237
    mVideoButton(nullptr),
238
    mHelpButton(nullptr),
239
    mAboutButton(nullptr),
240
    mThemesButton(nullptr),
241
    mPerfomanceButton(nullptr),
242
#ifdef ANDROID
243
    mCloseButton(nullptr),
244
#endif  // ANDROID
245
    mState(State::CHOOSE_SERVER),
246
    mOldState(State::START),
247
    mSkin(nullptr),
248
    mButtonPadding(1),
249
    mButtonSpacing(3),
250
    mPing(0),
251
1152
    mConfigAutoSaved(false)
252
{
253
384
    WindowManager::init();
254
384
}
255
256
void Client::testsInit()
257
{
258
    if (!settings.options.test.empty() &&
259
        settings.options.test != "99")
260
    {
261
        gameInit();
262
    }
263
    else
264
    {
265
        initRand();
266
        logger = new Logger;
267
        SDL::initLogger();
268
        Dirs::initLocalDataDir();
269
        Dirs::initTempDir();
270
        Dirs::initConfigDir();
271
        GettextHelper::initLang();
272
    }
273
}
274
275
void Client::gameInit()
276
{
277
    logger = new Logger;
278
    SDL::initLogger();
279
280
    initRand();
281
282
    assertListener = new AssertListener;
283
    // Load branding information
284
    if (!settings.options.brandingPath.empty())
285
        branding.init(settings.options.brandingPath);
286
    setBrandingDefaults(branding);
287
288
    Dirs::initRootDir();
289
    Dirs::initHomeDir();
290
291
    // Configure logger
292
    if (!settings.options.logFileName.empty())
293
    {
294
        settings.logFileName = settings.options.logFileName;
295
    }
296
    else
297
    {
298
        settings.logFileName = pathJoin(settings.localDataDir,
299
            "manaplus.log");
300
    }
301
    logger->log("Log file: " + settings.logFileName);
302
    logger->setLogFile(settings.logFileName);
303
304
#ifdef USE_FUZZER
305
    Fuzzer::init();
306
#endif  // USE_FUZZER
307
308
    if (settings.options.ipc == true)
309
        IPC::start();
310
    if (settings.options.test.empty())
311
        ConfigManager::backupConfig("config.xml");
312
    ConfigManager::initConfiguration();
313
    SDL::setLogLevel(config.getIntValue("sdlLogLevel"));
314
    settings.init();
315
    Net::loadIgnorePackets();
316
    setPathsDefaults(paths);
317
    initFeatures();
318
    initPaths();
319
    logger->log("init 4");
320
    logger->setDebugLog(config.getBoolValue("debugLog"));
321
    logger->setReportUnimplemented(config.getBoolValue("unimplimentedLog"));
322
323
    config.incValue("runcount");
324
325
#ifndef ANDROID
326
    if (settings.options.test.empty())
327
        ConfigManager::storeSafeParameters();
328
#endif  // ANDROID
329
330
    if (!VirtFs::setWriteDir(settings.localDataDir))
331
    {
332
        logger->error(strprintf("%s couldn't be set as home directory! "
333
            "Exiting.", settings.localDataDir.c_str()));
334
    }
335
336
    GettextHelper::initLang();
337
338
    chatLogger = new ChatLogger;
339
    if (settings.options.chatLogDir.empty())
340
    {
341
        chatLogger->setBaseLogDir(settings.localDataDir
342
            + std::string("/logs/"));
343
    }
344
    else
345
    {
346
        chatLogger->setBaseLogDir(settings.options.chatLogDir);
347
    }
348
349
    // Log the client version
350
    logger->log1(FULL_VERSION);
351
    logger->log("Start configPath: " + config.getConfigPath());
352
353
    Dirs::initScreenshotDir();
354
355
    updateEnv();
356
    SDL::allowScreenSaver(config.getBoolValue("allowscreensaver"));
357
    dumpLibs();
358
    dumpSizes();
359
360
    // Initialize SDL
361
    logger->log1("Initializing SDL...");
362
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
363
    {
364
        logger->safeError(strprintf("Could not initialize SDL: %s",
365
            SDL_GetError()));
366
    }
367
    atexit(SDL_Quit);
368
369
    PacketLimiter::initPacketLimiter();
370
#ifndef USE_SDL2
371
    SDL_EnableUNICODE(1);
372
#endif  // USE_SDL2
373
374
    WindowManager::applyKeyRepeat();
375
    eventsManager.init();
376
    eventsManager.enableEvents();
377
378
#ifdef WIN32
379
    Dirs::mountDataDir();
380
#endif  // WIN32
381
#ifndef USE_SDL2
382
    WindowManager::setIcon();
383
#endif  // USE_SDL2
384
385
    ConfigManager::checkConfigVersion();
386
    logVars();
387
    Cpu::detect();
388
    DyePalette::initFunctions();
389
#if defined(USE_OPENGL)
390
#if !defined(ANDROID) && !defined(__APPLE__) && \
391
    !defined(__native_client__) && !defined(UNITTESTS)
392
    if (!settings.options.safeMode &&
393
        settings.options.renderer < 0 &&
394
        settings.options.test.empty() &&
395
        !settings.options.validate &&
396
        !config.getBoolValue("videodetected"))
397
    {
398
        graphicsManager.detectVideoSettings();
399
    }
400
#endif  // !defined(ANDROID) && !defined(__APPLE__) &&
401
        // !defined(__native_client__) && !defined(UNITTESTS)
402
#endif  // defined(USE_OPENGL)
403
404
    initGraphics();
405
    UserAgent::update();
406
407
    touchManager.init();
408
409
#ifndef WIN32
410
    Dirs::extractDataDir();
411
    Dirs::mountDataDir();
412
#endif  // WIN32
413
414
    Dirs::updateDataPath();
415
416
    // Add the main data directories to our VirtFs search path
417
    if (!settings.options.dataPath.empty())
418
    {
419
        VirtFs::mountDir(settings.options.dataPath,
420
            Append_false);
421
    }
422
423
    // Add the local data directory to VirtFs search path
424
    VirtFs::mountDir(settings.localDataDir,
425
        Append_false);
426
    TranslationManager::loadCurrentLang();
427
    TranslationManager::loadDictionaryLang();
428
#ifdef ENABLE_CUSTOMNLS
429
    TranslationManager::loadGettextLang();
430
#endif  // ENABLE_CUSTOMNLS
431
432
#ifdef USE_SDL2
433
    WindowManager::setIcon();
434
#endif  // USE_SDL2
435
    WindowManager::initTitle();
436
437
    mainGraphics->postInit();
438
439
    theme = new Theme;
440
    Theme::selectSkin();
441
    ActorSprite::load();
442
    touchManager.init();
443
444
    // Initialize the item and emote shortcuts.
445
    for (size_t f = 0; f < SHORTCUT_TABS; f ++)
446
        itemShortcut[f] = new ItemShortcut(f);
447
    emoteShortcut = new EmoteShortcut;
448
    dropShortcut = new DropShortcut;
449
450
    gui = new Gui;
451
    gui->postInit(mainGraphics);
452
    dialogsManager = new DialogsManager;
453
    popupManager = new PopupManager;
454
455
    initSoundManager();
456
457
    // Initialize keyboard
458
    keyboard.init();
459
    inputManager.init();
460
461
    // Initialise player relations
462
    playerRelations.init();
463
    Joystick::init();
464
    WindowManager::createWindows();
465
466
    keyboard.update();
467
    if (joystick != nullptr)
468
        joystick->update();
469
470
    // Initialize default server
471
    mCurrentServer.hostname = settings.options.serverName;
472
    mCurrentServer.port = settings.options.serverPort;
473
    if (!settings.options.serverType.empty())
474
    {
475
        mCurrentServer.type = ServerInfo::parseType(
476
            settings.options.serverType);
477
    }
478
479
    loginData.username = settings.options.username;
480
    loginData.password = settings.options.password;
481
    LoginDialog::savedPassword = settings.options.password;
482
    loginData.remember = (serverConfig.getValue("remember", 1) != 0);
483
    loginData.registerLogin = false;
484
485
    if (mCurrentServer.hostname.empty())
486
    {
487
        mCurrentServer.hostname = branding.getValue("defaultServer", "");
488
        settings.options.serverName = mCurrentServer.hostname;
489
    }
490
491
    if (mCurrentServer.port == 0)
492
    {
493
        mCurrentServer.port = CAST_U16(branding.getValue(
494
            "defaultPort", CAST_S32(DEFAULT_PORT)));
495
        mCurrentServer.type = ServerInfo::parseType(
496
            branding.getValue("defaultServerType", "tmwathena"));
497
    }
498
499
    chatLogger->setServerName(mCurrentServer.hostname);
500
501
    if (loginData.username.empty() && loginData.remember)
502
        loginData.username = serverConfig.getValue("username", "");
503
504
    if (mState != State::ERROR)
505
        mState = State::CHOOSE_SERVER;
506
507
    startTimers();
508
509
    const int fpsLimit = config.getIntValue("fpslimit");
510
    settings.limitFps = fpsLimit > 0;
511
512
    SDL_initFramerate(&fpsManager);
513
    WindowManager::setFramerate(fpsLimit);
514
    initConfigListeners();
515
516
    settings.guiAlpha = config.getFloatValue("guialpha");
517
    optionChanged("fpslimit");
518
519
    start_time = time(nullptr);
520
521
    PlayerInfo::init();
522
523
#ifdef ANDROID
524
#ifndef USE_SDL2
525
    WindowManager::updateScreenKeyboard(SDL_GetScreenKeyboardHeight(nullptr));
526
#endif  // USE_SDL2
527
#endif  // ANDROID
528
529
#ifdef USE_MUMBLE
530
    if (!mumbleManager)
531
        mumbleManager = new MumbleManager;
532
#endif  // USE_MUMBLE
533
534
    mSkin = theme->load("windowmenu.xml", "");
535
    if (mSkin != nullptr)
536
    {
537
        mButtonPadding = mSkin->getPadding();
538
        mButtonSpacing = mSkin->getOption("spacing", 3);
539
    }
540
    if (settings.options.error)
541
        inputManager.executeAction(InputAction::ERROR);
542
543
    if (settings.options.validate == true)
544
        runValidate();
545
}
546
547
1152
Client::~Client()
548
{
549
384
    if (!settings.options.testMode)
550
384
        gameClear();
551
    else
552
        testsClear();
553
    CHECKLISTENERS
554
768
}
555
556
void Client::initConfigListeners()
557
{
558
    config.addListener("fpslimit", this);
559
    config.addListener("guialpha", this);
560
    config.addListener("gamma", this);
561
    config.addListener("enableGamma", this);
562
    config.addListener("particleEmitterSkip", this);
563
    config.addListener("vsync", this);
564
    config.addListener("repeateDelay", this);
565
    config.addListener("repeateInterval", this);
566
    config.addListener("logInput", this);
567
}
568
569
void Client::initSoundManager()
570
{
571
    // Initialize sound engine
572
    try
573
    {
574
        if (config.getBoolValue("sound"))
575
            soundManager.init();
576
577
        soundManager.setSfxVolume(config.getIntValue("sfxVolume"));
578
        soundManager.setMusicVolume(config.getIntValue("musicVolume"));
579
    }
580
    catch (const char *const err)
581
    {
582
        mState = State::ERROR;
583
        errorMessage = err;
584
        logger->log("Warning: %s", err);
585
    }
586
    soundManager.playMusic(branding.getValue(
587
        "loginMusic",
588
        "keprohm.ogg"),
589
        SkipError_true);
590
}
591
592
void Client::initGraphics()
593
{
594
#ifndef USE_SDL2
595
    WindowManager::applyVSync();
596
#endif  // USE_SDL2
597
598
    runCounters = config.getBoolValue("packetcounters");
599
600
    graphicsManager.initGraphics();
601
#ifdef USE_SDL2
602
    WindowManager::applyVSync();
603
#endif  // USE_SDL2
604
605
    imageHelper->postInit();
606
    setConfigDefaults2(config);
607
    WindowManager::applyGrabMode();
608
    WindowManager::applyGamma();
609
610
    mainGraphics->beginDraw();
611
}
612
613
void Client::testsClear()
614
{
615
    if (!settings.options.test.empty())
616
        gameClear();
617
    else
618
        BeingInfo::clear();
619
}
620
621
384
void Client::gameClear()
622
{
623
384
    if (logger != nullptr)
624
384
        logger->log1("Quitting1");
625
384
    isTerminate = true;
626
384
    config.removeListeners(this);
627
628
384
    delete2(assertListener);
629
630
384
    IPC::stop();
631
384
    eventsManager.shutdown();
632
384
    WindowManager::deleteWindows();
633
384
    if (windowContainer != nullptr)
634
248
        windowContainer->slowLogic();
635
636
384
    stopTimers();
637
384
    DbManager::unloadDb();
638
639
384
    if (loginHandler != nullptr)
640
        loginHandler->clearWorlds();
641
642
384
    if (chatHandler != nullptr)
643
        chatHandler->clear();
644
645
384
    if (charServerHandler != nullptr)
646
152
        charServerHandler->clear();
647
648
384
    delete2(ipc);
649
650
#ifdef USE_MUMBLE
651
384
    delete2(mumbleManager);
652
#endif  // USE_MUMBLE
653
654
384
    PlayerInfo::deinit();
655
656
    // Before config.write() since it writes the shortcuts to the config
657
2304
    for (unsigned f = 0; f < SHORTCUT_TABS; f ++)
658
1920
        delete2(itemShortcut[f])
659
384
    delete2(emoteShortcut);
660
384
    delete2(dropShortcut);
661
662
384
    playerRelations.store();
663
664
384
    if (logger != nullptr)
665
384
        logger->log1("Quitting2");
666
667
384
    delete2(mCurrentDialog);
668
384
    delete2(popupManager);
669
384
    delete2(dialogsManager);
670
384
    delete2(gui);
671
672
384
    if (inventoryHandler != nullptr)
673
152
        inventoryHandler->clear();
674
675
384
    if (logger != nullptr)
676
384
        logger->log1("Quitting3");
677
678
384
    touchManager.clear();
679
680
384
    GraphicsManager::deleteRenderers();
681
682
384
    if (logger != nullptr)
683
384
        logger->log1("Quitting4");
684
685
384
    XML::cleanupXML();
686
687
384
    if (logger != nullptr)
688
384
        logger->log1("Quitting5");
689
690
384
    BeingInfo::clear();
691
692
    // Shutdown sound
693
384
    soundManager.close();
694
695
384
    if (logger != nullptr)
696
384
        logger->log1("Quitting6");
697
698
384
    ActorSprite::unload();
699
700
384
    ResourceManager::deleteInstance();
701
702
384
    soundManager.shutdown();
703
704
384
    if (logger != nullptr)
705
384
        logger->log1("Quitting8");
706
707
384
    WindowManager::deleteIcon();
708
709
384
    if (logger != nullptr)
710
384
        logger->log1("Quitting9");
711
712
384
    delete2(joystick);
713
714
384
    keyboard.deinit();
715
716
384
    if (logger != nullptr)
717
384
        logger->log1("Quitting10");
718
719
384
    touchManager.shutdown();
720
721
#ifdef DEBUG_CONFIG
722
    config.enableKeyLogging();
723
#endif  // DEBUG_CONFIG
724
725
384
    config.removeOldKeys();
726
384
    config.write();
727
384
    serverConfig.write();
728
729
384
    config.clear();
730
384
    serverConfig.clear();
731
732
384
    if (logger != nullptr)
733
384
        logger->log1("Quitting11");
734
735
#ifdef USE_PROFILER
736
    Perfomance::clear();
737
#endif  // USE_PROFILER
738
739
#ifdef DEBUG_OPENGL_LEAKS
740
    if (logger)
741
        logger->log("textures left: %d", textures_count);
742
#endif  // DEBUG_OPENGL_LEAKS
743
744
384
    Graphics::cleanUp();
745
746
384
    if (logger != nullptr)
747
384
        logger->log1("Quitting12");
748
749
384
    delete2(chatLogger);
750
384
    TranslationManager::close();
751
384
}
752
753
int Client::testsExec()
754
{
755
#ifdef USE_OPENGL
756
    if (settings.options.test.empty())
757
    {
758
        TestMain test;
759
        return test.exec();
760
    }
761
    else
762
    {
763
        TestLauncher launcher(settings.options.test);
764
        return launcher.exec();
765
    }
766
#else  // USE_OPENGL
767
768
    return 0;
769
#endif  // USE_OPENGL
770
}
771
772
#define ADDBUTTON(var, object) var = object; \
773
    x -= var->getWidth() + mButtonSpacing; \
774
    var->setPosition(x, mButtonPadding); \
775
    top->add(var);
776
777
void Client::stateConnectGame1()
778
{
779
    if ((gameHandler != nullptr) &&
780
        (loginHandler != nullptr) &&
781
        gameHandler->isConnected())
782
    {
783
        loginHandler->disconnect();
784
    }
785
}
786
787
void Client::stateConnectServer1()
788
{
789
    if (mOldState == State::CHOOSE_SERVER)
790
    {
791
        settings.serverName = mCurrentServer.hostname;
792
        ConfigManager::initServerConfig(mCurrentServer.hostname);
793
        PacketLimiter::initPacketLimiter();
794
        initTradeFilter();
795
        Dirs::initUsersDir();
796
        playerRelations.init();
797
798
        // Initialize the item and emote shortcuts.
799
        for (unsigned f = 0; f < SHORTCUT_TABS; f ++)
800
        {
801
            delete itemShortcut[f];
802
            itemShortcut[f] = new ItemShortcut(f);
803
        }
804
        delete emoteShortcut;
805
        emoteShortcut = new EmoteShortcut;
806
807
        // Initialize the drop shortcuts.
808
        delete dropShortcut;
809
        dropShortcut = new DropShortcut;
810
811
        initFeatures();
812
        PlayerInfo::loadData();
813
        loginData.registerUrl = mCurrentServer.registerUrl;
814
        loginData.packetVersion = mCurrentServer.packetVersion;
815
        if (!mCurrentServer.onlineListUrl.empty())
816
            settings.onlineListUrl = mCurrentServer.onlineListUrl;
817
        else
818
            settings.onlineListUrl = settings.serverName;
819
        settings.persistentIp = mCurrentServer.persistentIp;
820
        settings.supportUrl = mCurrentServer.supportUrl;
821
        settings.updateMirrors = mCurrentServer.updateMirrors;
822
        settings.enableRemoteCommands = (serverConfig.getValue(
823
            "enableRemoteCommands", 1) != 0);
824
825
        if (settings.options.username.empty())
826
        {
827
            if (loginData.remember)
828
                loginData.username = serverConfig.getValue("username", "");
829
            else
830
                loginData.username.clear();
831
        }
832
        else
833
        {
834
            loginData.username = settings.options.username;
835
        }
836
        settings.login = loginData.username;
837
        WindowManager::updateTitle();
838
839
        loginData.remember = (serverConfig.getValue("remember", 1) != 0);
840
        Net::connectToServer(mCurrentServer);
841
842
#ifdef USE_MUMBLE
843
        if (mumbleManager)
844
            mumbleManager->setServer(mCurrentServer.hostname);
845
#endif  // USE_MUMBLE
846
847
#ifdef TMWA_SUPPORT
848
        GuildManager::init();
849
#endif  // TMWA_SUPPORT
850
851
        if (!mConfigAutoSaved)
852
        {
853
            mConfigAutoSaved = true;
854
            config.write();
855
        }
856
    }
857
    else if (mOldState != State::CHOOSE_SERVER &&
858
             (loginHandler != nullptr) &&
859
             loginHandler->isConnected())
860
    {
861
        mState = State::PRE_LOGIN;
862
    }
863
}
864
865
void Client::stateWorldSelect1()
866
{
867
    if (mOldState == State::UPDATE &&
868
        (loginHandler != nullptr))
869
    {
870
        if (loginHandler->getWorlds().size() < 2)
871
            mState = State::PRE_LOGIN;
872
    }
873
}
874
875
void Client::stateGame1()
876
{
877
    if (gui == nullptr)
878
        return;
879
880
    BasicContainer2 *const top = static_cast<BasicContainer2*>(
881
        gui->getTop());
882
883
    if (top == nullptr)
884
        return;
885
886
    CREATEWIDGETV(desktop, Desktop, nullptr);
887
    top->add(desktop);
888
    int x = top->getWidth() - mButtonPadding;
889
    ADDBUTTON(mSetupButton, new Button(desktop,
890
        // TRANSLATORS: setup tab quick button
891
        _("Setup"), "Setup", this))
892
    ADDBUTTON(mPerfomanceButton, new Button(desktop,
893
        // TRANSLATORS: perfoamance tab quick button
894
        _("Performance"), "Perfomance", this))
895
    ADDBUTTON(mVideoButton, new Button(desktop,
896
        // TRANSLATORS: video tab quick button
897
        _("Video"), "Video", this))
898
    ADDBUTTON(mThemesButton, new Button(desktop,
899
        // TRANSLATORS: theme tab quick button
900
        _("Theme"), "Themes", this))
901
    ADDBUTTON(mAboutButton, new Button(desktop,
902
        // TRANSLATORS: theme tab quick button
903
        _("About"), "about", this))
904
    ADDBUTTON(mHelpButton, new Button(desktop,
905
        // TRANSLATORS: theme tab quick button
906
        _("Help"), "help", this))
907
#ifdef ANDROID
908
    ADDBUTTON(mCloseButton, new Button(desktop,
909
        // TRANSLATORS: close quick button
910
        _("Close"), "close", this))
911
#endif  // ANDROID
912
913
    desktop->setSize(mainGraphics->getWidth(),
914
        mainGraphics->getHeight());
915
}
916
917
void Client::stateSwitchLogin1()
918
{
919
    if (mOldState == State::GAME &&
920
        (gameHandler != nullptr))
921
    {
922
        gameHandler->disconnect();
923
    }
924
}
925
926
int Client::gameExec()
927
{
928
    int lastTickTime = tick_time;
929
930
    while (mState != State::EXIT)
931
    {
932
        PROFILER_START();
933
        if (eventsManager.handleEvents())
934
            continue;
935
936
        BLOCK_START("Client::gameExec 3")
937
        if (generalHandler != nullptr)
938
            generalHandler->flushNetwork();
939
        BLOCK_END("Client::gameExec 3")
940
941
        BLOCK_START("Client::gameExec 4")
942
        if (gui != nullptr)
943
            gui->logic();
944
        cur_time = time(nullptr);
945
        int k = 0;
946
        while (lastTickTime != tick_time &&
947
               k < 40)
948
        {
949
            if (mGame != nullptr)
950
                mGame->logic();
951
            else if (gui != nullptr)
952
                gui->handleInput();
953
954
            ++lastTickTime;
955
            k ++;
956
        }
957
        soundManager.logic();
958
959
        logic_count += k;
960
        if (gui != nullptr)
961
            gui->slowLogic();
962
        if (mGame != nullptr)
963
            mGame->slowLogic();
964
        slowLogic();
965
        BLOCK_END("Client::gameExec 4")
966
967
        // This is done because at some point tick_time will wrap.
968
        lastTickTime = tick_time;
969
970
        // Update the screen when application is visible, delay otherwise.
971
        if (!WindowManager::getIsMinimized())
972
        {
973
            frame_count++;
974
            if (gui != nullptr)
975
                gui->draw();
976
            mainGraphics->updateScreen();
977
        }
978
        else
979
        {
980
            SDL_Delay(100);
981
        }
982
983
        BLOCK_START("~Client::SDL_framerateDelay")
984
        if (settings.limitFps)
985
            SDL_framerateDelay(&fpsManager);
986
        BLOCK_END("~Client::SDL_framerateDelay")
987
988
        BLOCK_START("Client::gameExec 6")
989
        if (mState == State::CONNECT_GAME)
990
        {
991
            stateConnectGame1();
992
        }
993
        else if (mState == State::CONNECT_SERVER)
994
        {
995
            stateConnectServer1();
996
        }
997
        else if (mState == State::WORLD_SELECT)
998
        {
999
            stateWorldSelect1();
1000
        }
1001
        else if (mOldState == State::START ||
1002
                 (mOldState == State::GAME && mState != State::GAME))
1003
        {
1004
            stateGame1();
1005
        }
1006
        else if (mState == State::SWITCH_LOGIN)
1007
        {
1008
            stateSwitchLogin1();
1009
        }
1010
        BLOCK_END("Client::gameExec 6")
1011
1012
        if (mState != mOldState)
1013
        {
1014
            BLOCK_START("Client::gameExec 7")
1015
            PlayerInfo::stateChange(mState);
1016
1017
            if (mOldState == State::GAME)
1018
            {
1019
                delete2(mGame);
1020
                assertListener = new AssertListener;
1021
                Game::clearInstance();
1022
                ResourceManager::cleanOrphans();
1023
                Party::clearParties();
1024
                Guild::clearGuilds();
1025
                NpcDialog::clearDialogs();
1026
                if (guildHandler != nullptr)
1027
                    guildHandler->clear();
1028
                if (partyHandler != nullptr)
1029
                    partyHandler->clear();
1030
                if (chatLogger != nullptr)
1031
                    chatLogger->clear();
1032
                if (!settings.options.dataPath.empty())
1033
                    UpdaterWindow::unloadMods(settings.options.dataPath);
1034
                else
1035
                    UpdaterWindow::unloadMods(settings.oldUpdates);
1036
                if (!settings.options.skipUpdate)
1037
                    UpdaterWindow::unloadMods(settings.oldUpdates + "/fix/");
1038
            }
1039
            else if (mOldState == State::CHAR_SELECT)
1040
            {
1041
                if (mState != State::CHANGEPASSWORD &&
1042
                    charServerHandler != nullptr)
1043
                {
1044
                    charServerHandler->clear();
1045
                }
1046
            }
1047
1048
            mOldState = mState;
1049
1050
            // Get rid of the dialog of the previous state
1051
            delete2(mCurrentDialog);
1052
1053
            // State has changed, while the quitDialog was active, it might
1054
            // not be correct anymore
1055
            if (mQuitDialog != nullptr)
1056
            {
1057
                mQuitDialog->scheduleDelete();
1058
                mQuitDialog = nullptr;
1059
            }
1060
            BLOCK_END("Client::gameExec 7")
1061
1062
            BLOCK_START("Client::gameExec 8")
1063
            switch (mState)
1064
            {
1065
                case State::CHOOSE_SERVER:
1066
                {
1067
                    BLOCK_START("Client::gameExec STATE_CHOOSE_SERVER")
1068
                    logger->log1("State: CHOOSE SERVER");
1069
                    unloadData();
1070
1071
                    // Allow changing this using a server choice dialog
1072
                    // We show the dialog box only if the command-line
1073
                    // options weren't set.
1074
                    if (settings.options.serverName.empty() &&
1075
                        settings.options.serverPort == 0 &&
1076
                        !branding.getValue("onlineServerList", "a").empty())
1077
                    {
1078
                        // Don't allow an alpha opacity
1079
                        // lower than the default value
1080
                        theme->setMinimumOpacity(0.8F);
1081
1082
                        CREATEWIDGETV(mCurrentDialog, ServerDialog,
1083
                            &mCurrentServer,
1084
                            settings.configDir);
1085
                    }
1086
                    else
1087
                    {
1088
                        mState = State::CONNECT_SERVER;
1089
1090
                        // Reset options so that cancelling or connect
1091
                        // timeout will show the server dialog.
1092
                        settings.options.serverName.clear();
1093
                        settings.options.serverPort = 0;
1094
                    }
1095
                    BLOCK_END("Client::gameExec STATE_CHOOSE_SERVER")
1096
                    break;
1097
                }
1098
1099
                case State::CONNECT_SERVER:
1100
                    BLOCK_START("Client::gameExec State::CONNECT_SERVER")
1101
                    logger->log1("State: CONNECT SERVER");
1102
                    loginData.updateHosts.clear();
1103
                    CREATEWIDGETV(mCurrentDialog, ConnectionDialog,
1104
                        // TRANSLATORS: connection dialog header
1105
                        _("Connecting to server"),
1106
                        State::SWITCH_SERVER);
1107
                    TranslationManager::loadCurrentLang();
1108
                    TranslationManager::loadDictionaryLang();
1109
                    BLOCK_END("Client::gameExec State::CONNECT_SERVER")
1110
                    break;
1111
1112
                case State::PRE_LOGIN:
1113
                    logger->log1("State: PRE_LOGIN");
1114
                    break;
1115
1116
                case State::LOGIN:
1117
                    BLOCK_START("Client::gameExec State::LOGIN")
1118
                    logger->log1("State: LOGIN");
1119
                    // Don't allow an alpha opacity
1120
                    // lower than the default value
1121
                    theme->setMinimumOpacity(0.8F);
1122
1123
                    if (packetVersion == 0)
1124
                    {
1125
                        packetVersion = loginData.packetVersion;
1126
                        if (packetVersion != 0)
1127
                        {
1128
                            loginHandler->updatePacketVersion();
1129
                            logger->log("Preconfigured packet version: %d",
1130
                                packetVersion);
1131
                        }
1132
                    }
1133
1134
                    loginData.updateType = static_cast<UpdateTypeT>(
1135
                        serverConfig.getValue("updateType", 0));
1136
1137
                    mSearchHash = Net::Download::adlerBuffer(
1138
                        const_cast<char*>(mCurrentServer.hostname.c_str()),
1139
                        CAST_S32(mCurrentServer.hostname.size()));
1140
                    if (settings.options.username.empty() ||
1141
                        settings.options.password.empty())
1142
                    {
1143
                        CREATEWIDGETV(mCurrentDialog, LoginDialog,
1144
                            loginData,
1145
                            &mCurrentServer,
1146
                            &settings.options.updateHost);
1147
                    }
1148
                    else
1149
                    {
1150
                        mState = State::LOGIN_ATTEMPT;
1151
                        // Clear the password so that when login fails, the
1152
                        // dialog will show up next time.
1153
                        settings.options.password.clear();
1154
                    }
1155
                    BLOCK_END("Client::gameExec State::LOGIN")
1156
                    break;
1157
1158
                case State::LOGIN_ATTEMPT:
1159
                    BLOCK_START("Client::gameExec State::LOGIN_ATTEMPT")
1160
                    logger->log1("State: LOGIN ATTEMPT");
1161
                    CREATEWIDGETV(mCurrentDialog, ConnectionDialog,
1162
                        // TRANSLATORS: connection dialog header
1163
                        _("Logging in"),
1164
                        State::SWITCH_SERVER);
1165
                    if (loginHandler != nullptr)
1166
                        loginHandler->loginOrRegister(&loginData);
1167
                    BLOCK_END("Client::gameExec State::LOGIN_ATTEMPT")
1168
                    break;
1169
1170
                case State::WORLD_SELECT:
1171
                    BLOCK_START("Client::gameExec State::WORLD_SELECT")
1172
                    logger->log1("State: WORLD SELECT");
1173
                    {
1174
                        TranslationManager::loadCurrentLang();
1175
                        TranslationManager::loadDictionaryLang();
1176
                        if (loginHandler == nullptr)
1177
                        {
1178
                            BLOCK_END("Client::gameExec State::WORLD_SELECT")
1179
                            break;
1180
                        }
1181
                        Worlds worlds = loginHandler->getWorlds();
1182
1183
                        if (worlds.empty())
1184
                        {
1185
                            // Trust that the netcode knows what it's doing
1186
                            mState = State::UPDATE;
1187
                        }
1188
                        else if (worlds.size() == 1)
1189
                        {
1190
                            loginHandler->chooseServer(
1191
                                0, mCurrentServer.persistentIp);
1192
                            mState = State::UPDATE;
1193
                        }
1194
                        else
1195
                        {
1196
                            CREATEWIDGETV(mCurrentDialog, WorldSelectDialog,
1197
                                worlds);
1198
                            if (settings.options.chooseDefault)
1199
                            {
1200
                                static_cast<WorldSelectDialog*>(mCurrentDialog)
1201
                                    ->action(ActionEvent(nullptr, "ok"));
1202
                            }
1203
                        }
1204
                    }
1205
                    BLOCK_END("Client::gameExec State::WORLD_SELECT")
1206
                    break;
1207
1208
                case State::WORLD_SELECT_ATTEMPT:
1209
                    BLOCK_START("Client::gameExec State::WORLD_SELECT_ATTEMPT")
1210
                    logger->log1("State: WORLD SELECT ATTEMPT");
1211
                    CREATEWIDGETV(mCurrentDialog, ConnectionDialog,
1212
                        // TRANSLATORS: connection dialog header
1213
                        _("Entering game world"),
1214
                        State::WORLD_SELECT);
1215
                    BLOCK_END("Client::gameExec State::WORLD_SELECT_ATTEMPT")
1216
                    break;
1217
1218
                case State::UPDATE:
1219
                    BLOCK_START("Client::gameExec State::UPDATE")
1220
                    logger->log1("State: UPDATE");
1221
1222
                    // Determine which source to use for the update host
1223
                    if (!settings.options.updateHost.empty())
1224
                        settings.updateHost = settings.options.updateHost;
1225
                    else
1226
                        settings.updateHost = loginData.updateHost;
1227
                    Dirs::initUpdatesDir();
1228
1229
                    if (!settings.oldUpdates.empty())
1230
                        UpdaterWindow::unloadUpdates(settings.oldUpdates);
1231
1232
                    if (settings.options.skipUpdate)
1233
                    {
1234
                        mState = State::LOAD_DATA;
1235
                        settings.oldUpdates.clear();
1236
                        UpdaterWindow::loadDirMods(settings.options.dataPath);
1237
                    }
1238
                    else if ((loginData.updateType & UpdateType::Skip) != 0)
1239
                    {
1240
                        settings.oldUpdates = pathJoin(settings.localDataDir,
1241
                            settings.updatesDir);
1242
                        UpdaterWindow::loadLocalUpdates(settings.oldUpdates);
1243
                        mState = State::LOAD_DATA;
1244
                    }
1245
                    else
1246
                    {
1247
                        settings.oldUpdates = pathJoin(settings.localDataDir,
1248
                            settings.updatesDir);
1249
                        CREATEWIDGETV(mCurrentDialog, UpdaterWindow,
1250
                            settings.updateHost,
1251
                            settings.oldUpdates,
1252
                            settings.options.dataPath.empty(),
1253
                            loginData.updateType);
1254
                    }
1255
                    BLOCK_END("Client::gameExec State::UPDATE")
1256
                    break;
1257
1258
                case State::LOAD_DATA:
1259
                {
1260
                    BLOCK_START("Client::gameExec State::LOAD_DATA")
1261
                    logger->log1("State: LOAD DATA");
1262
1263
                    loadData();
1264
1265
                    mState = State::GET_CHARACTERS;
1266
                    BLOCK_END("Client::gameExec State::LOAD_DATA")
1267
                    break;
1268
                }
1269
                case State::GET_CHARACTERS:
1270
                    BLOCK_START("Client::gameExec State::GET_CHARACTERS")
1271
                    logger->log1("State: GET CHARACTERS");
1272
                    CREATEWIDGETV(mCurrentDialog, ConnectionDialog,
1273
                        // TRANSLATORS: connection dialog header
1274
                        _("Requesting characters"),
1275
                        State::SWITCH_SERVER);
1276
                    if (charServerHandler != nullptr)
1277
                        charServerHandler->requestCharacters();
1278
                    BLOCK_END("Client::gameExec State::GET_CHARACTERS")
1279
                    break;
1280
1281
                case State::CHAR_SELECT:
1282
                    BLOCK_START("Client::gameExec State::CHAR_SELECT")
1283
                    logger->log1("State: CHAR SELECT");
1284
                    // Don't allow an alpha opacity
1285
                    // lower than the default value
1286
                    theme->setMinimumOpacity(0.8F);
1287
1288
                    settings.login = loginData.username;
1289
                    WindowManager::updateTitle();
1290
1291
                    CREATEWIDGETV(mCurrentDialog, CharSelectDialog,
1292
                        loginData);
1293
1294
                    if (!(static_cast<CharSelectDialog*>(mCurrentDialog))
1295
                        ->selectByName(settings.options.character,
1296
                        CharSelectDialog::Choose))
1297
                    {
1298
                        (static_cast<CharSelectDialog*>(mCurrentDialog))
1299
                            ->selectByName(
1300
                            serverConfig.getValue("lastCharacter", ""),
1301
                            settings.options.chooseDefault ?
1302
                            CharSelectDialog::Choose :
1303
                            CharSelectDialog::Focus);
1304
                    }
1305
1306
                    // Choosing character on the command line should work only
1307
                    // once, clear it so that 'switch character' works.
1308
                    settings.options.character.clear();
1309
                    BLOCK_END("Client::gameExec State::CHAR_SELECT")
1310
                    break;
1311
1312
                case State::CONNECT_GAME:
1313
                    BLOCK_START("Client::gameExec State::CONNECT_GAME")
1314
                    logger->log1("State: CONNECT GAME");
1315
                    CREATEWIDGETV(mCurrentDialog, ConnectionDialog,
1316
                        // TRANSLATORS: connection dialog header
1317
                        _("Connecting to the game server"),
1318
                        State::CHOOSE_SERVER);
1319
                    if (gameHandler != nullptr)
1320
                        gameHandler->connect();
1321
                    BLOCK_END("Client::gameExec State::CONNECT_GAME")
1322
                    break;
1323
1324
                case State::CHANGE_MAP:
1325
                    BLOCK_START("Client::gameExec State::CHANGE_MAP")
1326
                    logger->log1("State: CHANGE_MAP");
1327
                    CREATEWIDGETV(mCurrentDialog, ConnectionDialog,
1328
                        // TRANSLATORS: connection dialog header
1329
                        _("Changing game servers"),
1330
                        State::SWITCH_CHARACTER);
1331
                    if (gameHandler != nullptr)
1332
                        gameHandler->connect();
1333
                    BLOCK_END("Client::gameExec State::CHANGE_MAP")
1334
                    break;
1335
1336
                case State::GAME:
1337
                    BLOCK_START("Client::gameExec State::GAME")
1338
                    if (localPlayer != nullptr)
1339
                    {
1340
                        logger->log("Memorizing selected character %s",
1341
                            localPlayer->getName().c_str());
1342
                        serverConfig.setValue("lastCharacter",
1343
                            localPlayer->getName());
1344
#ifdef USE_MUMBLE
1345
                        if (mumbleManager)
1346
                            mumbleManager->setPlayer(localPlayer->getName());
1347
#endif  // USE_MUMBLE
1348
                    }
1349
1350
                    // Fade out logon-music here too to give the desired effect
1351
                    // of "flowing" into the game.
1352
                    soundManager.fadeOutMusic(1000);
1353
1354
                    // Allow any alpha opacity
1355
                    theme->setMinimumOpacity(-1.0F);
1356
1357
                    if (chatLogger != nullptr)
1358
                        chatLogger->setServerName(settings.serverName);
1359
1360
#ifdef ANDROID
1361
                    delete2(mCloseButton);
1362
#endif  // ANDROID
1363
1364
                    delete2(mSetupButton);
1365
                    delete2(mVideoButton);
1366
                    delete2(mThemesButton);
1367
                    delete2(mAboutButton);
1368
                    delete2(mHelpButton);
1369
                    delete2(mPerfomanceButton);
1370
                    delete2(desktop);
1371
1372
                    mCurrentDialog = nullptr;
1373
1374
                    logger->log1("State: GAME");
1375
                    if (generalHandler != nullptr)
1376
                        generalHandler->reloadPartially();
1377
                    mGame = new Game;
1378
                    BLOCK_END("Client::gameExec State::GAME")
1379
                    break;
1380
1381
                case State::LOGIN_ERROR:
1382
                    BLOCK_START("Client::gameExec State::LOGIN_ERROR")
1383
                    logger->log1("State: LOGIN ERROR");
1384
                    CREATEWIDGETV(mCurrentDialog, OkDialog,
1385
                        // TRANSLATORS: error dialog header
1386
                        _("Error"),
1387
                        errorMessage,
1388
                        // TRANSLATORS: ok dialog button
1389
                        _("Close"),
1390
                        DialogType::ERROR,
1391
                        Modal_true,
1392
                        ShowCenter_true,
1393
                        nullptr,
1394
                        260);
1395
                    mCurrentDialog->addActionListener(&loginListener);
1396
                    mCurrentDialog = nullptr;  // OkDialog deletes itself
1397
                    BLOCK_END("Client::gameExec State::LOGIN_ERROR")
1398
                    break;
1399
1400
                case State::ACCOUNTCHANGE_ERROR:
1401
                    BLOCK_START("Client::gameExec State::ACCOUNTCHANGE_ERROR")
1402
                    logger->log1("State: ACCOUNT CHANGE ERROR");
1403
                    CREATEWIDGETV(mCurrentDialog, OkDialog,
1404
                        // TRANSLATORS: error dialog header
1405
                        _("Error"),
1406
                        errorMessage,
1407
                        // TRANSLATORS: ok dialog button
1408
                        _("Close"),
1409
                        DialogType::ERROR,
1410
                        Modal_true,
1411
                        ShowCenter_true,
1412
                        nullptr,
1413
                        260);
1414
                    mCurrentDialog->addActionListener(&accountListener);
1415
                    mCurrentDialog = nullptr;  // OkDialog deletes itself
1416
                    BLOCK_END("Client::gameExec State::ACCOUNTCHANGE_ERROR")
1417
                    break;
1418
1419
                case State::REGISTER_PREP:
1420
                    BLOCK_START("Client::gameExec State::REGISTER_PREP")
1421
                    logger->log1("State: REGISTER_PREP");
1422
                    CREATEWIDGETV(mCurrentDialog, ConnectionDialog,
1423
                        // TRANSLATORS: connection dialog header
1424
                        _("Requesting registration details"),
1425
                        State::LOGIN);
1426
                    loginHandler->getRegistrationDetails();
1427
                    BLOCK_END("Client::gameExec State::REGISTER_PREP")
1428
                    break;
1429
1430
                case State::REGISTER:
1431
                    logger->log1("State: REGISTER");
1432
                    CREATEWIDGETV(mCurrentDialog, RegisterDialog,
1433
                        loginData);
1434
                    break;
1435
1436
                case State::REGISTER_ATTEMPT:
1437
                    BLOCK_START("Client::gameExec State::REGISTER_ATTEMPT")
1438
                    logger->log("Username is %s", loginData.username.c_str());
1439
                    if (loginHandler != nullptr)
1440
                        loginHandler->registerAccount(&loginData);
1441
                    BLOCK_END("Client::gameExec State::REGISTER_ATTEMPT")
1442
                    break;
1443
1444
                case State::CHANGEPASSWORD:
1445
                    BLOCK_START("Client::gameExec State::CHANGEPASSWORD")
1446
                    logger->log1("State: CHANGE PASSWORD");
1447
                    CREATEWIDGETV(mCurrentDialog, ChangePasswordDialog,
1448
                        loginData);
1449
                    mCurrentDialog->setVisible(Visible_true);
1450
                    BLOCK_END("Client::gameExec State::CHANGEPASSWORD")
1451
                    break;
1452
1453
                case State::CHANGEPASSWORD_ATTEMPT:
1454
                    BLOCK_START("Client::gameExec "
1455
                        "State::CHANGEPASSWORD_ATTEMPT")
1456
                    logger->log1("State: CHANGE PASSWORD ATTEMPT");
1457
                    if (loginHandler != nullptr)
1458
                    {
1459
                        loginHandler->changePassword(loginData.password,
1460
                            loginData.newPassword);
1461
                    }
1462
                    BLOCK_END("Client::gameExec State::CHANGEPASSWORD_ATTEMPT")
1463
                    break;
1464
1465
                case State::CHANGEPASSWORD_SUCCESS:
1466
                    BLOCK_START("Client::gameExec "
1467
                        "State::CHANGEPASSWORD_SUCCESS")
1468
                    logger->log1("State: CHANGE PASSWORD SUCCESS");
1469
                    CREATEWIDGETV(mCurrentDialog, OkDialog,
1470
                        // TRANSLATORS: password change message header
1471
                        _("Password Change"),
1472
                        // TRANSLATORS: password change message text
1473
                        _("Password changed successfully!"),
1474
                        // TRANSLATORS: ok dialog button
1475
                        _("OK"),
1476
                        DialogType::ERROR,
1477
                        Modal_true,
1478
                        ShowCenter_true,
1479
                        nullptr,
1480
                        260);
1481
                    mCurrentDialog->addActionListener(&accountListener);
1482
                    mCurrentDialog = nullptr;  // OkDialog deletes itself
1483
                    loginData.password = loginData.newPassword;
1484
                    loginData.newPassword.clear();
1485
                    BLOCK_END("Client::gameExec State::CHANGEPASSWORD_SUCCESS")
1486
                    break;
1487
1488
                case State::CHANGEEMAIL:
1489
                    logger->log1("State: CHANGE EMAIL");
1490
                    CREATEWIDGETV(mCurrentDialog,
1491
                        ChangeEmailDialog,
1492
                        loginData);
1493
                    mCurrentDialog->setVisible(Visible_true);
1494
                    break;
1495
1496
                case State::CHANGEEMAIL_ATTEMPT:
1497
                    logger->log1("State: CHANGE EMAIL ATTEMPT");
1498
                    if (loginHandler != nullptr)
1499
                        loginHandler->changeEmail(loginData.email);
1500
                    break;
1501
1502
                case State::CHANGEEMAIL_SUCCESS:
1503
                    logger->log1("State: CHANGE EMAIL SUCCESS");
1504
                    CREATEWIDGETV(mCurrentDialog, OkDialog,
1505
                        // TRANSLATORS: email change message header
1506
                        _("Email Change"),
1507
                        // TRANSLATORS: email change message text
1508
                        _("Email changed successfully!"),
1509
                        // TRANSLATORS: ok dialog button
1510
                        _("OK"),
1511
                        DialogType::ERROR,
1512
                        Modal_true,
1513
                        ShowCenter_true,
1514
                        nullptr,
1515
                        260);
1516
                    mCurrentDialog->addActionListener(&accountListener);
1517
                    mCurrentDialog = nullptr;  // OkDialog deletes itself
1518
                    break;
1519
1520
                case State::SWITCH_SERVER:
1521
                    BLOCK_START("Client::gameExec State::SWITCH_SERVER")
1522
                    logger->log1("State: SWITCH SERVER");
1523
1524
                    if (loginHandler != nullptr)
1525
                        loginHandler->disconnect();
1526
                    if (gameHandler != nullptr)
1527
                    {
1528
                        gameHandler->disconnect();
1529
                        gameHandler->clear();
1530
                    }
1531
                    settings.serverName.clear();
1532
                    settings.login.clear();
1533
                    WindowManager::updateTitle();
1534
                    serverConfig.write();
1535
                    serverConfig.unload();
1536
                    if (setupWindow != nullptr)
1537
                        setupWindow->externalUnload();
1538
1539
                    mState = State::CHOOSE_SERVER;
1540
                    BLOCK_END("Client::gameExec State::SWITCH_SERVER")
1541
                    break;
1542
1543
                case State::SWITCH_LOGIN:
1544
                    BLOCK_START("Client::gameExec State::SWITCH_LOGIN")
1545
                    logger->log1("State: SWITCH LOGIN");
1546
1547
                    if (loginHandler != nullptr)
1548
                    {
1549
                        loginHandler->logout();
1550
                        loginHandler->disconnect();
1551
                    }
1552
                    if (gameHandler != nullptr)
1553
                        gameHandler->disconnect();
1554
                    if (loginHandler != nullptr)
1555
                        loginHandler->connect();
1556
1557
                    settings.login.clear();
1558
                    WindowManager::updateTitle();
1559
                    mState = State::LOGIN;
1560
                    BLOCK_END("Client::gameExec State::SWITCH_LOGIN")
1561
                    break;
1562
1563
                case State::SWITCH_CHARACTER:
1564
                    BLOCK_START("Client::gameExec State::SWITCH_CHARACTER")
1565
                    logger->log1("State: SWITCH CHARACTER");
1566
1567
                    // Done with game
1568
                    if (gameHandler != nullptr)
1569
                        gameHandler->disconnect();
1570
1571
                    settings.login.clear();
1572
                    WindowManager::updateTitle();
1573
                    mState = State::GET_CHARACTERS;
1574
                    BLOCK_END("Client::gameExec State::SWITCH_CHARACTER")
1575
                    break;
1576
1577
                case State::LOGOUT_ATTEMPT:
1578
                    logger->log1("State: LOGOUT ATTEMPT");
1579
                    break;
1580
1581
                case State::WAIT:
1582
                    logger->log1("State: WAIT");
1583
                    break;
1584
1585
                case State::EXIT:
1586
                    BLOCK_START("Client::gameExec State::EXIT")
1587
                    logger->log1("State: EXIT");
1588
                    Net::unload();
1589
                    BLOCK_END("Client::gameExec State::EXIT")
1590
                    break;
1591
1592
                case State::FORCE_QUIT:
1593
                    BLOCK_START("Client::gameExec State::FORCE_QUIT")
1594
                    logger->log1("State: FORCE QUIT");
1595
                    if (generalHandler != nullptr)
1596
                        generalHandler->unload();
1597
                    mState = State::EXIT;
1598
                    BLOCK_END("Client::gameExec State::FORCE_QUIT")
1599
                  break;
1600
1601
                case State::ERROR:
1602
                    BLOCK_START("Client::gameExec State::ERROR")
1603
                    config.write();
1604
                    if (mOldState == State::GAME)
1605
                        serverConfig.write();
1606
                    logger->log1("State: ERROR");
1607
                    logger->log("Error: %s\n", errorMessage.c_str());
1608
                    mCurrentDialog = DialogsManager::openErrorDialog(
1609
                        // TRANSLATORS: error message header
1610
                        _("Error"),
1611
                        errorMessage,
1612
                        Modal_true);
1613
                    mCurrentDialog->addActionListener(&errorListener);
1614
                    mCurrentDialog = nullptr;  // OkDialog deletes itself
1615
                    gameHandler->disconnect();
1616
                    BLOCK_END("Client::gameExec State::ERROR")
1617
                    break;
1618
1619
                case State::AUTORECONNECT_SERVER:
1620
                    // ++++++
1621
                    break;
1622
1623
                case State::START:
1624
                default:
1625
                    mState = State::FORCE_QUIT;
1626
                    break;
1627
            }
1628
            BLOCK_END("Client::gameExec 8")
1629
        }
1630
        PROFILER_END();
1631
    }
1632
1633
    return 0;
1634
}
1635
1636
void Client::optionChanged(const std::string &name)
1637
{
1638
    if (name == "fpslimit")
1639
    {
1640
        const int fpsLimit = config.getIntValue("fpslimit");
1641
        settings.limitFps = fpsLimit > 0;
1642
        WindowManager::setFramerate(fpsLimit);
1643
    }
1644
    else if (name == "guialpha" ||
1645
             name == "enableGuiOpacity")
1646
    {
1647
        const float alpha = config.getFloatValue("guialpha");
1648
        settings.guiAlpha = alpha;
1649
        ImageHelper::setEnableAlpha(alpha != 1.0F &&
1650
            config.getBoolValue("enableGuiOpacity"));
1651
    }
1652
    else if (name == "gamma" ||
1653
             name == "enableGamma")
1654
    {
1655
        WindowManager::applyGamma();
1656
    }
1657
    else if (name == "particleEmitterSkip")
1658
    {
1659
        ParticleEngine::emitterSkip =
1660
            config.getIntValue("particleEmitterSkip") + 1;
1661
    }
1662
    else if (name == "vsync")
1663
    {
1664
        WindowManager::applyVSync();
1665
    }
1666
    else if (name == "repeateInterval" ||
1667
             name == "repeateDelay")
1668
    {
1669
        WindowManager::applyKeyRepeat();
1670
    }
1671
}
1672
1673
void Client::action(const ActionEvent &event)
1674
{
1675
    std::string tab;
1676
    const std::string &eventId = event.getId();
1677
1678
    if (eventId == "close")
1679
    {
1680
        setState(State::FORCE_QUIT);
1681
        return;
1682
    }
1683
    if (eventId == "Setup")
1684
    {
1685
        tab.clear();
1686
    }
1687
    else if (eventId == "help")
1688
    {
1689
        inputManager.executeAction(InputAction::WINDOW_HELP);
1690
        return;
1691
    }
1692
    else if (eventId == "about")
1693
    {
1694
        inputManager.executeAction(InputAction::WINDOW_ABOUT);
1695
        return;
1696
    }
1697
    else if (eventId == "Video")
1698
    {
1699
        tab = "Video";
1700
    }
1701
    else if (eventId == "Themes")
1702
    {
1703
        tab = "Theme";
1704
    }
1705
    else if (eventId == "Perfomance")
1706
    {
1707
        tab = "Perfomance";
1708
    }
1709
    else
1710
    {
1711
        return;
1712
    }
1713
1714
    if (setupWindow != nullptr)
1715
    {
1716
        setupWindow->setVisible(fromBool(
1717
            !setupWindow->isWindowVisible(), Visible));
1718
        if (setupWindow->isWindowVisible())
1719
        {
1720
            if (!tab.empty())
1721
                setupWindow->activateTab(tab);
1722
            setupWindow->requestMoveToTop();
1723
        }
1724
    }
1725
}
1726
1727
void Client::initFeatures()
1728
{
1729
    features.init(paths.getStringValue("featuresFile"),
1730
        UseVirtFs_true,
1731
        SkipError_true);
1732
    setFeaturesDefaults(features);
1733
    settings.fixDeadAnimation = features.getBoolValue("fixDeadAnimation");
1734
}
1735
1736
void Client::initPaths()
1737
{
1738
    settings.gmCommandSymbol = paths.getStringValue("gmCommandSymbol");
1739
    settings.gmCharCommandSymbol = paths.getStringValue("gmCharCommandSymbol");
1740
    settings.linkCommandSymbol = paths.getStringValue("linkCommandSymbol");
1741
    if (settings.linkCommandSymbol.empty())
1742
        settings.linkCommandSymbol = "=";
1743
    settings.overweightPercent = paths.getIntValue("overweightPercent");
1744
    settings.playerNameOffset = paths.getIntValue(
1745
        "playerNameOffset");
1746
    settings.playerBadgeAtRightOffset = paths.getIntValue(
1747
        "playerBadgeAtRightOffset");
1748
    settings.unknownSkillsAutoTab = paths.getBoolValue("unknownSkillsAutoTab");
1749
    settings.enableNewMailSystem = paths.getBoolValue("enableNewMailSystem");
1750
}
1751
1752
void Client::initTradeFilter()
1753
{
1754
    const std::string tradeListName =
1755
        settings.serverConfigDir + "/tradefilter.txt";
1756
1757
    std::ofstream tradeFile;
1758
    struct stat statbuf;
1759
1760
    if ((stat(tradeListName.c_str(), &statbuf) != 0) ||
1761
        !S_ISREG(statbuf.st_mode))
1762
    {
1763
        tradeFile.open(tradeListName.c_str(),
1764
            std::ios::out);
1765
        if (tradeFile.is_open())
1766
        {
1767
            tradeFile << ": sell" << std::endl;
1768
            tradeFile << ": buy" << std::endl;
1769
            tradeFile << ": trade" << std::endl;
1770
            tradeFile << "i sell" << std::endl;
1771
            tradeFile << "i buy" << std::endl;
1772
            tradeFile << "i trade" << std::endl;
1773
            tradeFile << "i trading" << std::endl;
1774
            tradeFile << "i am buy" << std::endl;
1775
            tradeFile << "i am sell" << std::endl;
1776
            tradeFile << "i am trade" << std::endl;
1777
            tradeFile << "i am trading" << std::endl;
1778
            tradeFile << "i'm buy" << std::endl;
1779
            tradeFile << "i'm sell" << std::endl;
1780
            tradeFile << "i'm trade" << std::endl;
1781
            tradeFile << "i'm trading" << std::endl;
1782
        }
1783
        else
1784
        {
1785
            reportAlways("Error opening file for writing: %s",
1786
                tradeListName.c_str());
1787
        }
1788
        tradeFile.close();
1789
    }
1790
}
1791
1792
152
bool Client::isTmw()
1793
{
1794
152
    const std::string &name = settings.serverName;
1795
304
    if (name == "server.themanaworld.org" ||
1796

456
        name == "themanaworld.org" ||
1797
152
        name == "167.114.129.72")
1798
    {
1799
        return true;
1800
    }
1801
    return false;
1802
}
1803
1804
void Client::moveButtons(const int width)
1805
{
1806
    if (mSetupButton != nullptr)
1807
    {
1808
        int x = width - mSetupButton->getWidth() - mButtonPadding;
1809
        mSetupButton->setPosition(x, mButtonPadding);
1810
#ifndef WIN32
1811
        x -= mPerfomanceButton->getWidth() + mButtonSpacing;
1812
        mPerfomanceButton->setPosition(x, mButtonPadding);
1813
1814
        x -= mVideoButton->getWidth() + mButtonSpacing;
1815
        mVideoButton->setPosition(x, mButtonPadding);
1816
1817
        x -= mThemesButton->getWidth() + mButtonSpacing;
1818
        mThemesButton->setPosition(x, mButtonPadding);
1819
1820
        x -= mAboutButton->getWidth() + mButtonSpacing;
1821
        mAboutButton->setPosition(x, mButtonPadding);
1822
1823
        x -= mHelpButton->getWidth() + mButtonSpacing;
1824
        mHelpButton->setPosition(x, mButtonPadding);
1825
#ifdef ANDROID
1826
        x -= mCloseButton->getWidth() + mButtonSpacing;
1827
        mCloseButton->setPosition(x, mButtonPadding);
1828
#endif  // ANDROID
1829
#endif  // WIN32
1830
    }
1831
}
1832
1833
124
void Client::windowRemoved(const Window *const window)
1834
{
1835
124
    if (mCurrentDialog == window)
1836
        mCurrentDialog = nullptr;
1837
124
}
1838
1839
void Client::logVars()
1840
{
1841
#ifdef ANDROID
1842
    logger->log("APPDIR: %s", getenv("APPDIR"));
1843
    logger->log("DATADIR2: %s", getSdStoragePath().c_str());
1844
#endif  // ANDROID
1845
}
1846
1847
void Client::slowLogic()
1848
{
1849
    if ((gameHandler == nullptr) ||
1850
        !gameHandler->mustPing())
1851
    {
1852
        return;
1853
    }
1854
1855
    if (get_elapsed_time1(mPing) > 1500)
1856
    {
1857
        mPing = tick_time;
1858
        if (mState == State::UPDATE ||
1859
            mState == State::LOGIN ||
1860
            mState == State::LOGIN_ATTEMPT ||
1861
            mState == State::REGISTER ||
1862
            mState == State::REGISTER_ATTEMPT)
1863
        {
1864
            if (loginHandler != nullptr)
1865
                loginHandler->ping();
1866
            if (generalHandler != nullptr)
1867
                generalHandler->flushSend();
1868
        }
1869
        else if (mState == State::CHAR_SELECT)
1870
        {
1871
            if (charServerHandler != nullptr)
1872
                charServerHandler->ping();
1873
            if (generalHandler != nullptr)
1874
                generalHandler->flushSend();
1875
        }
1876
    }
1877
}
1878
1879
void Client::loadData()
1880
{
1881
    // If another data path has been set,
1882
    // we don't load any other files...
1883
    if (settings.options.dataPath.empty())
1884
    {
1885
        // Add customdata directory
1886
        VirtFs::searchAndAddArchives(
1887
            "customdata/",
1888
            "zip",
1889
            Append_false);
1890
    }
1891
1892
    if (!settings.options.skipUpdate)
1893
    {
1894
        VirtFs::searchAndAddArchives(
1895
            settings.updatesDir + "/local/",
1896
            "zip",
1897
            Append_false);
1898
1899
        VirtFs::mountDir(pathJoin(
1900
            settings.localDataDir,
1901
            settings.updatesDir,
1902
            "local/"),
1903
            Append_false);
1904
    }
1905
1906
    logger->log("Init paths");
1907
    paths.init("paths.xml", UseVirtFs_true);
1908
    setPathsDefaults(paths);
1909
    initPaths();
1910
    if (SpriteReference::Empty == nullptr)
1911
    {
1912
        SpriteReference::Empty = new SpriteReference(
1913
            paths.getStringValue("spriteErrorFile"),
1914
            0);
1915
    }
1916
1917
    if (BeingInfo::unknown == nullptr)
1918
        BeingInfo::unknown = new BeingInfo;
1919
1920
    initFeatures();
1921
    TranslationManager::loadCurrentLang();
1922
    TranslationManager::loadDictionaryLang();
1923
    PlayerInfo::stateChange(mState);
1924
1925
    AttributesEnum::init();
1926
    DbManager::loadDb();
1927
1928
    delete spellManager;
1929
    spellManager = new SpellManager;
1930
    delete spellShortcut;
1931
    spellShortcut = new SpellShortcut;
1932
1933
    EquipmentWindow::prepareSlotNames();
1934
1935
    ActorSprite::load();
1936
1937
    if (desktop != nullptr)
1938
        desktop->reloadWallpaper();
1939
}
1940
1941
void Client::unloadData()
1942
{
1943
    DbManager::unloadDb();
1944
    mCurrentServer.supportUrl.clear();
1945
    settings.supportUrl.clear();
1946
    if (settings.options.dataPath.empty())
1947
    {
1948
        // Add customdata directory
1949
        VirtFs::searchAndRemoveArchives(
1950
            "customdata/",
1951
            "zip");
1952
    }
1953
1954
    if (!settings.oldUpdates.empty())
1955
    {
1956
        UpdaterWindow::unloadUpdates(settings.oldUpdates);
1957
        settings.oldUpdates.clear();
1958
    }
1959
1960
    if (!settings.options.skipUpdate)
1961
    {
1962
        VirtFs::searchAndRemoveArchives(
1963
            pathJoin(settings.updatesDir, "local/"),
1964
            "zip");
1965
1966
        VirtFs::unmountDirSilent(pathJoin(
1967
            settings.localDataDir,
1968
            settings.updatesDir,
1969
            "local/"));
1970
    }
1971
1972
    ResourceManager::clearCache();
1973
1974
    loginData.clearUpdateHost();
1975
    serverVersion = 0;
1976
    packetVersion = 0;
1977
    tmwServerVersion = 0;
1978
    evolPacketOffset = 0;
1979
}
1980
1981
void Client::runValidate()
1982
{
1983
    loadData();
1984
    WindowManager::createValidateWindows();
1985
1986
    WindowManager::deleteValidateWindows();
1987
    unloadData();
1988
    delete2(client);
1989
    VirtFs::deinit();
1990
    exit(0);
1991

6
}