diff options
Diffstat (limited to 'src/progs/manaverse/client.cpp')
-rw-r--r-- | src/progs/manaverse/client.cpp | 2090 |
1 files changed, 2090 insertions, 0 deletions
diff --git a/src/progs/manaverse/client.cpp b/src/progs/manaverse/client.cpp new file mode 100644 index 000000000..b13fa9f20 --- /dev/null +++ b/src/progs/manaverse/client.cpp @@ -0,0 +1,2090 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "progs/manaverse/client.h" + +#include "chatlogger.h" +#include "configmanager.h" +#include "dirs.h" +#include "eventsmanager.h" +#include "game.h" +#include "graphicsmanager.h" +#include "main.h" +#include "party.h" +#include "pincodemanager.h" +#include "settings.h" +#include "soundmanager.h" +#include "spellmanager.h" + +#include "being/localclan.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" +#include "being/playerrelations.h" + +#include "const/net/net.h" + +#include "enums/being/attributesstrings.h" + +#include "fs/virtfs/fs.h" +#include "fs/virtfs/tools.h" + +#include "gui/dialogsmanager.h" +#include "gui/gui.h" +#include "gui/skin.h" +#include "gui/popupmanager.h" +#include "gui/windowmanager.h" + +#include "gui/shortcut/dropshortcut.h" +#include "gui/shortcut/emoteshortcut.h" +#include "gui/shortcut/itemshortcut.h" +#include "gui/shortcut/spellshortcut.h" + +#include "gui/windows/changeemaildialog.h" +#include "gui/windows/changepassworddialog.h" +#include "gui/windows/charselectdialog.h" +#include "gui/windows/connectiondialog.h" +#include "gui/windows/equipmentwindow.h" +#include "gui/windows/logindialog.h" +#include "gui/windows/npcdialog.h" +#include "gui/windows/okdialog.h" +#include "gui/windows/registerdialog.h" +#include "gui/windows/serverdialog.h" +#include "gui/windows/setupwindow.h" +#include "gui/windows/updaterwindow.h" +#include "gui/windows/quitdialog.h" +#include "gui/windows/worldselectdialog.h" + +#include "gui/widgets/button.h" +#include "gui/widgets/createwidget.h" +#include "gui/widgets/desktop.h" +#include "gui/widgets/windowcontainer.h" + +#include "input/inputmanager.h" +#include "input/joystick.h" +#include "input/keyboardconfig.h" + +#include "input/touch/touchmanager.h" + +#include "net/charserverhandler.h" +#include "net/chathandler.h" +#include "net/download.h" +#include "net/gamehandler.h" +#include "net/generalhandler.h" +#include "net/guildhandler.h" +#include "net/inventoryhandler.h" +#include "net/ipc.h" +#include "net/loginhandler.h" +#include "net/net.h" +#include "net/updatetypeoperators.h" +#include "net/useragent.h" +#include "net/packetlimiter.h" +#include "net/partyhandler.h" + +#ifdef TMWA_SUPPORT +#include "net/tmwa/guildmanager.h" +#endif // TMWA_SUPPORT + +#include "particle/particleengine.h" + +#include "resources/dbmanager.h" +#include "resources/imagehelper.h" + +#include "resources/dye/dyepalette.h" + +#include "resources/resourcemanager/resourcemanager.h" + +#include "resources/sprite/spritereference.h" + +#include "utils/checkutils.h" +#include "utils/cpu.h" +#include "utils/delete2.h" +#include "utils/dumplibs.h" +#include "utils/dumpsizes.h" +#include "utils/env.h" +#include "utils/fuzzer.h" +#include "utils/gettext.h" +#include "utils/gettexthelper.h" +#include "utils/mrand.h" +#ifdef ANDROID +#include "fs/paths.h" +#endif // ANDROID +#include "utils/perfstat.h" +#include "utils/sdlcheckutils.h" +#include "utils/sdlhelper.h" +#include "utils/timer.h" + +#include "utils/translation/translationmanager.h" + +#include "listeners/assertlistener.h" +#include "listeners/errorlistener.h" + +#ifdef USE_OPENGL +#include "test/testlauncher.h" +#include "test/testmain.h" +#else // USE_OPENGL +#include "configuration.h" +#endif // USE_OPENGL + +#ifdef WIN32 +PRAGMA48(GCC diagnostic push) +PRAGMA48(GCC diagnostic ignored "-Wshadow") +#include <SDL_syswm.h> +PRAGMA48(GCC diagnostic pop) +#include "fs/specialfolder.h" +#undef ERROR +#endif // WIN32 + +#ifdef ANDROID +#ifndef USE_SDL2 +PRAGMA48(GCC diagnostic push) +PRAGMA48(GCC diagnostic ignored "-Wshadow") +#include <SDL_screenkeyboard.h> +PRAGMA48(GCC diagnostic pop) +#include <fstream> +#endif // USE_SDL2 +#endif // ANDROID + +#include <sys/stat.h> + +#ifdef USE_MUMBLE +#include "mumblemanager.h" +#endif // USE_MUMBLE + +PRAGMA48(GCC diagnostic push) +PRAGMA48(GCC diagnostic ignored "-Wshadow") +#ifdef USE_SDL2 +#include <SDL2_framerate.h> +#else // USE_SDL2 +#include <SDL_framerate.h> +#endif // USE_SDL2 +PRAGMA48(GCC diagnostic pop) + +#include "debug.h" + +std::string errorMessage; +LoginData loginData; + +Client *client = nullptr; + +extern FPSmanager fpsManager; +extern int evolPacketOffset; + +volatile bool runCounters; +bool isSafeMode = false; +int serverVersion = 0; +int packetVersion = 0; +int packetVersionMain = 0; +int packetVersionRe = 0; +int packetVersionZero = 0; +int packetsType = 0; +int itemIdLen = 2; +bool packets_main = true; +bool packets_re = false; +bool packets_zero = false; +unsigned int tmwServerVersion = 0; +time_t start_time; +unsigned int mLastHost = 0; +unsigned long mSearchHash = 0; +int textures_count = 0; +volatile bool isTerminate = false; + +namespace +{ + class AccountListener final : public ActionListener + { + public: + AccountListener() + { } + + A_DELETE_COPY(AccountListener) + + void action(const ActionEvent &event A_UNUSED) override final + { + client->setState(State::CHAR_SELECT); + } + } accountListener; + + class LoginListener final : public ActionListener + { + public: + LoginListener() + { } + + A_DELETE_COPY(LoginListener) + + void action(const ActionEvent &event A_UNUSED) override final + { + client->setState(State::PRE_LOGIN); + } + } loginListener; +} // namespace + +Client::Client() : + ActionListener(), + mCurrentServer(), + mGame(nullptr), + mCurrentDialog(nullptr), + mQuitDialog(nullptr), + mSetupButton(nullptr), + mVideoButton(nullptr), + mHelpButton(nullptr), + mAboutButton(nullptr), + mThemesButton(nullptr), + mPerfomanceButton(nullptr), +#ifdef ANDROID + mCloseButton(nullptr), +#endif // ANDROID + mState(State::CHOOSE_SERVER), + mOldState(State::START), + mSkin(nullptr), + mButtonPadding(1), + mButtonSpacing(3), + mPing(0), + mConfigAutoSaved(false) +{ + WindowManager::init(); +} + +void Client::testsInit() +{ + if (!settings.options.test.empty() && + settings.options.test != "99") + { + gameInit(); + } + else + { + initRand(); + logger = new Logger; + SDL::initLogger(); + Dirs::initLocalDataDir(); + Dirs::initTempDir(); + Dirs::initConfigDir(); + GettextHelper::initLang(); + } +} + +void Client::gameInit() +{ + logger = new Logger; + SDL::initLogger(); + + initRand(); + + assertListener = new AssertListener; + // Load branding information + if (!settings.options.brandingPath.empty()) + { + branding.init(settings.options.brandingPath, + UseVirtFs_false, + SkipError_false); + } + setBrandingDefaults(branding); + + Dirs::initRootDir(); + Dirs::initHomeDir(); + + // Configure logger + if (!settings.options.logFileName.empty()) + { + settings.logFileName = settings.options.logFileName; + } + else + { + settings.logFileName = pathJoin(settings.localDataDir, + "manaverse.log"); + } + logger->log("Log file: " + settings.logFileName); + logger->setLogFile(settings.logFileName); + +#ifdef USE_FUZZER + Fuzzer::init(); +#endif // USE_FUZZER + + if (settings.options.ipc == true) + IPC::start(); + if (settings.options.test.empty()) + ConfigManager::backupConfig("config.xml"); + ConfigManager::initConfiguration(); + SDL::setLogLevel(config.getIntValue("sdlLogLevel")); + settings.init(); + Net::loadIgnorePackets(); + setPathsDefaults(paths); + initFeatures(); + initPaths(); + logger->log("init 4"); + logger->setDebugLog(config.getBoolValue("debugLog")); + logger->setReportUnimplemented(config.getBoolValue("unimplimentedLog")); + + config.incValue("runcount"); + +#ifndef ANDROID + if (settings.options.test.empty()) + ConfigManager::storeSafeParameters(); +#endif // ANDROID + + if (!VirtFs::setWriteDir(settings.localDataDir)) + { + logger->error(strprintf("%s couldn't be set as home directory! " + "Exiting.", settings.localDataDir.c_str())); + } + + GettextHelper::initLang(); + + chatLogger = new ChatLogger; + if (settings.options.chatLogDir.empty()) + { + chatLogger->setBaseLogDir(settings.localDataDir + + std::string("/logs/")); + } + else + { + chatLogger->setBaseLogDir(settings.options.chatLogDir); + } + + // Log the client version + logger->log1(FULL_VERSION); + logger->log("Start configPath: " + config.getConfigPath()); + + Dirs::initScreenshotDir(); + + updateEnv(); + SDL::allowScreenSaver(config.getBoolValue("allowscreensaver")); + dumpLibs(); + dumpSizes(); + + // Initialize SDL + logger->log1("Initializing SDL..."); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) + { + logger->safeError(strprintf("Could not initialize SDL: %s", + SDL_GetError())); + } +#ifndef __SWITCH__ + atexit(SDL_Quit); +#endif + PacketLimiter::initPacketLimiter(); +#ifndef USE_SDL2 + SDL_EnableUNICODE(1); +#endif // USE_SDL2 + + WindowManager::applyKeyRepeat(); + eventsManager.init(); + eventsManager.enableEvents(); + +#ifdef WIN32 + Dirs::mountDataDir(); +#endif // WIN32 +#ifndef USE_SDL2 + WindowManager::setIcon(); +#endif // USE_SDL2 + + ConfigManager::checkConfigVersion(); + logVars(); + Cpu::detect(); + DyePalette::initFunctions(); +#if defined(USE_OPENGL) +#if !defined(ANDROID) && !defined(__APPLE__) +#if !defined(__native_client__) && !defined(__SWITCH__) && !defined(UNITTESTS) + if (!settings.options.safeMode && + settings.options.renderer < 0 && + settings.options.test.empty() && + !settings.options.validate && + !config.getBoolValue("videodetected")) + { + graphicsManager.detectVideoSettings(); + } +#endif // !defined(__native_client__) && !defined(__SWITCH__) && + // !defined(UNITTESTS) +#endif // !defined(ANDROID) && !defined(__APPLE__) && +#endif // defined(USE_OPENGL) + + initGraphics(); + UserAgent::update(); + + touchManager.init(); + +#ifndef WIN32 + Dirs::extractDataDir(); + Dirs::mountDataDir(); +#endif // WIN32 + + Dirs::updateDataPath(); + + // Add the main data directories to our VirtFs search path + if (!settings.options.dataPath.empty()) + { + VirtFs::mountDir(settings.options.dataPath, + Append_false); + } + + // Add the local data directory to VirtFs search path + VirtFs::mountDir(settings.localDataDir, + Append_false); + TranslationManager::loadCurrentLang(); + TranslationManager::loadDictionaryLang(); +#ifdef ENABLE_CUSTOMNLS + TranslationManager::loadGettextLang(); +#endif // ENABLE_CUSTOMNLS + +#ifdef USE_SDL2 + WindowManager::setIcon(); +#endif // USE_SDL2 + WindowManager::initTitle(); + + mainGraphics->postInit(); + + theme = new Theme; + Theme::selectSkin(); + ActorSprite::load(); + touchManager.init(); + + // Initialize the item and emote shortcuts. + for (size_t f = 0; f < SHORTCUT_TABS; f ++) + itemShortcut[f] = new ItemShortcut(f); + emoteShortcut = new EmoteShortcut; + dropShortcut = new DropShortcut; + + gui = new Gui; + gui->postInit(mainGraphics); + dialogsManager = new DialogsManager; + popupManager = new PopupManager; + + initSoundManager(); + + // Initialize keyboard + keyboard.init(); + inputManager.init(); + + // Initialise player relations + playerRelations.init(); + Joystick::init(); + WindowManager::createWindows(); + + keyboard.update(); + if (joystick != nullptr) + joystick->update(); + + // Initialize default server + mCurrentServer.hostname = settings.options.serverName; + mCurrentServer.port = settings.options.serverPort; + if (!settings.options.serverType.empty()) + { + mCurrentServer.type = ServerInfo::parseType( + settings.options.serverType); + } + + loginData.username = settings.options.username; + loginData.password = settings.options.password; + LoginDialog::savedPassword = settings.options.password; + loginData.remember = (serverConfig.getValue("remember", 1) != 0); + loginData.registerLogin = false; + + if (mCurrentServer.hostname.empty()) + { + mCurrentServer.hostname = branding.getValue("defaultServer", ""); + settings.options.serverName = mCurrentServer.hostname; + } + + if (mCurrentServer.port == 0) + { + mCurrentServer.port = CAST_U16(branding.getValue( + "defaultPort", CAST_S32(DEFAULT_PORT))); + mCurrentServer.type = ServerInfo::parseType( + branding.getValue("defaultServerType", "tmwathena")); + } + + chatLogger->setServerName(mCurrentServer.hostname); + + if (loginData.username.empty() && loginData.remember) + loginData.username = serverConfig.getValue("username", ""); + + if (mState != State::ERROR) + mState = State::CHOOSE_SERVER; + + startTimers(); + + const int fpsLimit = config.getIntValue("fpslimit"); + settings.limitFps = fpsLimit > 0; + + SDL_initFramerate(&fpsManager); + WindowManager::setFramerate(fpsLimit); + initConfigListeners(); + + settings.guiAlpha = config.getFloatValue("guialpha"); + optionChanged("fpslimit"); + + start_time = time(nullptr); + + PlayerInfo::init(); + +#ifdef ANDROID +#ifndef USE_SDL2 + WindowManager::updateScreenKeyboard(SDL_GetScreenKeyboardHeight(nullptr)); +#endif // USE_SDL2 +#endif // ANDROID + +#ifdef USE_MUMBLE + if (!mumbleManager) + mumbleManager = new MumbleManager; +#endif // USE_MUMBLE + + mSkin = theme->load("windowmenu.xml", + "", + true, + Theme::getThemePath()); + if (mSkin != nullptr) + { + mButtonPadding = mSkin->getPadding(); + mButtonSpacing = mSkin->getOption("spacing", 3); + } + if (settings.options.error) + inputManager.executeAction(InputAction::ERROR); + + if (settings.options.validate == true) + runValidate(); +} + +Client::~Client() +{ + if (!settings.options.testMode) + gameClear(); + else + testsClear(); + CHECKLISTENERS +} + +void Client::initConfigListeners() +{ + config.addListener("fpslimit", this); + config.addListener("guialpha", this); + config.addListener("gamma", this); + config.addListener("enableGamma", this); + config.addListener("particleEmitterSkip", this); + config.addListener("vsync", this); + config.addListener("repeateDelay", this); + config.addListener("repeateInterval", this); + config.addListener("logInput", this); +} + +void Client::initSoundManager() +{ + // Initialize sound engine + try + { + if (config.getBoolValue("sound")) + soundManager.init(); + + soundManager.setSfxVolume(config.getIntValue("sfxVolume")); + soundManager.setMusicVolume(config.getIntValue("musicVolume")); + } + catch (const char *const err) + { + mState = State::ERROR; + errorMessage = err; + logger->log("Warning: %s", err); + } + soundManager.playMusic(branding.getValue( + "loginMusic", + "keprohm.ogg"), + SkipError_true); +} + +void Client::initGraphics() +{ +#ifndef USE_SDL2 + WindowManager::applyVSync(); +#endif // USE_SDL2 + + runCounters = config.getBoolValue("packetcounters"); + + graphicsManager.initGraphics(); +#ifdef USE_SDL2 + WindowManager::applyVSync(); +#endif // USE_SDL2 + + imageHelper->postInit(); + setConfigDefaults2(config); + WindowManager::applyGrabMode(); + WindowManager::applyGamma(); + + mainGraphics->beginDraw(); +} + +void Client::testsClear() +{ + if (!settings.options.test.empty()) + gameClear(); + else + BeingInfo::clear(); +} + +void Client::gameClear() +{ + if (logger != nullptr) + logger->log1("Quitting1"); + isTerminate = true; + config.removeListeners(this); + + delete2(assertListener) + + IPC::stop(); + eventsManager.shutdown(); + WindowManager::deleteWindows(); + if (windowContainer != nullptr) + windowContainer->slowLogic(); + + stopTimers(); + DbManager::unloadDb(); + + if (loginHandler != nullptr) + loginHandler->clearWorlds(); + + if (chatHandler != nullptr) + chatHandler->clear(); + + if (charServerHandler != nullptr) + charServerHandler->clear(); + + delete2(ipc) + +#ifdef USE_MUMBLE + delete2(mumbleManager) +#endif // USE_MUMBLE + + PlayerInfo::deinit(); + + // Before config.write() since it writes the shortcuts to the config + for (unsigned f = 0; f < SHORTCUT_TABS; f ++) + delete2(itemShortcut[f]) + delete2(emoteShortcut) + delete2(dropShortcut) + + playerRelations.store(); + + if (logger != nullptr) + logger->log1("Quitting2"); + + delete2(mCurrentDialog) + delete2(popupManager) + delete2(dialogsManager) + delete2(gui) + + if (inventoryHandler != nullptr) + inventoryHandler->clear(); + + if (logger != nullptr) + logger->log1("Quitting3"); + + touchManager.clear(); + + GraphicsManager::deleteRenderers(); + + if (logger != nullptr) + logger->log1("Quitting4"); + + XML::cleanupXML(); + + if (logger != nullptr) + logger->log1("Quitting5"); + + BeingInfo::clear(); + + // Shutdown sound + soundManager.close(); + + if (logger != nullptr) + logger->log1("Quitting6"); + + ActorSprite::unload(); + + ResourceManager::deleteInstance(); + + soundManager.shutdown(); + + if (logger != nullptr) + logger->log1("Quitting8"); + + WindowManager::deleteIcon(); + + if (logger != nullptr) + logger->log1("Quitting9"); + + delete2(joystick) + + keyboard.deinit(); + + if (logger != nullptr) + logger->log1("Quitting10"); + + touchManager.shutdown(); + +#ifdef DEBUG_CONFIG + config.enableKeyLogging(); +#endif // DEBUG_CONFIG + + config.removeOldKeys(); + config.write(); + serverConfig.write(); + + config.clear(); + serverConfig.clear(); + + if (logger != nullptr) + logger->log1("Quitting11"); + +#ifdef USE_PROFILER + Perfomance::clear(); +#endif // USE_PROFILER + +#ifdef DEBUG_OPENGL_LEAKS + if (logger) + logger->log("textures left: %d", textures_count); +#endif // DEBUG_OPENGL_LEAKS + + Graphics::cleanUp(); + + if (logger != nullptr) + logger->log1("Quitting12"); + + delete2(chatLogger) + TranslationManager::close(); +} + +int Client::testsExec() +{ +#ifdef USE_OPENGL + if (settings.options.test.empty()) + { + TestMain test; + return test.exec(); + } + else + { + TestLauncher launcher(settings.options.test); + return launcher.exec(); + } +#else // USE_OPENGL + + return 0; +#endif // USE_OPENGL +} + +#define ADDBUTTON(var, object) var = object; \ + x -= var->getWidth() + mButtonSpacing; \ + var->setPosition(x, mButtonPadding); \ + top->add(var); + +void Client::stateConnectGame1() +{ + if ((gameHandler != nullptr) && + (loginHandler != nullptr) && + gameHandler->isConnected()) + { + loginHandler->disconnect(); + } +} + +void Client::stateConnectServer1() +{ + if (mOldState == State::CHOOSE_SERVER) + { + settings.serverName = mCurrentServer.hostname; + ConfigManager::initServerConfig(mCurrentServer.hostname); + PacketLimiter::initPacketLimiter(); + initTradeFilter(); + Dirs::initUsersDir(); + playerRelations.init(); + + // Initialize the item and emote shortcuts. + for (unsigned f = 0; f < SHORTCUT_TABS; f ++) + { + delete itemShortcut[f]; + itemShortcut[f] = new ItemShortcut(f); + } + delete emoteShortcut; + emoteShortcut = new EmoteShortcut; + + // Initialize the drop shortcuts. + delete dropShortcut; + dropShortcut = new DropShortcut; + + initFeatures(); + PlayerInfo::loadData(); + loginData.registerUrl = mCurrentServer.registerUrl; + loginData.packetVersion = mCurrentServer.packetVersion; + if (!mCurrentServer.onlineListUrl.empty()) + settings.onlineListUrl = mCurrentServer.onlineListUrl; + else + settings.onlineListUrl = settings.serverName; + settings.persistentIp = mCurrentServer.persistentIp; + settings.supportUrl = mCurrentServer.supportUrl; + settings.updateMirrors = mCurrentServer.updateMirrors; + settings.enableRemoteCommands = (serverConfig.getValue( + "enableRemoteCommands", 1) != 0); + + if (settings.options.username.empty()) + { + if (loginData.remember) + loginData.username = serverConfig.getValue("username", ""); + else + loginData.username.clear(); + } + else + { + loginData.username = settings.options.username; + } +#ifdef SAVE_PASSWORD + LoginDialog::savedPassword = loginData.remember ? + serverConfig.getValue("password", "") : ""; + loginData.password = LoginDialog::savedPassword; + settings.options.password = LoginDialog::savedPassword; +#endif + settings.login = loginData.username; + WindowManager::updateTitle(); + + loginData.remember = (serverConfig.getValue("remember", 1) != 0); + Net::connectToServer(mCurrentServer); + +#ifdef USE_MUMBLE + if (mumbleManager) + mumbleManager->setServer(mCurrentServer.hostname); +#endif // USE_MUMBLE + +#ifdef TMWA_SUPPORT + GuildManager::init(); +#endif // TMWA_SUPPORT + + if (!mConfigAutoSaved) + { + mConfigAutoSaved = true; + config.write(); + } + } + else if (loginHandler != nullptr && + loginHandler->isConnected()) + { + mState = State::PRE_LOGIN; + } +} + +void Client::stateWorldSelect1() +{ + if (mOldState == State::UPDATE && + (loginHandler != nullptr)) + { + if (loginHandler->getWorlds().size() < 2) + mState = State::PRE_LOGIN; + } +} + +void Client::stateGame1() +{ + if (gui == nullptr) + return; + + BasicContainer2 *const top = static_cast<BasicContainer2*>( + gui->getTop()); + + if (top == nullptr) + return; + + CREATEWIDGETV(desktop, Desktop, nullptr); + top->add(desktop); + int x = top->getWidth() - mButtonPadding; + ADDBUTTON(mSetupButton, new Button(desktop, + // TRANSLATORS: setup tab quick button + _("Setup"), "Setup", BUTTON_SKIN, this)) + ADDBUTTON(mPerfomanceButton, new Button(desktop, + // TRANSLATORS: perfoamance tab quick button + _("Performance"), "Perfomance", BUTTON_SKIN, this)) + ADDBUTTON(mVideoButton, new Button(desktop, + // TRANSLATORS: video tab quick button + _("Video"), "Video", BUTTON_SKIN, this)) + ADDBUTTON(mThemesButton, new Button(desktop, + // TRANSLATORS: theme tab quick button + _("Theme"), "Themes", BUTTON_SKIN, this)) + ADDBUTTON(mAboutButton, new Button(desktop, + // TRANSLATORS: theme tab quick button + _("About"), "about", BUTTON_SKIN, this)) + ADDBUTTON(mHelpButton, new Button(desktop, + // TRANSLATORS: theme tab quick button + _("Help"), "help", BUTTON_SKIN, this)) +#ifdef ANDROID + ADDBUTTON(mCloseButton, new Button(desktop, + // TRANSLATORS: close quick button + _("Close"), "close", BUTTON_SKIN, this)) +#endif // ANDROID + + desktop->setSize(mainGraphics->getWidth(), + mainGraphics->getHeight()); +} + +void Client::stateSwitchLogin1() +{ + if (mOldState == State::GAME && + (gameHandler != nullptr)) + { + gameHandler->disconnect(); + } +} + +int Client::gameExec() +{ + int lastTickTime = tick_time; + + Perf::init(); + + while (mState != State::EXIT) + { + PROFILER_START(); + PERF_STAT(0); + if (eventsManager.handleEvents()) + continue; + + PERF_STAT(1); + + BLOCK_START("Client::gameExec 3") + if (generalHandler != nullptr) + generalHandler->flushNetwork(); + BLOCK_END("Client::gameExec 3") + + PERF_STAT(2); + + BLOCK_START("Client::gameExec 4") + if (gui != nullptr) + gui->logic(); + + PERF_STAT(3); + + cur_time = time(nullptr); + int k = 0; + while (lastTickTime != tick_time && + k < 40) + { + if (mGame != nullptr) + mGame->logic(); + else if (gui != nullptr) + gui->handleInput(); + + ++lastTickTime; + k ++; + } + + PERF_STAT(4); + + soundManager.logic(); + + PERF_STAT(5); + + logic_count = logic_count + k; + if (gui != nullptr) + gui->slowLogic(); + + PERF_STAT(6); + + if (mGame != nullptr) + mGame->slowLogic(); + + PERF_STAT(7); + + slowLogic(); + + PERF_STAT(8); + + BLOCK_END("Client::gameExec 4") + + // This is done because at some point tick_time will wrap. + lastTickTime = tick_time; + + BLOCK_START("Client::gameExec 6") + if (mState == State::CONNECT_GAME) + { + stateConnectGame1(); + } + else if (mState == State::CONNECT_SERVER) + { + stateConnectServer1(); + } + else if (mState == State::WORLD_SELECT) + { + stateWorldSelect1(); + } + else if (mOldState == State::START || + (mOldState == State::GAME && mState != State::GAME)) + { + stateGame1(); + } + else if (mState == State::SWITCH_LOGIN) + { + stateSwitchLogin1(); + } + BLOCK_END("Client::gameExec 6") + + PERF_STAT(9); + + if (mState != mOldState) + { + BLOCK_START("Client::gameExec 7") + PlayerInfo::stateChange(mState); + + if (mOldState == State::GAME) + { + delete2(mGame) + assertListener = new AssertListener; + Game::clearInstance(); + ResourceManager::cleanOrphans(false); + Party::clearParties(); + Guild::clearGuilds(); + NpcDialog::clearDialogs(); + if (guildHandler != nullptr) + guildHandler->clear(); + if (partyHandler != nullptr) + partyHandler->clear(); + if (chatLogger != nullptr) + chatLogger->clear(); + if (!settings.options.dataPath.empty()) + UpdaterWindow::unloadMods(settings.options.dataPath); + else + UpdaterWindow::unloadMods(settings.oldUpdates); + if (!settings.options.skipUpdate) + UpdaterWindow::unloadMods(settings.oldUpdates + "/fix/"); + } + else if (mOldState == State::CHAR_SELECT) + { + if (mState != State::CHANGEPASSWORD && + charServerHandler != nullptr) + { + charServerHandler->clear(); + } + } + + mOldState = mState; + + // Get rid of the dialog of the previous state + delete2(mCurrentDialog) + + // State has changed, while the quitDialog was active, it might + // not be correct anymore + if (mQuitDialog != nullptr) + { + mQuitDialog->scheduleDelete(); + mQuitDialog = nullptr; + } + BLOCK_END("Client::gameExec 7") + + BLOCK_START("Client::gameExec 8") + switch (mState) + { + case State::CHOOSE_SERVER: + { + BLOCK_START("Client::gameExec STATE_CHOOSE_SERVER") + logger->log1("State: CHOOSE SERVER"); + unloadData(); + pincodeManager.closeDialogs(); + + // Allow changing this using a server choice dialog + // We show the dialog box only if the command-line + // options weren't set. + if (settings.options.serverName.empty() && + settings.options.serverPort == 0 && + !branding.getValue("onlineServerList", "a").empty()) + { + // Don't allow an alpha opacity + // lower than the default value + theme->setMinimumOpacity(0.8F); + + CREATEWIDGETV(mCurrentDialog, ServerDialog, + &mCurrentServer, + settings.configDir); + } + else + { + mState = State::CONNECT_SERVER; + + // Reset options so that cancelling or connect + // timeout will show the server dialog. + settings.options.serverName.clear(); + settings.options.serverPort = 0; + } + BLOCK_END("Client::gameExec STATE_CHOOSE_SERVER") + break; + } + + case State::CONNECT_SERVER: + BLOCK_START("Client::gameExec State::CONNECT_SERVER") + logger->log1("State: CONNECT SERVER"); + loginData.updateHosts.clear(); + CREATEWIDGETV(mCurrentDialog, ConnectionDialog, + // TRANSLATORS: connection dialog header + _("Connecting to server"), + State::SWITCH_SERVER); + TranslationManager::loadCurrentLang(); + TranslationManager::loadDictionaryLang(); + pincodeManager.init(); + BLOCK_END("Client::gameExec State::CONNECT_SERVER") + break; + + case State::PRE_LOGIN: + logger->log1("State: PRE_LOGIN"); + break; + + case State::LOGIN: + BLOCK_START("Client::gameExec State::LOGIN") + logger->log1("State: LOGIN"); + // Don't allow an alpha opacity + // lower than the default value + theme->setMinimumOpacity(0.8F); + + if (packetVersion == 0) + { + packetVersion = loginData.packetVersion; + if (packetVersion != 0) + { + loginHandler->updatePacketVersion(); + logger->log("Preconfigured packet version: %d", + packetVersion); + } + } + + loginData.updateType = static_cast<UpdateTypeT>( + serverConfig.getValue("updateType", 0)); + + mSearchHash = Net::Download::adlerBuffer( + const_cast<char*>(mCurrentServer.hostname.c_str()), + CAST_S32(mCurrentServer.hostname.size())); + if (settings.options.username.empty() || + settings.options.password.empty()) + { + CREATEWIDGETV(mCurrentDialog, LoginDialog, + loginData, + &mCurrentServer, + &settings.options.updateHost); + } + else + { + mState = State::LOGIN_ATTEMPT; + // Clear the password so that when login fails, the + // dialog will show up next time. + settings.options.password.clear(); + } + BLOCK_END("Client::gameExec State::LOGIN") + break; + + case State::LOGIN_ATTEMPT: + BLOCK_START("Client::gameExec State::LOGIN_ATTEMPT") + logger->log1("State: LOGIN ATTEMPT"); + CREATEWIDGETV(mCurrentDialog, ConnectionDialog, + // TRANSLATORS: connection dialog header + _("Logging in"), + State::SWITCH_SERVER); + if (loginHandler != nullptr) + loginHandler->loginOrRegister(&loginData); + BLOCK_END("Client::gameExec State::LOGIN_ATTEMPT") + break; + + case State::WORLD_SELECT: + BLOCK_START("Client::gameExec State::WORLD_SELECT") + logger->log1("State: WORLD SELECT"); + { + TranslationManager::loadCurrentLang(); + TranslationManager::loadDictionaryLang(); + if (loginHandler == nullptr) + { + BLOCK_END("Client::gameExec State::WORLD_SELECT") + break; + } + Worlds worlds = loginHandler->getWorlds(); + + if (worlds.empty()) + { + // Trust that the netcode knows what it's doing + mState = State::UPDATE; + } + else if (worlds.size() == 1) + { + loginHandler->chooseServer( + 0, mCurrentServer.persistentIp); + mState = State::UPDATE; + } + else + { + CREATEWIDGETV(mCurrentDialog, WorldSelectDialog, + worlds); + if (settings.options.chooseDefault) + { + static_cast<WorldSelectDialog*>(mCurrentDialog) + ->action(ActionEvent(nullptr, "ok")); + } + } + } + BLOCK_END("Client::gameExec State::WORLD_SELECT") + break; + + case State::WORLD_SELECT_ATTEMPT: + BLOCK_START("Client::gameExec State::WORLD_SELECT_ATTEMPT") + logger->log1("State: WORLD SELECT ATTEMPT"); + CREATEWIDGETV(mCurrentDialog, ConnectionDialog, + // TRANSLATORS: connection dialog header + _("Entering game world"), + State::WORLD_SELECT); + BLOCK_END("Client::gameExec State::WORLD_SELECT_ATTEMPT") + break; + + case State::UPDATE: + BLOCK_START("Client::gameExec State::UPDATE") + logger->log1("State: UPDATE"); + + // Determine which source to use for the update host + if (!settings.options.updateHost.empty()) + settings.updateHost = settings.options.updateHost; + else + settings.updateHost = loginData.updateHost; + Dirs::initUpdatesDir(); + + if (!settings.oldUpdates.empty()) + UpdaterWindow::unloadUpdates(settings.oldUpdates); + + if (settings.options.skipUpdate) + { + mState = State::LOAD_DATA; + settings.oldUpdates.clear(); + UpdaterWindow::loadDirMods(settings.options.dataPath); + } + else if ((loginData.updateType & UpdateType::Skip) != 0) + { + settings.oldUpdates = pathJoin(settings.localDataDir, + settings.updatesDir); + UpdaterWindow::loadLocalUpdates(settings.oldUpdates); + mState = State::LOAD_DATA; + } + else + { + settings.oldUpdates = pathJoin(settings.localDataDir, + settings.updatesDir); + CREATEWIDGETV(mCurrentDialog, UpdaterWindow, + settings.updateHost, + settings.oldUpdates, + settings.options.dataPath.empty(), + loginData.updateType); + } + BLOCK_END("Client::gameExec State::UPDATE") + break; + + case State::LOAD_DATA: + { + BLOCK_START("Client::gameExec State::LOAD_DATA") + logger->log1("State: LOAD DATA"); + + loadData(); + + mState = State::GET_CHARACTERS; + BLOCK_END("Client::gameExec State::LOAD_DATA") + break; + } + case State::GET_CHARACTERS: + BLOCK_START("Client::gameExec State::GET_CHARACTERS") + logger->log1("State: GET CHARACTERS"); + CREATEWIDGETV(mCurrentDialog, ConnectionDialog, + // TRANSLATORS: connection dialog header + _("Requesting characters"), + State::SWITCH_SERVER); + if (charServerHandler != nullptr) + charServerHandler->requestCharacters(); + BLOCK_END("Client::gameExec State::GET_CHARACTERS") + break; + + case State::CHAR_SELECT: + BLOCK_START("Client::gameExec State::CHAR_SELECT") + logger->log1("State: CHAR SELECT"); + // Don't allow an alpha opacity + // lower than the default value + theme->setMinimumOpacity(0.8F); + + settings.login = loginData.username; + WindowManager::updateTitle(); + + CREATEWIDGETV(mCurrentDialog, CharSelectDialog, + loginData); + pincodeManager.updateState(); + + if (!(static_cast<CharSelectDialog*>(mCurrentDialog)) + ->selectByName(settings.options.character, + CharSelectDialog::Choose)) + { + (static_cast<CharSelectDialog*>(mCurrentDialog)) + ->selectByName( + serverConfig.getValue("lastCharacter", ""), + settings.options.chooseDefault ? + CharSelectDialog::Choose : + CharSelectDialog::Focus); + } + + // Choosing character on the command line should work only + // once, clear it so that 'switch character' works. + settings.options.character.clear(); + BLOCK_END("Client::gameExec State::CHAR_SELECT") + break; + + case State::CONNECT_GAME: + BLOCK_START("Client::gameExec State::CONNECT_GAME") + logger->log1("State: CONNECT GAME"); + CREATEWIDGETV(mCurrentDialog, ConnectionDialog, + // TRANSLATORS: connection dialog header + _("Connecting to the game server"), + State::CHOOSE_SERVER); + if (gameHandler != nullptr) + gameHandler->connect(); + BLOCK_END("Client::gameExec State::CONNECT_GAME") + break; + + case State::CHANGE_MAP: + BLOCK_START("Client::gameExec State::CHANGE_MAP") + logger->log1("State: CHANGE_MAP"); + CREATEWIDGETV(mCurrentDialog, ConnectionDialog, + // TRANSLATORS: connection dialog header + _("Changing game servers"), + State::SWITCH_CHARACTER); + if (gameHandler != nullptr) + gameHandler->connect(); + BLOCK_END("Client::gameExec State::CHANGE_MAP") + break; + + case State::GAME: + BLOCK_START("Client::gameExec State::GAME") + if (localPlayer != nullptr) + { + logger->log("Memorizing selected character %s", + localPlayer->getName().c_str()); + serverConfig.setValue("lastCharacter", + localPlayer->getName()); +#ifdef USE_MUMBLE + if (mumbleManager) + mumbleManager->setPlayer(localPlayer->getName()); +#endif // USE_MUMBLE + Perf::init(); + } + + // Fade out logon-music here too to give the desired effect + // of "flowing" into the game. + soundManager.fadeOutMusic(1000); + + // Allow any alpha opacity + theme->setMinimumOpacity(-1.0F); + + if (chatLogger != nullptr) + chatLogger->setServerName(settings.serverName); + +#ifdef ANDROID + delete2(mCloseButton) +#endif // ANDROID + + delete2(mSetupButton) + delete2(mVideoButton) + delete2(mThemesButton) + delete2(mAboutButton) + delete2(mHelpButton) + delete2(mPerfomanceButton) + delete2(desktop) + + mCurrentDialog = nullptr; + + logger->log1("State: GAME"); + if (generalHandler != nullptr) + generalHandler->reloadPartially(); + mGame = new Game; + BLOCK_END("Client::gameExec State::GAME") + break; + + case State::LOGIN_ERROR: + BLOCK_START("Client::gameExec State::LOGIN_ERROR") + logger->log1("State: LOGIN ERROR"); + CREATEWIDGETV(mCurrentDialog, OkDialog, + // TRANSLATORS: error dialog header + _("Error"), + errorMessage, + // TRANSLATORS: ok dialog button + _("Close"), + DialogType::ERROR, + Modal_true, + ShowCenter_true, + nullptr, + 260); + mCurrentDialog->addActionListener(&loginListener); + mCurrentDialog = nullptr; // OkDialog deletes itself + BLOCK_END("Client::gameExec State::LOGIN_ERROR") + break; + + case State::ACCOUNTCHANGE_ERROR: + BLOCK_START("Client::gameExec State::ACCOUNTCHANGE_ERROR") + logger->log1("State: ACCOUNT CHANGE ERROR"); + CREATEWIDGETV(mCurrentDialog, OkDialog, + // TRANSLATORS: error dialog header + _("Error"), + errorMessage, + // TRANSLATORS: ok dialog button + _("Close"), + DialogType::ERROR, + Modal_true, + ShowCenter_true, + nullptr, + 260); + mCurrentDialog->addActionListener(&accountListener); + mCurrentDialog = nullptr; // OkDialog deletes itself + BLOCK_END("Client::gameExec State::ACCOUNTCHANGE_ERROR") + break; + + case State::REGISTER_PREP: + BLOCK_START("Client::gameExec State::REGISTER_PREP") + logger->log1("State: REGISTER_PREP"); + CREATEWIDGETV(mCurrentDialog, ConnectionDialog, + // TRANSLATORS: connection dialog header + _("Requesting registration details"), + State::LOGIN); + loginHandler->getRegistrationDetails(); + BLOCK_END("Client::gameExec State::REGISTER_PREP") + break; + + case State::REGISTER: + logger->log1("State: REGISTER"); + CREATEWIDGETV(mCurrentDialog, RegisterDialog, + loginData); + break; + + case State::REGISTER_ATTEMPT: + BLOCK_START("Client::gameExec State::REGISTER_ATTEMPT") + logger->log("Username is %s", loginData.username.c_str()); + if (loginHandler != nullptr) + loginHandler->registerAccount(&loginData); + BLOCK_END("Client::gameExec State::REGISTER_ATTEMPT") + break; + + case State::CHANGEPASSWORD: + BLOCK_START("Client::gameExec State::CHANGEPASSWORD") + logger->log1("State: CHANGE PASSWORD"); + CREATEWIDGETV(mCurrentDialog, ChangePasswordDialog, + loginData); + mCurrentDialog->setVisible(Visible_true); + BLOCK_END("Client::gameExec State::CHANGEPASSWORD") + break; + + case State::CHANGEPASSWORD_ATTEMPT: + BLOCK_START("Client::gameExec " + "State::CHANGEPASSWORD_ATTEMPT") + logger->log1("State: CHANGE PASSWORD ATTEMPT"); + if (loginHandler != nullptr) + { + loginHandler->changePassword(loginData.password, + loginData.newPassword); + } + BLOCK_END("Client::gameExec State::CHANGEPASSWORD_ATTEMPT") + break; + + case State::CHANGEPASSWORD_SUCCESS: + BLOCK_START("Client::gameExec " + "State::CHANGEPASSWORD_SUCCESS") + logger->log1("State: CHANGE PASSWORD SUCCESS"); + CREATEWIDGETV(mCurrentDialog, OkDialog, + // TRANSLATORS: password change message header + _("Password Change"), + // TRANSLATORS: password change message text + _("Password changed successfully!"), + // TRANSLATORS: ok dialog button + _("OK"), + DialogType::ERROR, + Modal_true, + ShowCenter_true, + nullptr, + 260); + mCurrentDialog->addActionListener(&accountListener); + mCurrentDialog = nullptr; // OkDialog deletes itself + loginData.password = loginData.newPassword; + loginData.newPassword.clear(); + BLOCK_END("Client::gameExec State::CHANGEPASSWORD_SUCCESS") + break; + + case State::CHANGEEMAIL: + logger->log1("State: CHANGE EMAIL"); + CREATEWIDGETV(mCurrentDialog, + ChangeEmailDialog, + loginData); + mCurrentDialog->setVisible(Visible_true); + break; + + case State::CHANGEEMAIL_ATTEMPT: + logger->log1("State: CHANGE EMAIL ATTEMPT"); + if (loginHandler != nullptr) + loginHandler->changeEmail(loginData.email); + break; + + case State::CHANGEEMAIL_SUCCESS: + logger->log1("State: CHANGE EMAIL SUCCESS"); + CREATEWIDGETV(mCurrentDialog, OkDialog, + // TRANSLATORS: email change message header + _("Email Change"), + // TRANSLATORS: email change message text + _("Email changed successfully!"), + // TRANSLATORS: ok dialog button + _("OK"), + DialogType::ERROR, + Modal_true, + ShowCenter_true, + nullptr, + 260); + mCurrentDialog->addActionListener(&accountListener); + mCurrentDialog = nullptr; // OkDialog deletes itself + break; + + case State::SWITCH_SERVER: + BLOCK_START("Client::gameExec State::SWITCH_SERVER") + logger->log1("State: SWITCH SERVER"); + + if (loginHandler != nullptr) + loginHandler->disconnect(); + if (gameHandler != nullptr) + { + gameHandler->disconnect(); + gameHandler->clear(); + } + settings.serverName.clear(); + settings.login.clear(); + WindowManager::updateTitle(); + serverConfig.write(); + serverConfig.unload(); + if (setupWindow != nullptr) + setupWindow->externalUnload(); + + mState = State::CHOOSE_SERVER; + BLOCK_END("Client::gameExec State::SWITCH_SERVER") + break; + + case State::SWITCH_LOGIN: + BLOCK_START("Client::gameExec State::SWITCH_LOGIN") + logger->log1("State: SWITCH LOGIN"); + + if (loginHandler != nullptr) + { + loginHandler->logout(); + loginHandler->disconnect(); + } + if (gameHandler != nullptr) + gameHandler->disconnect(); + if (loginHandler != nullptr) + loginHandler->connect(); + + settings.login.clear(); + WindowManager::updateTitle(); + mState = State::LOGIN; + BLOCK_END("Client::gameExec State::SWITCH_LOGIN") + break; + + case State::SWITCH_CHARACTER: + BLOCK_START("Client::gameExec State::SWITCH_CHARACTER") + logger->log1("State: SWITCH CHARACTER"); + + // Done with game + if (gameHandler != nullptr) + gameHandler->disconnect(); + + settings.login.clear(); + WindowManager::updateTitle(); + mState = State::GET_CHARACTERS; + BLOCK_END("Client::gameExec State::SWITCH_CHARACTER") + break; + + case State::LOGOUT_ATTEMPT: + logger->log1("State: LOGOUT ATTEMPT"); + break; + + case State::WAIT: + logger->log1("State: WAIT"); + break; + + case State::EXIT: + BLOCK_START("Client::gameExec State::EXIT") + logger->log1("State: EXIT"); + Net::unload(); + BLOCK_END("Client::gameExec State::EXIT") + break; + + case State::FORCE_QUIT: + BLOCK_START("Client::gameExec State::FORCE_QUIT") + logger->log1("State: FORCE QUIT"); + if (generalHandler != nullptr) + generalHandler->unload(); + mState = State::EXIT; + BLOCK_END("Client::gameExec State::FORCE_QUIT") + break; + + case State::ERROR: + BLOCK_START("Client::gameExec State::ERROR") + config.write(); +#ifdef SAVE_PASSWORD + if (errorMessage == "Wrong password.") + { + serverConfig.setValue("password", ""); + serverConfig.write(); + } +#endif + if (mOldState == State::GAME) + serverConfig.write(); + logger->log1("State: ERROR"); + logger->log("Error: %s\n", errorMessage.c_str()); + pincodeManager.closeDialogs(); + mCurrentDialog = DialogsManager::openErrorDialog( + // TRANSLATORS: error message header + _("Error"), + errorMessage, + Modal_true); + mCurrentDialog->addActionListener(&errorListener); + mCurrentDialog = nullptr; // OkDialog deletes itself + gameHandler->disconnect(); + BLOCK_END("Client::gameExec State::ERROR") + break; + + case State::AUTORECONNECT_SERVER: + // ++++++ + break; + + case State::START: + default: + mState = State::FORCE_QUIT; + break; + } + BLOCK_END("Client::gameExec 8") + } + + PERF_STAT(10); + + // Update the screen when application is visible, delay otherwise. + if (!WindowManager::getIsMinimized()) + { + frame_count = frame_count + 1; + if (gui != nullptr) + gui->draw(); + mainGraphics->updateScreen(); + } + else + { + SDL_Delay(100); + } + + PERF_STAT(11); + + BLOCK_START("~Client::SDL_framerateDelay") + if (settings.limitFps) + SDL_framerateDelay(&fpsManager); + BLOCK_END("~Client::SDL_framerateDelay") + + PERF_STAT(12); + PERF_NEXTFRAME(); + PROFILER_END(); + } + + return settings.exitcode; +} + +void Client::optionChanged(const std::string &name) +{ + if (name == "fpslimit") + { + const int fpsLimit = config.getIntValue("fpslimit"); + settings.limitFps = fpsLimit > 0; + WindowManager::setFramerate(fpsLimit); + } + else if (name == "guialpha" || + name == "enableGuiOpacity") + { + const float alpha = config.getFloatValue("guialpha"); + settings.guiAlpha = alpha; + ImageHelper::setEnableAlpha(alpha != 1.0F && + config.getBoolValue("enableGuiOpacity")); + } + else if (name == "gamma" || + name == "enableGamma") + { + WindowManager::applyGamma(); + } + else if (name == "particleEmitterSkip") + { + ParticleEngine::emitterSkip = + config.getIntValue("particleEmitterSkip") + 1; + } + else if (name == "vsync") + { + WindowManager::applyVSync(); + } + else if (name == "repeateInterval" || + name == "repeateDelay") + { + WindowManager::applyKeyRepeat(); + } +} + +void Client::action(const ActionEvent &event) +{ + std::string tab; + const std::string &eventId = event.getId(); + + if (eventId == "close") + { + setState(State::FORCE_QUIT); + return; + } + if (eventId == "Setup") + { + tab.clear(); + } + else if (eventId == "help") + { + inputManager.executeAction(InputAction::WINDOW_HELP); + return; + } + else if (eventId == "about") + { + inputManager.executeAction(InputAction::WINDOW_ABOUT); + return; + } + else if (eventId == "Video") + { + tab = "Video"; + } + else if (eventId == "Themes") + { + tab = "Theme"; + } + else if (eventId == "Perfomance") + { + tab = "Perfomance"; + } + else + { + return; + } + + if (setupWindow != nullptr) + { + setupWindow->setVisible(fromBool( + !setupWindow->isWindowVisible(), Visible)); + if (setupWindow->isWindowVisible()) + { + if (!tab.empty()) + setupWindow->activateTab(tab); + setupWindow->requestMoveToTop(); + } + } +} + +void Client::initFeatures() +{ + features.init(paths.getStringValue("featuresFile"), + UseVirtFs_true, + SkipError_true); + setFeaturesDefaults(features); + settings.fixDeadAnimation = features.getBoolValue("fixDeadAnimation"); +} + +void Client::initPaths() +{ + settings.gmCommandSymbol = paths.getStringValue("gmCommandSymbol"); + settings.gmCharCommandSymbol = paths.getStringValue("gmCharCommandSymbol"); + settings.linkCommandSymbol = paths.getStringValue("linkCommandSymbol"); + if (settings.linkCommandSymbol.empty()) + settings.linkCommandSymbol = "="; + settings.overweightPercent = paths.getIntValue("overweightPercent"); + settings.fixedInventorySize = paths.getIntValue("fixedInventorySize"); + settings.playerNameOffset = paths.getIntValue( + "playerNameOffset"); + settings.playerBadgeAtRightOffset = paths.getIntValue( + "playerBadgeAtRightOffset"); + settings.unknownSkillsAutoTab = paths.getBoolValue("unknownSkillsAutoTab"); + settings.enableNewMailSystem = paths.getBoolValue("enableNewMailSystem"); +} + +void Client::initTradeFilter() +{ + const std::string tradeListName = + settings.serverConfigDir + "/tradefilter.txt"; + + std::ofstream tradeFile; + struct stat statbuf; + + if ((stat(tradeListName.c_str(), &statbuf) != 0) || + !S_ISREG(statbuf.st_mode)) + { + tradeFile.open(tradeListName.c_str(), + std::ios::out); + if (tradeFile.is_open()) + { + tradeFile << ": sell" << std::endl; + tradeFile << ": buy" << std::endl; + tradeFile << ": trade" << std::endl; + tradeFile << "i sell" << std::endl; + tradeFile << "i buy" << std::endl; + tradeFile << "i trade" << std::endl; + tradeFile << "i trading" << std::endl; + tradeFile << "i am buy" << std::endl; + tradeFile << "i am sell" << std::endl; + tradeFile << "i am trade" << std::endl; + tradeFile << "i am trading" << std::endl; + tradeFile << "i'm buy" << std::endl; + tradeFile << "i'm sell" << std::endl; + tradeFile << "i'm trade" << std::endl; + tradeFile << "i'm trading" << std::endl; + } + else + { + reportAlways("Error opening file for writing: %s", + tradeListName.c_str()) + } + tradeFile.close(); + } +} + +bool Client::isTmw() +{ + const std::string &name = settings.serverName; + if (name == "server.themanaworld.org" || + name == "themanaworld.org" || + name == "167.114.129.72") + { + return true; + } + return false; +} + +void Client::moveButtons(const int width) +{ + if (mSetupButton != nullptr) + { + int x = width - mSetupButton->getWidth() - mButtonPadding; + mSetupButton->setPosition(x, mButtonPadding); +#ifndef WIN32 + x -= mPerfomanceButton->getWidth() + mButtonSpacing; + mPerfomanceButton->setPosition(x, mButtonPadding); + + x -= mVideoButton->getWidth() + mButtonSpacing; + mVideoButton->setPosition(x, mButtonPadding); + + x -= mThemesButton->getWidth() + mButtonSpacing; + mThemesButton->setPosition(x, mButtonPadding); + + x -= mAboutButton->getWidth() + mButtonSpacing; + mAboutButton->setPosition(x, mButtonPadding); + + x -= mHelpButton->getWidth() + mButtonSpacing; + mHelpButton->setPosition(x, mButtonPadding); +#ifdef ANDROID + x -= mCloseButton->getWidth() + mButtonSpacing; + mCloseButton->setPosition(x, mButtonPadding); +#endif // ANDROID +#endif // WIN32 + } +} + +void Client::windowRemoved(const Window *const window) +{ + if (mCurrentDialog == window) + mCurrentDialog = nullptr; +} + +void Client::focusWindow() +{ + if (mCurrentDialog != nullptr) + { + mCurrentDialog->requestFocus(); + } +} + +void Client::updatePinState() +{ + if (mCurrentDialog == nullptr || + mState != State::CHAR_SELECT) + { + return; + } + CharSelectDialog *const dialog = + dynamic_cast<CharSelectDialog*>(mCurrentDialog); + if (dialog != nullptr) + pincodeManager.updateState(); +} + +void Client::logVars() +{ +#ifdef ANDROID + logger->log("APPDIR: %s", getenv("APPDIR")); + logger->log("DATADIR2: %s", getSdStoragePath().c_str()); +#endif // ANDROID +} + +void Client::slowLogic() +{ + if ((gameHandler == nullptr) || + !gameHandler->mustPing()) + { + return; + } + + if (get_elapsed_time1(mPing) > 1500) + { + mPing = tick_time; + if (mState == State::UPDATE || + mState == State::LOGIN || + mState == State::LOGIN_ATTEMPT || + mState == State::REGISTER || + mState == State::REGISTER_ATTEMPT) + { + if (loginHandler != nullptr) + loginHandler->ping(); + if (generalHandler != nullptr) + generalHandler->flushSend(); + } + else if (mState == State::CHAR_SELECT) + { + if (charServerHandler != nullptr) + charServerHandler->ping(); + if (generalHandler != nullptr) + generalHandler->flushSend(); + } + } +} + +void Client::loadData() +{ + // If another data path has been set, + // we don't load any other files... + if (settings.options.dataPath.empty()) + { + // Add customdata directory + VirtFs::searchAndAddArchives( + "customdata/", + "zip", + Append_false); + } + + if (!settings.options.skipUpdate) + { + VirtFs::searchAndAddArchives( + settings.updatesDir + "/local/", + "zip", + Append_false); + + VirtFs::mountDir(pathJoin( + settings.localDataDir, + settings.updatesDir, + "local/"), + Append_false); + } + + logger->log("Init paths"); + paths.init("paths.xml", + UseVirtFs_true, + SkipError_false); + setPathsDefaults(paths); + initPaths(); + if (SpriteReference::Empty == nullptr) + { + SpriteReference::Empty = new SpriteReference( + paths.getStringValue("spriteErrorFile"), + 0); + } + + if (BeingInfo::unknown == nullptr) + BeingInfo::unknown = new BeingInfo; + + initFeatures(); + TranslationManager::loadCurrentLang(); + TranslationManager::loadDictionaryLang(); + PlayerInfo::stateChange(mState); + + AttributesEnum::init(); + DbManager::loadDb(); + + delete spellManager; + spellManager = new SpellManager; + delete spellShortcut; + spellShortcut = new SpellShortcut; + + EquipmentWindow::prepareSlotNames(); + + ActorSprite::load(); + + if (desktop != nullptr) + desktop->reloadWallpaper(); +} + +void Client::unloadData() +{ + DbManager::unloadDb(); + mCurrentServer.supportUrl.clear(); + settings.supportUrl.clear(); + if (settings.options.dataPath.empty()) + { + // Add customdata directory + VirtFs::searchAndRemoveArchives( + "customdata/", + "zip"); + } + + if (!settings.oldUpdates.empty()) + { + UpdaterWindow::unloadUpdates(settings.oldUpdates); + settings.oldUpdates.clear(); + } + + if (!settings.options.skipUpdate) + { + VirtFs::searchAndRemoveArchives( + pathJoin(settings.updatesDir, "local/"), + "zip"); + + VirtFs::unmountDirSilent(pathJoin( + settings.localDataDir, + settings.updatesDir, + "local/")); + } + + ResourceManager::clearCache(); + + loginData.clearUpdateHost(); + localClan.clear(); + serverVersion = 0; + packetVersion = 0; + packetVersionMain = 0; + packetVersionRe = 0; + packetVersionZero = 0; + tmwServerVersion = 0; + evolPacketOffset = 0; +} + +void Client::runValidate() +{ + loadData(); + WindowManager::createValidateWindows(); + + WindowManager::deleteValidateWindows(); + unloadData(); + delete2(client) + VirtFs::deinit(); + exit(0); +} |