diff options
author | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2010-02-21 20:40:07 +0100 |
---|---|---|
committer | Chuck Miller <shadowmil@gmail.com> | 2010-02-21 17:05:40 -0500 |
commit | c8b0d1e56f27c3141895d28b2fc768afffe7bb2d (patch) | |
tree | 0a03458836badee3e1b0da13a0721c9261e7fa86 | |
parent | 204a14c91bbe4436eb3b26bebf30cbe5669bdd1a (diff) | |
download | mana-client-c8b0d1e56f27c3141895d28b2fc768afffe7bb2d.tar.gz mana-client-c8b0d1e56f27c3141895d28b2fc768afffe7bb2d.tar.bz2 mana-client-c8b0d1e56f27c3141895d28b2fc768afffe7bb2d.tar.xz mana-client-c8b0d1e56f27c3141895d28b2fc768afffe7bb2d.zip |
Made tick counter and framerate limiter work during login sequence
Much code was moved from main() to the new Client::exec(). This new
event loop now integrates with the Game class, so that the tick counter
and framerate limiter apply universally.
The Client class is also responsible for some things that used to be
global variables.
Mantis-issue: ...
48 files changed, 1553 insertions, 1452 deletions
@@ -282,6 +282,8 @@ ./src/imageparticle.h ./src/inventory.cpp ./src/inventory.h +./src/client.cpp +./src/client.h ./src/item.cpp ./src/item.h ./src/itemshortcut.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1273fe40..4f90d0a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -396,6 +396,8 @@ SET(SRCS being.h beingmanager.cpp beingmanager.h + client.cpp + client.h channel.cpp channel.h channelmanager.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 8f7c7164..75b699b7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -301,6 +301,8 @@ mana_SOURCES = gui/widgets/avatarlistbox.cpp \ being.h \ beingmanager.cpp \ beingmanager.h \ + client.cpp \ + client.h \ channel.cpp \ channel.h \ channelmanager.cpp \ diff --git a/src/being.cpp b/src/being.cpp index c49834f1..33194e0e 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -22,9 +22,9 @@ #include "being.h" #include "animatedsprite.h" +#include "client.h" #include "configuration.h" #include "effectmanager.h" -#include "game.h" #include "graphics.h" #include "localplayer.h" #include "log.h" @@ -62,7 +62,6 @@ static const int DEFAULT_BEING_WIDTH = 32; static const int DEFAULT_BEING_HEIGHT = 32; -extern const int MILLISECONDS_IN_A_TICK; int Being::mNumberOfHairstyles = 1; diff --git a/src/client.cpp b/src/client.cpp new file mode 100644 index 00000000..1f608fa6 --- /dev/null +++ b/src/client.cpp @@ -0,0 +1,1152 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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 "client.h" +#include "main.h" + +#include "configuration.h" +#include "emoteshortcut.h" +#include "game.h" +#include "itemshortcut.h" +#include "keyboardconfig.h" +#ifdef USE_OPENGL +#include "openglgraphics.h" +#endif +#include "playerrelations.h" +#include "sound.h" +#include "statuseffect.h" +#include "units.h" + +#include "gui/changeemaildialog.h" +#include "gui/changepassworddialog.h" +#include "gui/charselectdialog.h" +#include "gui/connectiondialog.h" +#include "gui/gui.h" +#include "gui/skin.h" +#include "gui/login.h" +#include "gui/okdialog.h" +#include "gui/palette.h" +#include "gui/quitdialog.h" +#include "gui/register.h" +#include "gui/sdlinput.h" +#include "gui/serverdialog.h" +#include "gui/setup.h" +#include "gui/unregisterdialog.h" +#include "gui/updatewindow.h" +#include "gui/worldselectdialog.h" + +#include "gui/widgets/button.h" +#include "gui/widgets/desktop.h" + +#include "net/charhandler.h" +#include "net/gamehandler.h" +#include "net/generalhandler.h" +#include "net/logindata.h" +#include "net/loginhandler.h" +#include "net/net.h" +#include "net/worldinfo.h" + +#include "resources/colordb.h" +#include "resources/emotedb.h" +#include "resources/image.h" +#include "resources/itemdb.h" +#include "resources/monsterdb.h" +#include "resources/npcdb.h" +#include "resources/resourcemanager.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#ifdef __APPLE__ +#include <CoreFoundation/CFBundle.h> +#endif + +#include <physfs.h> +#include <SDL_image.h> + +#ifdef WIN32 +#include <SDL_syswm.h> +#else +#include <cerrno> +#include <sys/stat.h> +#endif + +#include <cassert> + +/** + * Tells the max tick value, + * setting it back to zero (and start again). + */ +static const int MAX_TICK_VALUE = 10000; + +static const int defaultSfxVolume = 100; +static const int defaultMusicVolume = 60; + +// TODO: Get rid fo these globals +std::string errorMessage; +ErrorListener errorListener; +LoginData loginData; + +Configuration config; /**< XML file configuration reader */ +Configuration branding; /**< XML branding information reader */ +Logger *logger; /**< Log object */ +KeyboardConfig keyboard; + +Palette *guiPalette; +Graphics *graphics; + +Sound sound; + +void ErrorListener::action(const gcn::ActionEvent &) +{ + Client::setState(STATE_CHOOSE_SERVER); +} + +volatile int tick_time; /**< Tick counter */ +volatile int fps = 0; /**< Frames counted in the last second */ +volatile int frame_count = 0; /**< Counts the frames during one second */ + +/** + * Advances game logic counter. + * Called every 10 milliseconds by SDL_AddTimer() + * @see MILLISECONDS_IN_A_TICK value + */ +Uint32 nextTick(Uint32 interval, void *param) +{ + tick_time++; + if (tick_time == MAX_TICK_VALUE) + tick_time = 0; + return interval; +} + +/** + * Updates fps. + * Called every seconds by SDL_AddTimer() + */ +Uint32 nextSecond(Uint32 interval, void *param) +{ + fps = frame_count; + frame_count = 0; + + return interval; +} + +/** + * @return the elapsed time in milliseconds + * between two tick values. + */ +int get_elapsed_time(int start_time) +{ + if (start_time <= tick_time) + return (tick_time - start_time) * MILLISECONDS_IN_A_TICK; + else + return (tick_time + (MAX_TICK_VALUE - start_time)) + * MILLISECONDS_IN_A_TICK; +} + + +// This anonymous namespace hides whatever is inside from other modules. +namespace { + +class AccountListener : public gcn::ActionListener +{ +public: + void action(const gcn::ActionEvent &) + { + Client::setState(STATE_CHAR_SELECT); + } +} accountListener; + +class LoginListener : public gcn::ActionListener +{ +public: + void action(const gcn::ActionEvent &) + { + Client::setState(STATE_LOGIN); + } +} loginListener; + +} // anonymous namespace + + +Client *Client::mInstance = 0; + +Client::Client(const Options &options): + options(options), + currentDialog(0), + quitDialog(0), + desktop(0), + setupButton(0), + state(STATE_CHOOSE_SERVER), + oldstate(STATE_START), + mLogicCounterId(0), + mSecondsCounterId(0), + mLimitFps(false) +{ + assert(!mInstance); + mInstance = this; + + // Load branding information + branding.init("data/branding.xml"); + + initHomeDir(options); + initScreenshotDir(options.screenshotDir); + + // Configure logger + logger = new Logger; + logger->setLogFile(homeDir + std::string("/mana.log")); + + // Log the mana version + logger->log("Mana %s", FULL_VERSION); + + initConfiguration(options); + logger->setLogToStandardOut(config.getValue("logToStandardOut", 0)); + + // Initialize SDL + logger->log("Initializing SDL..."); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) + { + logger->error(strprintf("Could not initialize SDL: %s", + SDL_GetError())); + } + atexit(SDL_Quit); + + SDL_EnableUNICODE(1); + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + + SDL_WM_SetCaption(branding.getValue("appName", "Mana").c_str(), NULL); + + ResourceManager *resman = ResourceManager::getInstance(); + + if (!resman->setWriteDir(homeDir)) + { + logger->error(strprintf("%s couldn't be set as home directory! " + "Exiting.", homeDir.c_str())); + } + + // Add the user's homedir to PhysicsFS search path + resman->addToSearchPath(homeDir, false); + + // Add the main data directories to our PhysicsFS search path + if (!options.dataPath.empty()) + resman->addToSearchPath(options.dataPath, true); + resman->addToSearchPath("data", true); +#if defined __APPLE__ + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); + char path[PATH_MAX]; + if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, + PATH_MAX)) + { + fprintf(stderr, "Can't find Resources directory\n"); + } + CFRelease(resourcesURL); + strncat(path, "/data", PATH_MAX - 1); + resman->addToSearchPath(path, true); +#else + resman->addToSearchPath(PKG_DATADIR "data", true); +#endif + +#ifdef WIN32 + static SDL_SysWMinfo pInfo; + SDL_GetWMInfo(&pInfo); + HICON icon = LoadIcon(GetModuleHandle(NULL), "A"); + if (icon) + { + SetClassLong(pInfo.window, GCL_HICON, (LONG) icon); + } +#else + icon = IMG_Load(resman->getPath( + branding.getValue("appIcon", "data/icons/mana.png")).c_str()); + if (icon) + { + SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); + SDL_WM_SetIcon(icon, NULL); + } +#endif + +#ifdef USE_OPENGL + bool useOpenGL = !options.noOpenGL && (config.getValue("opengl", 0) == 1); + + // Setup image loading for the right image format + Image::setLoadAsOpenGL(useOpenGL); + + // Create the graphics context + graphics = useOpenGL ? new OpenGLGraphics : new Graphics; +#else + // Create the graphics context + graphics = new Graphics; +#endif + + const int width = (int) config.getValue("screenwidth", defaultScreenWidth); + const int height = (int) config.getValue("screenheight", defaultScreenHeight); + const int bpp = 0; + const bool fullscreen = ((int) config.getValue("screen", 0) == 1); + const bool hwaccel = ((int) config.getValue("hwaccel", 0) == 1); + + // Try to set the desired video mode + if (!graphics->setVideoMode(width, height, bpp, fullscreen, hwaccel)) + { + logger->error(strprintf("Couldn't set %dx%dx%d video mode: %s", + width, height, bpp, SDL_GetError())); + } + + // Initialize for drawing + graphics->_beginDraw(); + + // Initialize the item shortcuts. + itemShortcut = new ItemShortcut; + + // Initialize the emote shortcuts. + emoteShortcut = new EmoteShortcut; + + gui = new Gui(graphics); + + // Initialize sound engine + try + { + if (config.getValue("sound", 0) == 1) + sound.init(); + + sound.setSfxVolume((int) config.getValue("sfxVolume", + defaultSfxVolume)); + sound.setMusicVolume((int) config.getValue("musicVolume", + defaultMusicVolume)); + } + catch (const char *err) + { + state = STATE_ERROR; + errorMessage = err; + logger->log("Warning: %s", err); + } + + // Initialize keyboard + keyboard.init(); + + // Initialise player relations + player_relations.init(); + + guiPalette = new Palette; + setupWindow = new Setup; + + sound.playMusic(branding.getValue("loginMusic", "Magick - Real.ogg")); + + // Initialize default server + currentServer.hostname = options.serverName; + currentServer.port = options.serverPort; + loginData.username = options.username; + loginData.password = options.password; + loginData.remember = config.getValue("remember", 0); + loginData.registerLogin = false; + + if (currentServer.hostname.empty()) + { + currentServer.hostname = branding.getValue("defaultServer", + "server.themanaworld.org").c_str(); + } + if (options.serverPort == 0) + { + currentServer.port = (short) branding.getValue("defaultPort", + DEFAULT_PORT); + } + if (loginData.username.empty() && loginData.remember) + loginData.username = config.getValue("username", ""); + + if (state != STATE_ERROR) + state = STATE_CHOOSE_SERVER; + + // Initialize logic and seconds counters + tick_time = 0; + mLogicCounterId = SDL_AddTimer(MILLISECONDS_IN_A_TICK, nextTick, NULL); + mSecondsCounterId = SDL_AddTimer(1000, nextSecond, NULL); + + // Initialize frame limiting + SDL_initFramerate(&mFpsManager); + config.addListener("fpslimit", this); + optionChanged("fpslimit"); +} + +Client::~Client() +{ + SDL_RemoveTimer(mLogicCounterId); + SDL_RemoveTimer(mSecondsCounterId); + + // Before config.write() since it writes the shortcuts to the config + delete itemShortcut; + delete emoteShortcut; + + config.write(); + + delete gui; + delete graphics; + + // Shutdown libxml + xmlCleanupParser(); + + // Shutdown sound + sound.close(); + + // Unload XML databases + ColorDB::unload(); + EmoteDB::unload(); + ItemDB::unload(); + MonsterDB::unload(); + NPCDB::unload(); + StatusEffect::unload(); + + ResourceManager::deleteInstance(); + + SDL_FreeSurface(icon); + + logger->log("Quitting"); + delete guiPalette; + delete logger; + + mInstance = 0; +} + +int Client::exec() +{ + int lastTickTime = tick_time; + + Game *game = 0; + SDL_Event event; + + while (state != STATE_EXIT) + { + bool handledEvents = false; + + if (game) + { + // Let the game handle the events while it is active + game->handleInput(); + } + else + { + // Handle SDL events + while (SDL_PollEvent(&event)) + { + handledEvents = true; + + switch (event.type) + { + case SDL_QUIT: + state = STATE_EXIT; + break; + + case SDL_KEYDOWN: + break; + } + + guiInput->pushInput(event); + } + } + + if (Net::getGeneralHandler()) + Net::getGeneralHandler()->flushNetwork(); + + while (get_elapsed_time(lastTickTime) > 0) + { + gui->logic(); + if (game) + game->logic(); + + ++lastTickTime; + } + + // This is done because at some point tick_time will wrap. + lastTickTime = tick_time; + + // Update the screen when application is active, delay otherwise. + if (SDL_GetAppState() & SDL_APPACTIVE) + { + frame_count++; + gui->draw(); + graphics->updateScreen(); + } + else + { + SDL_Delay(10); + } + + if (mLimitFps) + SDL_framerateDelay(&mFpsManager); + + + // TODO: Add connect timeouts + if (state == STATE_CONNECT_GAME && + Net::getGameHandler()->isConnected()) + { + Net::getLoginHandler()->disconnect(); + } + else if (state == STATE_CONNECT_SERVER && oldstate == STATE_CHOOSE_SERVER) + { + Net::connectToServer(currentServer); + } + else if (state == STATE_CONNECT_SERVER && + oldstate != STATE_CHOOSE_SERVER && + Net::getLoginHandler()->isConnected()) + { + state = STATE_LOGIN; + } + else if (state == STATE_WORLD_SELECT && oldstate == STATE_UPDATE) + { + if (Net::getLoginHandler()->getWorlds().size() < 2) + { + state = STATE_LOGIN; + } + } + else if (oldstate == STATE_START || + (oldstate == STATE_GAME && state != STATE_GAME)) + { + gcn::Container *top = static_cast<gcn::Container*>(gui->getTop()); + + desktop = new Desktop; + top->add(desktop); + setupButton = new Button(_("Setup"), "Setup", this); + setupButton->setPosition(top->getWidth() - setupButton->getWidth() + - 3, 3); + top->add(setupButton); + + int screenWidth = (int) config.getValue("screenwidth", + defaultScreenWidth); + int screenHeight = (int) config.getValue("screenheight", + defaultScreenHeight); + + desktop->setSize(screenWidth, screenHeight); + } + + if (state == STATE_SWITCH_LOGIN && oldstate == STATE_GAME) + { + Net::getGameHandler()->disconnect(); + } + + if (state != oldstate) + { + if (oldstate == STATE_GAME) + { + delete game; + game = 0; + } + + oldstate = state; + + // Get rid of the dialog of the previous state + if (currentDialog) + { + delete currentDialog; + currentDialog = NULL; + } + // State has changed, while the quitDialog was active, it might + // not be correct anymore + if (quitDialog) + quitDialog->scheduleDelete(); + + switch (state) + { + case STATE_CHOOSE_SERVER: + logger->log("State: CHOOSE SERVER"); + + // Allow changing this using a server choice dialog + // We show the dialog box only if the command-line + // options weren't set. + if (options.serverName.empty() && options.serverPort == 0) + { + // Don't allow an alpha opacity + // lower than the default value + SkinLoader::instance()->setMinimumOpacity(0.8f); + + currentDialog = new ServerDialog(¤tServer, + homeDir); + } + else + { + state = STATE_CONNECT_SERVER; + + // Reset options so that cancelling or connect + // timeout will show the server dialog. + options.serverName.clear(); + options.serverPort = 0; + } + break; + + case STATE_CONNECT_SERVER: + logger->log("State: CONNECT SERVER"); + currentDialog = new ConnectionDialog( + _("Connecting to server"), STATE_SWITCH_SERVER); + break; + + case STATE_LOGIN: + logger->log("State: LOGIN"); + // Don't allow an alpha opacity + // lower than the default value + SkinLoader::instance()->setMinimumOpacity(0.8f); + + if (options.username.empty() + || options.password.empty()) + { + currentDialog = new LoginDialog(&loginData); + } + else + { + state = STATE_LOGIN_ATTEMPT; + // Clear the password so that when login fails, the + // dialog will show up next time. + options.password.clear(); + } + break; + + case STATE_LOGIN_ATTEMPT: + logger->log("State: LOGIN ATTEMPT"); + accountLogin(&loginData); + currentDialog = new ConnectionDialog( + _("Logging in"), STATE_SWITCH_SERVER); + break; + + case STATE_WORLD_SELECT: + logger->log("State: WORLD SELECT"); + { + Worlds worlds = Net::getLoginHandler()->getWorlds(); + + if (worlds.size() == 0) + { + // Trust that the netcode knows what it's doing + state = STATE_UPDATE; + } + else if (worlds.size() == 1) + { + Net::getLoginHandler()->chooseServer(0); + state = STATE_UPDATE; + } + else + { + currentDialog = new WorldSelectDialog(worlds); + if (options.chooseDefault) + { + ((WorldSelectDialog*) currentDialog)->action( + gcn::ActionEvent(NULL, "ok")); + } + } + } + break; + + case STATE_WORLD_SELECT_ATTEMPT: + logger->log("State: WORLD SELECT ATTEMPT"); + currentDialog = new ConnectionDialog( + _("Entering game world"), STATE_WORLD_SELECT); + break; + + case STATE_UPDATE: + // Determine which source to use for the update host + if (!options.updateHost.empty()) + updateHost = options.updateHost; + else + updateHost = loginData.updateHost; + initUpdatesDir(); + + if (options.skipUpdate) + { + state = STATE_LOAD_DATA; + } + else + { + logger->log("State: UPDATE"); + currentDialog = new UpdaterWindow(updateHost, + homeDir + "/" + updatesDir,options.dataPath.empty()); + } + break; + + case STATE_LOAD_DATA: + logger->log("State: LOAD DATA"); + + // If another data path has been set, + // we don't load any other files... + if (options.dataPath.empty()) + { + // Add customdata directory + ResourceManager::getInstance()->searchAndAddArchives( + "customdata/", + "zip", + false); + } + + // Load XML databases + ColorDB::load(); + ItemDB::load(); + Being::load(); // Hairstyles + MonsterDB::load(); + NPCDB::load(); + EmoteDB::load(); + StatusEffect::load(); + Units::loadUnits(); + + desktop->reloadWallpaper(); + + state = STATE_GET_CHARACTERS; + break; + + case STATE_GET_CHARACTERS: + logger->log("State: GET CHARACTERS"); + Net::getCharHandler()->requestCharacters(); + currentDialog = new ConnectionDialog( + _("Requesting characters"), + STATE_SWITCH_SERVER); + break; + + case STATE_CHAR_SELECT: + logger->log("State: CHAR SELECT"); + // Don't allow an alpha opacity + // lower than the default value + SkinLoader::instance()->setMinimumOpacity(0.8f); + + currentDialog = new CharSelectDialog(&loginData); + + if (!((CharSelectDialog*) currentDialog)->selectByName( + options.character, CharSelectDialog::Choose)) + { + ((CharSelectDialog*) currentDialog)->selectByName( + config.getValue("lastCharacter", ""), + options.chooseDefault ? + CharSelectDialog::Choose : + CharSelectDialog::Focus); + } + + break; + + case STATE_CONNECT_GAME: + logger->log("State: CONNECT GAME"); + + Net::getGameHandler()->connect(); + currentDialog = new ConnectionDialog( + _("Connecting to the game server"), + STATE_SWITCH_CHARACTER); + break; + + case STATE_GAME: + logger->log("Memorizing selected character %s", + player_node->getName().c_str()); + config.setValue("lastCharacter", player_node->getName()); + + Net::getGameHandler()->inGame(); + + // Fade out logon-music here too to give the desired effect + // of "flowing" into the game. + sound.fadeOutMusic(1000); + + // Allow any alpha opacity + SkinLoader::instance()->setMinimumOpacity(-1.0f); + + delete setupButton; + delete desktop; + setupButton = NULL; + desktop = NULL; + + currentDialog = NULL; + + logger->log("State: GAME"); + game = new Game; + break; + + case STATE_LOGIN_ERROR: + logger->log("State: LOGIN ERROR"); + currentDialog = new OkDialog(_("Error"), errorMessage); + currentDialog->addActionListener(&loginListener); + currentDialog = NULL; // OkDialog deletes itself + break; + + case STATE_ACCOUNTCHANGE_ERROR: + logger->log("State: ACCOUNT CHANGE ERROR"); + currentDialog = new OkDialog(_("Error"), errorMessage); + currentDialog->addActionListener(&accountListener); + currentDialog = NULL; // OkDialog deletes itself + break; + + case STATE_REGISTER_PREP: + logger->log("State: REGISTER_PREP"); + Net::getLoginHandler()->getRegistrationDetails(); + currentDialog = new ConnectionDialog( + _("Requesting registration details"), STATE_LOGIN); + break; + + case STATE_REGISTER: + logger->log("State: REGISTER"); + currentDialog = new RegisterDialog(&loginData); + break; + + case STATE_REGISTER_ATTEMPT: + logger->log("Username is %s", loginData.username.c_str()); + Net::getLoginHandler()->registerAccount(&loginData); + break; + + case STATE_CHANGEPASSWORD: + logger->log("State: CHANGE PASSWORD"); + currentDialog = new ChangePasswordDialog(&loginData); + break; + + case STATE_CHANGEPASSWORD_ATTEMPT: + logger->log("State: CHANGE PASSWORD ATTEMPT"); + Net::getLoginHandler()->changePassword(loginData.username, + loginData.password, + loginData.newPassword); + break; + + case STATE_CHANGEPASSWORD_SUCCESS: + logger->log("State: CHANGE PASSWORD SUCCESS"); + currentDialog = new OkDialog(_("Password Change"), + _("Password changed successfully!")); + currentDialog->addActionListener(&accountListener); + currentDialog = NULL; // OkDialog deletes itself + loginData.password = loginData.newPassword; + loginData.newPassword = ""; + break; + + case STATE_CHANGEEMAIL: + logger->log("State: CHANGE EMAIL"); + currentDialog = new ChangeEmailDialog(&loginData); + break; + + case STATE_CHANGEEMAIL_ATTEMPT: + logger->log("State: CHANGE EMAIL ATTEMPT"); + Net::getLoginHandler()->changeEmail(loginData.email); + break; + + case STATE_CHANGEEMAIL_SUCCESS: + logger->log("State: CHANGE EMAIL SUCCESS"); + currentDialog = new OkDialog(_("Email Change"), + _("Email changed successfully!")); + currentDialog->addActionListener(&accountListener); + currentDialog = NULL; // OkDialog deletes itself + break; + + case STATE_UNREGISTER: + logger->log("State: UNREGISTER"); + currentDialog = new UnRegisterDialog(&loginData); + break; + + case STATE_UNREGISTER_ATTEMPT: + logger->log("State: UNREGISTER ATTEMPT"); + Net::getLoginHandler()->unregisterAccount( + loginData.username, loginData.password); + break; + + case STATE_UNREGISTER_SUCCESS: + logger->log("State: UNREGISTER SUCCESS"); + Net::getLoginHandler()->disconnect(); + + currentDialog = new OkDialog(_("Unregister Successful"), + _("Farewell, come back any time...")); + loginData.clear(); + //The errorlistener sets the state to STATE_CHOOSE_SERVER + currentDialog->addActionListener(&errorListener); + currentDialog = NULL; // OkDialog deletes itself + break; + + case STATE_SWITCH_SERVER: + logger->log("State: SWITCH SERVER"); + + Net::getLoginHandler()->disconnect(); + Net::getGameHandler()->disconnect(); + + state = STATE_CHOOSE_SERVER; + break; + + case STATE_SWITCH_LOGIN: + logger->log("State: SWITCH LOGIN"); + + Net::getLoginHandler()->logout(); + + state = STATE_LOGIN; + break; + + case STATE_SWITCH_CHARACTER: + logger->log("State: SWITCH CHARACTER"); + + // Done with game + Net::getGameHandler()->disconnect(); + + Net::getCharHandler()->requestCharacters(); + break; + + case STATE_LOGOUT_ATTEMPT: + logger->log("State: LOGOUT ATTEMPT"); + // TODO + break; + + case STATE_WAIT: + logger->log("State: WAIT"); + break; + + case STATE_EXIT: + logger->log("State: EXIT"); + Net::unload(); + break; + + case STATE_FORCE_QUIT: + logger->log("State: FORCE QUIT"); + if (Net::getGeneralHandler()) + Net::getGeneralHandler()->unload(); + state = STATE_EXIT; + break; + + case STATE_ERROR: + logger->log("State: ERROR"); + currentDialog = new OkDialog(_("Error"), errorMessage); + currentDialog->addActionListener(&errorListener); + currentDialog = NULL; // OkDialog deletes itself + Net::getGameHandler()->disconnect(); + break; + + default: + state = STATE_FORCE_QUIT; + break; + } + } + } + + return 0; +} + +void Client::optionChanged(const std::string &name) +{ + const int fpsLimit = (int) config.getValue("fpslimit", 60); + mLimitFps = fpsLimit > 0; + if (mLimitFps) + SDL_setFramerate(&mFpsManager, fpsLimit); +} + +void Client::action(const gcn::ActionEvent &event) +{ + Window *window = 0; + + if (event.getId() == "Setup") + window = setupWindow; + + if (window) + { + window->setVisible(!window->isVisible()); + if (window->isVisible()) + window->requestMoveToTop(); + } +} + +/** + * Initializes the home directory. On UNIX and FreeBSD, ~/.mana is used. On + * Windows and other systems we use the current working directory. + */ +void Client::initHomeDir(const Options &options) +{ + homeDir = options.homeDir; + + if (homeDir.empty()) + { +#ifdef __APPLE__ + // Use Application Directory instead of .mana + homeDir = std::string(PHYSFS_getUserDir()) + + "/Library/Application Support/" + + branding.getValue("appName", "Mana"); +#else + homeDir = std::string(PHYSFS_getUserDir()) + + "/." + branding.getValue("appShort", "mana"); +#endif + } +#if defined WIN32 + if (!CreateDirectory(homeDir.c_str(), 0) && + GetLastError() != ERROR_ALREADY_EXISTS) +#else + // Create home directory if it doesn't exist already + if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && + (errno != EEXIST)) +#endif + { + logger->error(strprintf(_("%s doesn't exist and can't be created! " + "Exiting."), homeDir.c_str())); + } +} + +/** + * Initialize configuration. + */ +void Client::initConfiguration(const Options &options) +{ + // Fill configuration with defaults + logger->log("Initializing configuration..."); + std::string defaultHost = branding.getValue("defaultServer", + "server.themanaworld.org"); + int defaultPort = (int) branding.getValue("defaultPort", DEFAULT_PORT); + config.setValue("port", defaultPort); + config.setValue("hwaccel", false); +#if (defined __APPLE__ || defined WIN32) && defined USE_OPENGL + config.setValue("opengl", true); +#else + config.setValue("opengl", false); +#endif + config.setValue("screen", false); + config.setValue("sound", true); + config.setValue("guialpha", 0.8f); + config.setValue("remember", true); + config.setValue("sfxVolume", 100); + config.setValue("musicVolume", 60); + config.setValue("fpslimit", 60); + std::string defaultUpdateHost = branding.getValue("defaultUpdateHost", + "http://updates.themanaworld.org"); + config.setValue("updatehost", defaultUpdateHost); + config.setValue("customcursor", true); + config.setValue("ChatLogLength", 128); + + // Checking if the configuration file exists... otherwise create it with + // default options. + FILE *configFile = 0; + std::string configPath = options.configPath; + + if (configPath.empty()) + configPath = homeDir + "/config.xml"; + + configFile = fopen(configPath.c_str(), "r"); + + // If we can't read it, it doesn't exist ! + if (configFile == NULL) + { + // We reopen the file in write mode and we create it + configFile = fopen(configPath.c_str(), "wt"); + } + if (configFile == NULL) + { + logger->log("Can't create %s. Using defaults.", configPath.c_str()); + } + else + { + fclose(configFile); + config.init(configPath); + } +} + +/** + * Parse the update host and determine the updates directory + * Then verify that the directory exists (creating if needed). + */ +void Client::initUpdatesDir() +{ + std::stringstream updates; + + // If updatesHost is currently empty, fill it from config file + if (updateHost.empty()) + { + updateHost = + config.getValue("updatehost", "http://updates.themanaworld.org/"); + } + + // Remove any trailing slash at the end of the update host + if (updateHost.at(updateHost.size() - 1) == '/') + updateHost.resize(updateHost.size() - 1); + + // Parse out any "http://" or "ftp://", and set the updates directory + size_t pos; + pos = updateHost.find("://"); + if (pos != updateHost.npos) + { + if (pos + 3 < updateHost.length()) + { + updates << "updates/" << updateHost.substr(pos + 3); + updatesDir = updates.str(); + } + else + { + logger->log("Error: Invalid update host: %s", updateHost.c_str()); + errorMessage = strprintf(_("Invalid update host: %s"), + updateHost.c_str()); + state = STATE_ERROR; + } + } + else + { + logger->log("Warning: no protocol was specified for the update host"); + updates << "updates/" << updateHost; + updatesDir = updates.str(); + } + + ResourceManager *resman = ResourceManager::getInstance(); + + // Verify that the updates directory exists. Create if necessary. + if (!resman->isDirectory("/" + updatesDir)) + { + if (!resman->mkdir("/" + updatesDir)) + { +#if defined WIN32 + std::string newDir = homeDir + "\\" + updatesDir; + std::string::size_type loc = newDir.find("/", 0); + + while (loc != std::string::npos) + { + newDir.replace(loc, 1, "\\"); + loc = newDir.find("/", loc); + } + + if (!CreateDirectory(newDir.c_str(), 0) && + GetLastError() != ERROR_ALREADY_EXISTS) + { + logger->log("Error: %s can't be made, but doesn't exist!", + newDir.c_str()); + errorMessage = _("Error creating updates directory!"); + state = STATE_ERROR; + } +#else + logger->log("Error: %s/%s can't be made, but doesn't exist!", + homeDir.c_str(), updatesDir.c_str()); + errorMessage = _("Error creating updates directory!"); + state = STATE_ERROR; +#endif + } + } +} + +void Client::initScreenshotDir(const std::string &dir) +{ + if (dir.empty()) + { + screenshotDir = std::string(PHYSFS_getUserDir()) + "Desktop"; + // If ~/Desktop does not exist, we save screenshots in the user's home. + struct stat statbuf; + if (stat(screenshotDir.c_str(), &statbuf)) + screenshotDir = std::string(PHYSFS_getUserDir()); + } + else + screenshotDir = dir; +} + +void Client::accountLogin(LoginData *loginData) +{ + logger->log("Username is %s", loginData->username.c_str()); + + // Send login infos + if (loginData->registerLogin) + Net::getLoginHandler()->registerAccount(loginData); + else + Net::getLoginHandler()->loginAccount(loginData); + + // Clear the password, avoids auto login when returning to login + loginData->password = ""; + + // TODO This is not the best place to save the config, but at least better + // than the login gui window + if (loginData->remember) + config.setValue("username", loginData->username); + config.setValue("remember", loginData->remember); +} diff --git a/src/client.h b/src/client.h new file mode 100644 index 00000000..47328c4d --- /dev/null +++ b/src/client.h @@ -0,0 +1,212 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/>. + */ + +#ifndef CLIENT_H +#define CLIENT_H + +#include "net/serverinfo.h" + +#include "configlistener.h" + +#include <guichan/actionlistener.hpp> + +#include <string> + +#include <SDL.h> +#include <SDL_framerate.h> + +class Button; +class Desktop; +class LoginData; +class Window; +class QuitDialog; + +/** + * Set the milliseconds value of a tick time. + */ +static const int MILLISECONDS_IN_A_TICK = 10; + +//manaserv uses 9601 +//static const short DEFAULT_PORT = 9601; +static const short DEFAULT_PORT = 6901; + +extern volatile int fps; +extern volatile int tick_time; + +class ErrorListener : public gcn::ActionListener +{ + public: + void action(const gcn::ActionEvent &event); +}; + +extern std::string errorMessage; +extern ErrorListener errorListener; +extern LoginData loginData; + +/** + * Returns elapsed time. (Warning: supposes the delay is always < 100 seconds) + */ +int get_elapsed_time(int start_time); + +/** + * All client states. + */ +enum State { + STATE_ERROR = -1, + STATE_START = 0, + STATE_CHOOSE_SERVER, + STATE_CONNECT_SERVER, + STATE_LOGIN, + STATE_LOGIN_ATTEMPT, + STATE_WORLD_SELECT, // 5 + STATE_WORLD_SELECT_ATTEMPT, + STATE_UPDATE, + STATE_LOAD_DATA, + STATE_GET_CHARACTERS, + STATE_CHAR_SELECT, // 10 + STATE_CONNECT_GAME, + STATE_GAME, + STATE_CHANGE_MAP, // Switch map-server/gameserver + STATE_LOGIN_ERROR, + STATE_ACCOUNTCHANGE_ERROR, // 15 + STATE_REGISTER_PREP, + STATE_REGISTER, + STATE_REGISTER_ATTEMPT, + STATE_CHANGEPASSWORD, + STATE_CHANGEPASSWORD_ATTEMPT, // 20 + STATE_CHANGEPASSWORD_SUCCESS, + STATE_CHANGEEMAIL, + STATE_CHANGEEMAIL_ATTEMPT, + STATE_CHANGEEMAIL_SUCCESS, + STATE_UNREGISTER, // 25 + STATE_UNREGISTER_ATTEMPT, + STATE_UNREGISTER_SUCCESS, + STATE_SWITCH_SERVER, + STATE_SWITCH_LOGIN, + STATE_SWITCH_CHARACTER, // 30 + STATE_LOGOUT_ATTEMPT, + STATE_WAIT, + STATE_EXIT, + STATE_FORCE_QUIT +}; + +/** + * The core part of the client. This class initializes all subsystems, runs + * the event loop, and shuts everything down again. + */ +class Client : public ConfigListener, public gcn::ActionListener +{ +public: + /** + * A structure holding the values of various options that can be passed + * from the command line. + */ + struct Options + { + Options(): + printHelp(false), + printVersion(false), + skipUpdate(false), + chooseDefault(false), + noOpenGL(false), + serverPort(0) + {} + + bool printHelp; + bool printVersion; + bool skipUpdate; + bool chooseDefault; + bool noOpenGL; + std::string username; + std::string password; + std::string character; + std::string configPath; + std::string updateHost; + std::string dataPath; + std::string homeDir; + std::string screenshotDir; + + std::string serverName; + short serverPort; + }; + + Client(const Options &options); + ~Client(); + + /** + * Provides access to the client instance. + */ + static Client *instance() { return mInstance; } + + int exec(); + + static void setState(State state) + { instance()->state = state; } + + static State getState() + { return instance()->state; } + + static const std::string &getHomeDirectory() + { return instance()->homeDir; } + + static const std::string &getScreenshotDirectory() + { return instance()->screenshotDir; } + + void optionChanged(const std::string &name); + void action(const gcn::ActionEvent &event); + +private: + void initHomeDir(const Options &options); + void initConfiguration(const Options &options); + void initUpdatesDir(); + void initScreenshotDir(const std::string &dir); + + void accountLogin(LoginData *loginData); + + static Client *mInstance; + + Options options; + + std::string homeDir; + std::string updateHost; + std::string updatesDir; + std::string screenshotDir; + + ServerInfo currentServer; + + Window *currentDialog; + QuitDialog *quitDialog; + Desktop *desktop; + Button *setupButton; + + State state; + State oldstate; + + SDL_Surface *icon; + + SDL_TimerID mLogicCounterId; + SDL_TimerID mSecondsCounterId; + + bool mLimitFps; + FPSmanager mFpsManager; +}; + +#endif // CLIENT_H diff --git a/src/game.cpp b/src/game.cpp index 45e6b761..56978dca 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -23,6 +23,7 @@ #include "beingmanager.h" #include "channelmanager.h" +#include "client.h" #include "commandhandler.h" #include "configuration.h" #include "effectmanager.h" @@ -34,7 +35,6 @@ #include "keyboardconfig.h" #include "localplayer.h" #include "log.h" -#include "main.h" #include "map.h" #include "npc.h" #include "particle.h" @@ -96,9 +96,6 @@ std::string map_path; -volatile int tick_time; -volatile int fps = 0, frame = 0; - Joystick *joystick = NULL; OkDialog *weightNotice = NULL; @@ -133,55 +130,6 @@ Viewport *viewport = NULL; /**< Viewport on the map. */ ChatTab *localChatTab = NULL; -std::string screenshotDir = ""; - -/** - * Tells the max tick value, - * setting it back to zero (and start again). - */ -const int MAX_TICK_VALUE = 10000; -/** - * Set the milliseconds value of a tick time. - */ -const int MILLISECONDS_IN_A_TICK = 10; - -/** - * Advances game logic counter. - * Called every 10 milliseconds by SDL_AddTimer() - * @see MILLISECONDS_IN_A_TICK value - */ -Uint32 nextTick(Uint32 interval, void *param) -{ - tick_time++; - if (tick_time == MAX_TICK_VALUE) tick_time = 0; - return interval; -} - -/** - * Updates fps. - * Called every seconds by SDL_AddTimer() - */ -Uint32 nextSecond(Uint32 interval, void *param) -{ - fps = frame; - frame = 0; - - return interval; -} - -/** - * @return the elapsed time in milliseconds - * between two tick values. - */ -int get_elapsed_time(int start_time) -{ - if (start_time <= tick_time) - return (tick_time - start_time) * MILLISECONDS_IN_A_TICK; - else - return (tick_time + (MAX_TICK_VALUE - start_time)) - * MILLISECONDS_IN_A_TICK; -} - /** * Initialize every game sub-engines in the right order */ @@ -266,8 +214,6 @@ Game *Game::mInstance = 0; Game::Game(): mLastTarget(Being::UNKNOWN), - mLogicCounterId(0), mSecondsCounterId(0), - mLimitFps(false), mCurrentMap(0), mMapName("") { assert(!mInstance); @@ -276,7 +222,7 @@ Game::Game(): disconnectedDialog = NULL; // Create the viewport - viewport = new Viewport(); + viewport = new Viewport; viewport->setDimension(gcn::Rectangle(0, 0, graphics->getWidth(), graphics->getHeight())); @@ -291,11 +237,6 @@ Game::Game(): initEngines(); - // Initialize logic and seconds counters - tick_time = 0; - mLogicCounterId = SDL_AddTimer(MILLISECONDS_IN_A_TICK, nextTick, NULL); - mSecondsCounterId = SDL_AddTimer(1000, nextSecond, NULL); - // This part is eAthena specific // For Manaserv, the map is obtained // with the GPMSG_PLAYER_MAP_CHANGE flag. @@ -317,11 +258,6 @@ Game::Game(): */ Net::getGameHandler()->ping(tick_time); - // Initialize frame limiting - SDL_initFramerate(&mFpsManager); - config.addListener("fpslimit", this); - optionChanged("fpslimit"); - Joystick::init(); // TODO: The user should be able to choose which one to use // Open the first device @@ -356,26 +292,9 @@ Game::~Game() floorItemManager = NULL; joystick = NULL; - SDL_RemoveTimer(mLogicCounterId); - SDL_RemoveTimer(mSecondsCounterId); - mInstance = 0; } -void setScreenshotDir(const std::string &dir) -{ - if (dir.empty()) - { - screenshotDir = std::string(PHYSFS_getUserDir()) + "Desktop"; - // If ~/Desktop does not exist, we save screenshots in the user's home. - struct stat statbuf; - if (stat(screenshotDir.c_str(), &statbuf)) - screenshotDir = std::string(PHYSFS_getUserDir()); - } - else - screenshotDir = dir; -} - static bool saveScreenshot() { static unsigned int screenshotCount = 0; @@ -392,7 +311,7 @@ static bool saveScreenshot() screenshotCount++; filenameSuffix.str(""); filename.str(""); - filename << screenshotDir << "/"; + filename << Client::getScreenshotDirectory() << "/"; filenameSuffix << "Mana_Screenshot_" << screenshotCount << ".png"; filename << filenameSuffix.str(); testExists.open(filename.str().c_str(), std::ios::in); @@ -420,74 +339,36 @@ static bool saveScreenshot() return success; } -void Game::optionChanged(const std::string &name) +void Game::logic() { - const int fpsLimit = (int) config.getValue("fpslimit", 60); - mLimitFps = fpsLimit > 0; - if (mLimitFps) - SDL_setFramerate(&mFpsManager, fpsLimit); -} + handleInput(); -void Game::exec() -{ - int gameTime = tick_time; + // Handle all necessary game logic + beingManager->logic(); + particleEngine->update(); + if (mCurrentMap) + mCurrentMap->update(); - while (state == STATE_GAME) + // Handle network stuff + if (!Net::getGameHandler()->isConnected()) { - if (mCurrentMap) - mCurrentMap->update(get_elapsed_time(gameTime)); - - handleInput(); - - // Handle all necessary game logic - while (get_elapsed_time(gameTime) > 0) + if (Client::getState() != STATE_ERROR) { - beingManager->logic(); - particleEngine->update(); - gui->logic(); - - gameTime++; + errorMessage = _("The connection to the server was lost."); } - // This is done because at some point tick_time will wrap. - gameTime = tick_time; - - // Update the screen when application is active, delay otherwise. - if (SDL_GetAppState() & SDL_APPACTIVE) + if (!disconnectedDialog) { - frame++; - gui->draw(); - graphics->updateScreen(); - } - else - { - SDL_Delay(10); - } - - if (mLimitFps) - SDL_framerateDelay(&mFpsManager); - - // Handle network stuff - Net::getGeneralHandler()->flushNetwork(); - if (!Net::getGameHandler()->isConnected()) - { - if (state != STATE_ERROR) - { - errorMessage = _("The connection to the server was lost."); - } - - if (!disconnectedDialog) - { - disconnectedDialog = new OkDialog(_("Network Error"), errorMessage); - disconnectedDialog->addActionListener(&errorListener); - disconnectedDialog->requestMoveToTop(); - } + disconnectedDialog = new OkDialog(_("Network Error"), + errorMessage); + disconnectedDialog->addActionListener(&errorListener); + disconnectedDialog->requestMoveToTop(); } } } /** - * The MONSTER input handling method. + * The huge input handling method. */ void Game::handleInput() { @@ -849,7 +730,7 @@ void Game::handleInput() // Quit event else if (event.type == SDL_QUIT) { - state = STATE_EXIT; + Client::setState(STATE_EXIT); } // Push input to GUI when not used @@ -861,7 +742,7 @@ void Game::handleInput() } catch (gcn::Exception e) { - const char* err = e.getMessage().c_str(); + const char *err = e.getMessage().c_str(); logger->log("Warning: guichan input exception: %s", err); } } @@ -22,23 +22,18 @@ #ifndef GAME_H #define GAME_H -#include "configlistener.h" +#include <string> -#include <SDL.h> -#include <SDL_framerate.h> - -extern std::string map_path; -extern volatile int fps; -extern volatile int tick_time; -extern const int MILLISECONDS_IN_A_TICK; +extern std::string map_path; // TODO: Get rid of this global class Map; class WindowMenu; /** - * The main class responsible for running the game. + * The main class responsible for running the game. The game starts after you + * have selected your character. */ -class Game : public ConfigListener +class Game { public: /** @@ -58,14 +53,13 @@ class Game : public ConfigListener static Game *instance() { return mInstance; } /** - * This method runs the game. It returns when the game stops. + * This method takes the game a small step further. It is called 100 + * times per second. */ - void exec(); + void logic(); void handleInput(); - void optionChanged(const std::string &name); - void changeMap(const std::string &mapName); /** @@ -76,13 +70,8 @@ class Game : public ConfigListener const std::string &getCurrentMapName() { return mMapName; } private: - int mLastTarget; - SDL_TimerID mLogicCounterId; - SDL_TimerID mSecondsCounterId; - - bool mLimitFps; - FPSmanager mFpsManager; + int mLastTarget; WindowMenu *mWindowMenu; @@ -92,11 +81,4 @@ class Game : public ConfigListener static Game *mInstance; }; -/** - * Returns elapsed time. (Warning: supposes the delay is always < 100 seconds) - */ -int get_elapsed_time(int start_time); - -void setScreenshotDir(const std::string &dir); - #endif diff --git a/src/gui/changeemaildialog.cpp b/src/gui/changeemaildialog.cpp index d31d569f..b8edb5f6 100644 --- a/src/gui/changeemaildialog.cpp +++ b/src/gui/changeemaildialog.cpp @@ -21,7 +21,7 @@ #include "gui/changeemaildialog.h" -#include "main.h" +#include "client.h" #include "log.h" #include "gui/register.h" @@ -104,7 +104,7 @@ void ChangeEmailDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "cancel") { - state = STATE_CHAR_SELECT; + Client::setState(STATE_CHAR_SELECT); } else if (event.getId() == "change_email") { @@ -162,7 +162,7 @@ void ChangeEmailDialog::action(const gcn::ActionEvent &event) mChangeEmailButton->setEnabled(false); // Set the new email address mLoginData->email = newFirstEmail; - state = STATE_CHANGEEMAIL_ATTEMPT; + Client::setState(STATE_CHANGEEMAIL_ATTEMPT); } } } diff --git a/src/gui/changepassworddialog.cpp b/src/gui/changepassworddialog.cpp index 7ad15db1..5f7c03f9 100644 --- a/src/gui/changepassworddialog.cpp +++ b/src/gui/changepassworddialog.cpp @@ -21,7 +21,7 @@ #include "changepassworddialog.h" -#include "main.h" +#include "client.h" #include "log.h" #include "gui/register.h" @@ -85,7 +85,7 @@ void ChangePasswordDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "cancel") { - state = STATE_CHAR_SELECT; + Client::setState(STATE_CHAR_SELECT); } else if (event.getId() == "change_password") { @@ -156,7 +156,7 @@ void ChangePasswordDialog::action(const gcn::ActionEvent &event) // Set the new password mLoginData->password = oldPassword; mLoginData->newPassword = newFirstPass; - state = STATE_CHANGEPASSWORD_ATTEMPT; + Client::setState(STATE_CHANGEPASSWORD_ATTEMPT); } } } diff --git a/src/gui/charselectdialog.cpp b/src/gui/charselectdialog.cpp index ca525285..c2c63248 100644 --- a/src/gui/charselectdialog.cpp +++ b/src/gui/charselectdialog.cpp @@ -21,9 +21,9 @@ #include "gui/charselectdialog.h" +#include "client.h" #include "game.h" #include "localplayer.h" -#include "main.h" #include "units.h" #include "log.h" @@ -179,7 +179,7 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) // Check if a button of a character was pressed const gcn::Widget *sourceParent = event.getSource()->getParent(); int selected = -1; - for (unsigned i = 0; i < MAX_CHARACTER_COUNT; ++i) + for (int i = 0; i < MAX_CHARACTER_COUNT; ++i) if (mCharacterEntries[i] == sourceParent) selected = i; @@ -206,19 +206,19 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) } else if (eventId == "switch") { - state = STATE_SWITCH_LOGIN; + Client::setState(STATE_SWITCH_LOGIN); } else if (eventId == "change_password") { - state = STATE_CHANGEPASSWORD; + Client::setState(STATE_CHANGEPASSWORD); } else if (eventId == "change_email") { - state = STATE_CHANGEEMAIL; + Client::setState(STATE_CHANGEEMAIL); } else if (eventId == "unregister") { - state = STATE_UNREGISTER; + Client::setState(STATE_UNREGISTER); } } @@ -308,7 +308,7 @@ bool CharSelectDialog::selectByName(const std::string &name, if (mLocked) return false; - for (unsigned i = 0; i < MAX_CHARACTER_COUNT; ++i) { + for (int i = 0; i < MAX_CHARACTER_COUNT; ++i) { if (Net::Character *character = mCharacterEntries[i]->getCharacter()) { if (character->dummy->getName() == name) { mCharacterEntries[i]->requestFocus(); diff --git a/src/gui/charselectdialog.h b/src/gui/charselectdialog.h index 5a21566f..b6e71715 100644 --- a/src/gui/charselectdialog.h +++ b/src/gui/charselectdialog.h @@ -100,6 +100,7 @@ class CharSelectDialog : public Window, public gcn::ActionListener, gcn::Button *mUnregisterButton; gcn::Button *mChangeEmailButton; + enum { MAX_CHARACTER_COUNT = 3 }; CharacterDisplay *mCharacterEntries[MAX_CHARACTER_COUNT]; LoginData *mLoginData; diff --git a/src/gui/connectiondialog.cpp b/src/gui/connectiondialog.cpp index 7d474247..0ef14d5a 100644 --- a/src/gui/connectiondialog.cpp +++ b/src/gui/connectiondialog.cpp @@ -21,7 +21,6 @@ #include "connectiondialog.h" -#include "main.h" #include "log.h" #include "gui/widgets/button.h" @@ -56,7 +55,7 @@ ConnectionDialog::ConnectionDialog(const std::string &text, void ConnectionDialog::action(const gcn::ActionEvent &) { logger->log("Cancel pressed"); - state = mCancelState; + Client::setState(mCancelState); } void ConnectionDialog::draw(gcn::Graphics *graphics) diff --git a/src/gui/connectiondialog.h b/src/gui/connectiondialog.h index 0f826c9a..623a6645 100644 --- a/src/gui/connectiondialog.h +++ b/src/gui/connectiondialog.h @@ -22,7 +22,7 @@ #ifndef CONNECTION_H #define CONNECTION_H -#include "main.h" +#include "client.h" #include "gui/widgets/window.h" diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index 317ca93b..2ad0ba4a 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -21,6 +21,7 @@ #include "gui/debugwindow.h" +#include "client.h" #include "game.h" #include "particle.h" #include "main.h" diff --git a/src/gui/login.cpp b/src/gui/login.cpp index 20a48a44..9eb3f2c0 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -21,7 +21,7 @@ #include "gui/login.h" -#include "main.h" +#include "client.h" #include "configuration.h" #include "gui/okdialog.h" @@ -107,18 +107,19 @@ void LoginDialog::action(const gcn::ActionEvent &event) mRegisterButton->setEnabled(false); mServerButton->setEnabled(false); mLoginButton->setEnabled(false); - state = STATE_LOGIN_ATTEMPT; + + Client::setState(STATE_LOGIN_ATTEMPT); } else if (event.getId() == "server") { - state = STATE_SWITCH_SERVER; + Client::setState(STATE_SWITCH_SERVER); } else if (event.getId() == "register") { mLoginData->username = mUserField->getText(); mLoginData->password = mPassField->getText(); - state = STATE_REGISTER_PREP; + Client::setState(STATE_REGISTER_PREP); } } @@ -140,9 +141,9 @@ void LoginDialog::keyPressed(gcn::KeyEvent &keyEvent) } } -bool LoginDialog::canSubmit() +bool LoginDialog::canSubmit() const { return !mUserField->getText().empty() && !mPassField->getText().empty() && - state == STATE_LOGIN; + Client::getState() == STATE_LOGIN; } diff --git a/src/gui/login.h b/src/gui/login.h index b26ba776..93bae338 100644 --- a/src/gui/login.h +++ b/src/gui/login.h @@ -66,7 +66,7 @@ class LoginDialog : public Window, public gcn::ActionListener, * Returns whether submit can be enabled. This is true in the login * state, when all necessary fields have some text. */ - bool canSubmit(); + bool canSubmit() const; gcn::TextField *mUserField; gcn::TextField *mPassField; diff --git a/src/gui/palette.cpp b/src/gui/palette.cpp index b853a8f5..e7c49c89 100644 --- a/src/gui/palette.cpp +++ b/src/gui/palette.cpp @@ -23,7 +23,7 @@ #include "palette.h" #include "configuration.h" -#include "game.h" +#include "client.h" #include "gui/gui.h" diff --git a/src/gui/quitdialog.cpp b/src/gui/quitdialog.cpp index 0645dd7f..6ecc62a6 100644 --- a/src/gui/quitdialog.cpp +++ b/src/gui/quitdialog.cpp @@ -21,7 +21,7 @@ #include "gui/quitdialog.h" -#include "main.h" +#include "client.h" #include "gui/sdlinput.h" @@ -50,7 +50,9 @@ QuitDialog::QuitDialog(QuitDialog** pointerToMe): ContainerPlacer place = getPlacer(0, 0); - //All states, when we're not logged in to someone. + const State state = Client::getState(); + + // All states, when we're not logged in to someone. if (state == STATE_CHOOSE_SERVER || state == STATE_CONNECT_SERVER || state == STATE_LOGIN || @@ -106,19 +108,19 @@ void QuitDialog::action(const gcn::ActionEvent &event) { if (mForceQuit->isSelected()) { - state = STATE_FORCE_QUIT; + Client::setState(STATE_FORCE_QUIT); } else if (mLogoutQuit->isSelected()) { - state = STATE_EXIT; + Client::setState(STATE_EXIT); } else if (mSwitchAccountServer->isSelected()) { - state = STATE_SWITCH_SERVER; + Client::setState(STATE_SWITCH_SERVER); } else if (mSwitchCharacter->isSelected()) { - assert(state == STATE_GAME); + assert(Client::getState() == STATE_GAME); Net::getCharHandler()->switchCharacter(); } diff --git a/src/gui/recorder.cpp b/src/gui/recorder.cpp index 5bb02844..7cb6e055 100644 --- a/src/gui/recorder.cpp +++ b/src/gui/recorder.cpp @@ -20,7 +20,7 @@ #include "gui/recorder.h" -#include "main.h" +#include "client.h" #include "gui/chat.h" @@ -102,7 +102,7 @@ void Recorder::setRecordingFile(const std::string &msg) * recorded. */ localChatTab->chatLog(_("Starting to record..."), BY_SERVER); - const std::string file = std::string(getHomeDirectory() + msgCopy); + const std::string file = Client::getHomeDirectory() + msgCopy; mStream.open(file.c_str(), std::ios_base::trunc); diff --git a/src/gui/register.cpp b/src/gui/register.cpp index dd863483..9fbd8cc8 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -21,9 +21,9 @@ #include "gui/register.h" +#include "client.h" #include "configuration.h" #include "log.h" -#include "main.h" #include "gui/login.h" #include "gui/okdialog.h" @@ -144,7 +144,7 @@ void RegisterDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "cancel") { - state = STATE_LOGIN; + Client::setState(STATE_LOGIN); } else if (event.getId() == "register" && canSubmit()) { @@ -232,7 +232,7 @@ void RegisterDialog::action(const gcn::ActionEvent &event) mLoginData->email = mEmailField->getText(); mLoginData->registerLogin = true; - state = STATE_REGISTER_ATTEMPT; + Client::setState(STATE_REGISTER_ATTEMPT); } } } @@ -247,5 +247,5 @@ bool RegisterDialog::canSubmit() const return !mUserField->getText().empty() && !mPasswordField->getText().empty() && !mConfirmField->getText().empty() && - state == STATE_REGISTER; + Client::getState() == STATE_REGISTER; } diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp index 091197c5..f439420b 100644 --- a/src/gui/serverdialog.cpp +++ b/src/gui/serverdialog.cpp @@ -21,9 +21,9 @@ #include "gui/serverdialog.h" +#include "client.h" #include "configuration.h" #include "log.h" -#include "main.h" #include "gui/okdialog.h" #include "gui/sdlinput.h" @@ -149,7 +149,8 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir): currentServer.hostname = config.getValue(currentConfig, ""); currentConfig = "MostUsedServerPort" + toString(i); - currentServer.port = (short) config.getValue(currentConfig, DEFAULT_PORT); + currentServer.port = (short) config.getValue(currentConfig, + DEFAULT_PORT); currentConfig = "MostUsedServerType" + toString(i); currentServer.type = stringToServerType(config @@ -308,13 +309,13 @@ void ServerDialog::action(const gcn::ActionEvent &event) mServerInfo->hostname = currentServer.hostname; mServerInfo->port = currentServer.port; mServerInfo->type = currentServer.type; - state = STATE_CONNECT_SERVER; + Client::setState(STATE_CONNECT_SERVER); } } else if (event.getId() == "quit") { mDownload->cancel(); - state = STATE_FORCE_QUIT; + Client::setState(STATE_FORCE_QUIT); } else if (event.getId() == "addEntry") { @@ -328,7 +329,7 @@ void ServerDialog::keyPressed(gcn::KeyEvent &keyEvent) if (key.getValue() == Key::ESCAPE) { - state = STATE_EXIT; + Client::setState(STATE_EXIT); } else if (key.getValue() == Key::ENTER) { diff --git a/src/gui/unregisterdialog.cpp b/src/gui/unregisterdialog.cpp index 97b3bf76..8601e76b 100644 --- a/src/gui/unregisterdialog.cpp +++ b/src/gui/unregisterdialog.cpp @@ -21,7 +21,7 @@ #include "gui/unregisterdialog.h" -#include "main.h" +#include "client.h" #include "log.h" #include "gui/okdialog.h" @@ -95,7 +95,7 @@ void UnRegisterDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "cancel") { - state = STATE_CHAR_SELECT; + Client::setState(STATE_CHAR_SELECT); } else if (event.getId() == "unregister") { @@ -138,7 +138,7 @@ void UnRegisterDialog::action(const gcn::ActionEvent &event) // No errors detected, unregister the new user. mUnRegisterButton->setEnabled(false); mLoginData->password = password; - state = STATE_UNREGISTER_ATTEMPT; + Client::setState(STATE_UNREGISTER_ATTEMPT); } } } diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index 1d5f001a..743f3936 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -21,9 +21,9 @@ #include "gui/updatewindow.h" +#include "client.h" #include "configuration.h" #include "log.h" -#include "main.h" #include "gui/sdlinput.h" @@ -223,7 +223,7 @@ void UpdaterWindow::action(const gcn::ActionEvent &event) } else if (event.getId() == "play") { - state = STATE_LOAD_DATA; + Client::setState(STATE_LOAD_DATA); } } @@ -234,7 +234,7 @@ void UpdaterWindow::keyPressed(gcn::KeyEvent &keyEvent) if (key.getValue() == Key::ESCAPE) { action(gcn::ActionEvent(NULL, mCancelButton->getActionEventId())); - state = STATE_WORLD_SELECT; + Client::setState(STATE_WORLD_SELECT); } else if (key.getValue() == Key::ENTER) { @@ -296,15 +296,18 @@ int UpdaterWindow::updateProgress(void *ptr, DownloadStatus status, float progress = (float) dn / dt; - if (progress != progress) progress = 0.0f; // check for NaN - if (progress < 0.0f) progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway. - if (progress > 1.0f) progress = 1.0f; + if (progress != progress) + progress = 0.0f; // check for NaN + if (progress < 0.0f) + progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway. + if (progress > 1.0f) + progress = 1.0f; uw->setLabel( uw->mCurrentFile + " (" + toString((int) (progress * 100)) + "%)"); uw->setProgress(progress); - if (state != STATE_UPDATE || uw->mDownloadStatus == UPDATE_ERROR) + if (Client::getState() != STATE_UPDATE || uw->mDownloadStatus == UPDATE_ERROR) { // If the action was canceled return an error code to stop the mThread return -1; diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index 8320e7b1..b51d4878 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -21,10 +21,10 @@ #include "gui/viewport.h" +#include "client.h" #include "beingmanager.h" #include "configuration.h" #include "flooritemmanager.h" -#include "game.h" #include "graphics.h" #include "keyboardconfig.h" #include "localplayer.h" diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp index 9cbc7030..cf6dd6f5 100644 --- a/src/gui/widgets/avatarlistbox.cpp +++ b/src/gui/widgets/avatarlistbox.cpp @@ -29,6 +29,8 @@ #include "resources/image.h" #include "resources/resourcemanager.h" +#include "utils/stringutils.h" + #include <guichan/font.hpp> int AvatarListBox::instances = 0; diff --git a/src/gui/worldselectdialog.cpp b/src/gui/worldselectdialog.cpp index ac229302..3219b83d 100644 --- a/src/gui/worldselectdialog.cpp +++ b/src/gui/worldselectdialog.cpp @@ -21,7 +21,7 @@ #include "gui/worldselectdialog.h" -#include "main.h" +#include "client.h" #include "gui/sdlinput.h" @@ -115,12 +115,12 @@ void WorldSelectDialog::action(const gcn::ActionEvent &event) Net::getLoginHandler()->chooseServer(mWorldList->getSelected()); // Check in case netcode moves us forward - if (state == STATE_WORLD_SELECT) - state = STATE_WORLD_SELECT_ATTEMPT; + if (Client::getState() == STATE_WORLD_SELECT) + Client::setState(STATE_WORLD_SELECT_ATTEMPT); } else if (event.getId() == "login") { - state = STATE_LOGIN; + Client::setState(STATE_LOGIN); } } diff --git a/src/localplayer.cpp b/src/localplayer.cpp index bb6373eb..8133bf4a 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -21,11 +21,11 @@ #include "localplayer.h" +#include "client.h" #include "configuration.h" #include "effectmanager.h" #include "equipment.h" #include "flooritem.h" -#include "game.h" #include "graphics.h" #include "guild.h" #include "inventory.h" diff --git a/src/main.cpp b/src/main.cpp index 8d5cbf84..5ed1c4ec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,496 +21,15 @@ #include "main.h" -#include "configuration.h" -#include "emoteshortcut.h" -#include "game.h" -#include "graphics.h" -#include "itemshortcut.h" -#include "keyboardconfig.h" -#include "localplayer.h" -#include "log.h" -#ifdef USE_OPENGL -#include "openglgraphics.h" -#endif -#include "playerrelations.h" -#include "sound.h" -#include "statuseffect.h" -#include "units.h" - -#include "gui/changeemaildialog.h" -#include "gui/changepassworddialog.h" -#include "gui/charselectdialog.h" -#include "gui/connectiondialog.h" -#include "gui/gui.h" -#include "gui/skin.h" -#include "gui/login.h" -#include "gui/okdialog.h" -#include "gui/palette.h" -#include "gui/quitdialog.h" -#include "gui/register.h" -#include "gui/sdlinput.h" -#include "gui/serverdialog.h" -#include "gui/setup.h" -#include "gui/unregisterdialog.h" -#include "gui/updatewindow.h" -#include "gui/worldselectdialog.h" - -#include "gui/widgets/button.h" -#include "gui/widgets/desktop.h" - -#include "net/charhandler.h" -#include "net/gamehandler.h" -#include "net/generalhandler.h" -#include "net/logindata.h" -#include "net/loginhandler.h" -#include "net/net.h" -#include "net/worldinfo.h" - -#include "resources/colordb.h" -#include "resources/emotedb.h" -#include "resources/image.h" -#include "resources/itemdb.h" -#include "resources/monsterdb.h" -#include "resources/npcdb.h" -#include "resources/resourcemanager.h" - #include "utils/gettext.h" -#include "utils/stringutils.h" -#include <SDL_image.h> +#include "client.h" #include <libxml/parser.h> -#ifdef WIN32 -#include <SDL_syswm.h> -#else -#include <cerrno> -#include <sys/stat.h> -#endif - #include <getopt.h> #include <iostream> #include <physfs.h> -#include <unistd.h> -#include <vector> - -#ifdef __APPLE__ -#include <CoreFoundation/CFBundle.h> -#endif - -#ifdef __MINGW32__ -#include <windows.h> -#define usleep(usec) (Sleep ((usec) / 1000), 0) -#endif - -namespace -{ - class SetupListener : public gcn::ActionListener - { - public: - /** - * Called when receiving actions from widget. - */ - void action(const gcn::ActionEvent &event); - } listener; -} - -static const int defaultSfxVolume = 100; -static const int defaultMusicVolume = 60; - -Graphics *graphics; -Game *game = 0; - -State state = STATE_START; -std::string errorMessage; -ErrorListener errorListener; - -Sound sound; -Music *bgm; - -Configuration config; /**< XML file configuration reader */ -Configuration branding; /**< XML branding information reader */ -Logger *logger; /**< Log object */ -KeyboardConfig keyboard; - -LoginData loginData; - -Palette *guiPalette; - -// This anonymous namespace hides whatever is inside from other modules. -namespace { - -std::string homeDir; -std::string updateHost; -std::string updatesDir; - -SDL_Surface *icon; - -/** - * A structure holding the values of various options that can be passed from - * the command line. - */ -struct Options -{ - /** - * Constructor. - */ - Options(): - printHelp(false), - printVersion(false), - skipUpdate(false), - chooseDefault(false), - noOpenGL(false), - serverPort(0) - {} - - bool printHelp; - bool printVersion; - bool skipUpdate; - bool chooseDefault; - bool noOpenGL; - std::string username; - std::string password; - std::string character; - std::string configPath; - std::string updateHost; - std::string dataPath; - std::string homeDir; - std::string screenshotDir; - - std::string serverName; - short serverPort; - -}; - -/** - * Parse the update host and determine the updates directory - * Then verify that the directory exists (creating if needed). - */ -static void setUpdatesDir() -{ - std::stringstream updates; - - // If updatesHost is currently empty, fill it from config file - if (updateHost.empty()) - { - updateHost = - config.getValue("updatehost", "http://updates.themanaworld.org/"); - } - - // Remove any trailing slash at the end of the update host - if (updateHost.at(updateHost.size() - 1) == '/') - updateHost.resize(updateHost.size() - 1); - - // Parse out any "http://" or "ftp://", and set the updates directory - size_t pos; - pos = updateHost.find("://"); - if (pos != updateHost.npos) - { - if (pos + 3 < updateHost.length()) - { - updates << "updates/" << updateHost.substr(pos + 3); - updatesDir = updates.str(); - } - else - { - logger->log("Error: Invalid update host: %s", updateHost.c_str()); - errorMessage = strprintf(_("Invalid update host: %s"), updateHost.c_str()); - state = STATE_ERROR; - } - } - else - { - logger->log("Warning: no protocol was specified for the update host"); - updates << "updates/" << updateHost; - updatesDir = updates.str(); - } - - ResourceManager *resman = ResourceManager::getInstance(); - - // Verify that the updates directory exists. Create if necessary. - if (!resman->isDirectory("/" + updatesDir)) - { - if (!resman->mkdir("/" + updatesDir)) - { -#if defined WIN32 - std::string newDir = homeDir + "\\" + updatesDir; - std::string::size_type loc = newDir.find("/", 0); - - while (loc != std::string::npos) - { - newDir.replace(loc, 1, "\\"); - loc = newDir.find("/", loc); - } - - if (!CreateDirectory(newDir.c_str(), 0) && - GetLastError() != ERROR_ALREADY_EXISTS) - { - logger->log("Error: %s can't be made, but doesn't exist!", - newDir.c_str()); - errorMessage = _("Error creating updates directory!"); - state = STATE_ERROR; - } -#else - logger->log("Error: %s/%s can't be made, but doesn't exist!", - homeDir.c_str(), updatesDir.c_str()); - errorMessage = _("Error creating updates directory!"); - state = STATE_ERROR; -#endif - } - } -} - -/** - * Initializes the home directory. On UNIX and FreeBSD, ~/.mana is used. On - * Windows and other systems we use the current working directory. - */ -static void initHomeDir(const Options &options) -{ - homeDir = options.homeDir; - - if (homeDir.empty()) - { -#ifdef __APPLE__ - // Use Application Directory instead of .mana - homeDir = std::string(PHYSFS_getUserDir()) + - "/Library/Application Support/" + - branding.getValue("appName", "Mana"); -#else - homeDir = std::string(PHYSFS_getUserDir()) + - "/." + branding.getValue("appShort", "mana"); -#endif - } -#if defined WIN32 - if (!CreateDirectory(homeDir.c_str(), 0) && - GetLastError() != ERROR_ALREADY_EXISTS) -#else - // Create home directory if it doesn't exist already - if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && - (errno != EEXIST)) -#endif - { - logger->error(strprintf(_("%s doesn't exist and can't be created! " - "Exiting."), homeDir.c_str())); - } -} - -/** - * Initialize configuration. - */ -static void initConfiguration(const Options &options) -{ - // Fill configuration with defaults - logger->log("Initializing configuration..."); - std::string defaultHost = branding.getValue("defaultServer", - "server.themanaworld.org"); - int defaultPort = (int)branding.getValue("defaultPort", DEFAULT_PORT); - config.setValue("port", defaultPort); - config.setValue("hwaccel", false); -#if (defined __APPLE__ || defined WIN32) && defined USE_OPENGL - config.setValue("opengl", true); -#else - config.setValue("opengl", false); -#endif - config.setValue("screen", false); - config.setValue("sound", true); - config.setValue("guialpha", 0.8f); - config.setValue("remember", true); - config.setValue("sfxVolume", 100); - config.setValue("musicVolume", 60); - config.setValue("fpslimit", 60); - std::string defaultUpdateHost = branding.getValue("defaultUpdateHost", - "http://updates.themanaworld.org"); - config.setValue("updatehost", defaultUpdateHost); - config.setValue("customcursor", true); - config.setValue("ChatLogLength", 128); - - // Checking if the configuration file exists... otherwise create it with - // default options. - FILE *configFile = 0; - std::string configPath = options.configPath; - - if (configPath.empty()) - configPath = homeDir + "/config.xml"; - - configFile = fopen(configPath.c_str(), "r"); - - // If we can't read it, it doesn't exist ! - if (configFile == NULL) - { - // We reopen the file in write mode and we create it - configFile = fopen(configPath.c_str(), "wt"); - } - if (configFile == NULL) - { - logger->log("Can't create %s. Using defaults.", configPath.c_str()); - } - else - { - fclose(configFile); - config.init(configPath); - } -} - -/** - * Do all initialization stuff. - */ -static void initEngine(const Options &options) -{ - // Initialize SDL - logger->log("Initializing SDL..."); - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) - { - logger->error(strprintf("Could not initialize SDL: %s", - SDL_GetError())); - } - atexit(SDL_Quit); - - SDL_EnableUNICODE(1); - SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); - - SDL_WM_SetCaption(branding.getValue("appName", "Mana").c_str(), NULL); - - ResourceManager *resman = ResourceManager::getInstance(); - - if (!resman->setWriteDir(homeDir)) - { - logger->error(strprintf("%s couldn't be set as home directory! " - "Exiting.", homeDir.c_str())); - } - - // Add the user's homedir to PhysicsFS search path - resman->addToSearchPath(homeDir, false); - - // Add the main data directories to our PhysicsFS search path - if (!options.dataPath.empty()) - resman->addToSearchPath(options.dataPath, true); - resman->addToSearchPath("data", true); -#if defined __APPLE__ - CFBundleRef mainBundle = CFBundleGetMainBundle(); - CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); - char path[PATH_MAX]; - if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, - PATH_MAX)) - { - fprintf(stderr, "Can't find Resources directory\n"); - } - CFRelease(resourcesURL); - strncat(path, "/data", PATH_MAX - 1); - resman->addToSearchPath(path, true); -#else - resman->addToSearchPath(PKG_DATADIR "data", true); -#endif - -#ifdef WIN32 - static SDL_SysWMinfo pInfo; - SDL_GetWMInfo(&pInfo); - HICON icon = LoadIcon(GetModuleHandle(NULL), "A"); - if (icon) - { - SetClassLong(pInfo.window, GCL_HICON, (LONG) icon); - } -#else - icon = IMG_Load(resman->getPath(branding.getValue("appIcon", "data/icons/mana.png")).c_str()); - if (icon) - { - SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); - SDL_WM_SetIcon(icon, NULL); - } -#endif - -#ifdef USE_OPENGL - bool useOpenGL = !options.noOpenGL && (config.getValue("opengl", 0) == 1); - - // Setup image loading for the right image format - Image::setLoadAsOpenGL(useOpenGL); - - // Create the graphics context - graphics = useOpenGL ? new OpenGLGraphics : new Graphics; -#else - // Create the graphics context - graphics = new Graphics; -#endif - - const int width = (int) config.getValue("screenwidth", defaultScreenWidth); - const int height = (int) config.getValue("screenheight", defaultScreenHeight); - const int bpp = 0; - const bool fullscreen = ((int) config.getValue("screen", 0) == 1); - const bool hwaccel = ((int) config.getValue("hwaccel", 0) == 1); - - // Try to set the desired video mode - if (!graphics->setVideoMode(width, height, bpp, fullscreen, hwaccel)) - { - logger->error(strprintf("Couldn't set %dx%dx%d video mode: %s", - width, height, bpp, SDL_GetError())); - } - - // Initialize for drawing - graphics->_beginDraw(); - - // Initialize the item shortcuts. - itemShortcut = new ItemShortcut; - - // Initialize the emote shortcuts. - emoteShortcut = new EmoteShortcut; - - gui = new Gui(graphics); - - // Initialize sound engine - try - { - if (config.getValue("sound", 0) == 1) - sound.init(); - - sound.setSfxVolume((int) config.getValue("sfxVolume", - defaultSfxVolume)); - sound.setMusicVolume((int) config.getValue("musicVolume", - defaultMusicVolume)); - } - catch (const char *err) - { - state = STATE_ERROR; - errorMessage = err; - logger->log("Warning: %s", err); - } - - // Initialize keyboard - keyboard.init(); - - // Initialise player relations - player_relations.init(); -} - -/** Clear the engine */ -static void exitEngine() -{ - // Before config.write() since it writes the shortcuts to the config - delete itemShortcut; - delete emoteShortcut; - - config.write(); - - delete gui; - delete graphics; - - // Shutdown libxml - xmlCleanupParser(); - - // Shutdown sound - sound.close(); - - // Unload XML databases - ColorDB::unload(); - EmoteDB::unload(); - ItemDB::unload(); - MonsterDB::unload(); - NPCDB::unload(); - StatusEffect::unload(); - - ResourceManager::deleteInstance(); - - SDL_FreeSurface(icon); -} static void printHelp() { @@ -544,7 +63,7 @@ static void printVersion() std::cout << strprintf("Mana client %s", FULL_VERSION) << std::endl; } -static void parseOptions(int argc, char *argv[], Options &options) +static void parseOptions(int argc, char *argv[], Client::Options &options) { const char *optstring = "hvud:U:P:Dc:s:p:C:H:S:Oi:"; @@ -626,58 +145,9 @@ static void parseOptions(int argc, char *argv[], Options &options) } } -class AccountListener : public gcn::ActionListener -{ -public: - void action(const gcn::ActionEvent &) - { - state = STATE_CHAR_SELECT; - } -} accountListener; - -class LoginListener : public gcn::ActionListener -{ -public: - void action(const gcn::ActionEvent &) - { - state = STATE_LOGIN; - } -} loginListener; - -} // namespace - -void ErrorListener::action(const gcn::ActionEvent &) -{ - state = STATE_CHOOSE_SERVER; -} - -const std::string &getHomeDirectory() -{ - return homeDir; -} - -// TODO Find some nice place for these functions -static void accountLogin(LoginData *loginData) -{ - logger->log("Username is %s", loginData->username.c_str()); - - // Send login infos - if (loginData->registerLogin) - Net::getLoginHandler()->registerAccount(loginData); - else - Net::getLoginHandler()->loginAccount(loginData); - - // Clear the password, avoids auto login when returning to login - loginData->password = ""; - - // TODO This is not the best place to save the config, but at least better - // than the login gui window - if (loginData->remember) - config.setValue("username", loginData->username); - config.setValue("remember", loginData->remember); -} - +#ifdef WIN32 extern "C" char const *_nl_locale_name_default(void); +#endif static void initInternationalization() { @@ -711,16 +181,18 @@ static void initXML() xmlSetGenericErrorFunc(NULL, xmlNullLogger); } -/** Main */ + int main(int argc, char *argv[]) { #if defined(DEBUG) && defined(WIN32) - // load mingw crash handler. Won't fail if dll is not present. + // Load mingw crash handler. Won't fail if dll is not present. LoadLibrary("exchndl.dll"); #endif + // Parse command line options - Options options; + Client::Options options; parseOptions(argc, argv, options); + if (options.printHelp) { printHelp(); @@ -735,557 +207,15 @@ int main(int argc, char *argv[]) initInternationalization(); // Initialize PhysicsFS - PHYSFS_init(argv[0]); - - initXML(); - - // Load branding information - branding.init("data/branding.xml"); - - initHomeDir(options); - - setScreenshotDir(options.screenshotDir); - - // Configure logger - logger = new Logger; - logger->setLogFile(homeDir + std::string("/mana.log")); - - // Log the mana version - logger->log("Mana %s", FULL_VERSION); - - initConfiguration(options); - logger->setLogToStandardOut(config.getValue("logToStandardOut", 0)); - - initEngine(options); - - // Needs to be created in main, as the updater uses it - guiPalette = new Palette; - - Window *currentDialog = 0; - QuitDialog *quitDialog = 0; - setupWindow = new Setup; - - gcn::Container *top = static_cast<gcn::Container*>(gui->getTop()); - Desktop *desktop = 0; - Button *setupButton = 0; - - sound.playMusic(branding.getValue("loginMusic", "Magick - Real.ogg")); - - // Initialize default server - ServerInfo currentServer; - currentServer.hostname = options.serverName; - currentServer.port = options.serverPort; - loginData.username = options.username; - loginData.password = options.password; - loginData.remember = config.getValue("remember", 0); - loginData.registerLogin = false; - - if (currentServer.hostname.empty()) - { - currentServer.hostname = branding.getValue("defaultServer", - "server.themanaworld.org").c_str(); - } - if (options.serverPort == 0) - { - currentServer.port = (short) branding.getValue("defaultPort", - DEFAULT_PORT); - } - if (loginData.username.empty() && loginData.remember) - loginData.username = config.getValue("username", ""); - - if (state != STATE_ERROR) - state = STATE_CHOOSE_SERVER; - State oldstate = STATE_START; // We start with a status change - - SDL_Event event; - - while (state != STATE_EXIT) - { - bool handledEvents = false; - - // Handle SDL events - while (SDL_PollEvent(&event)) - { - handledEvents = true; - - switch (event.type) - { - case SDL_QUIT: - state = STATE_EXIT; - break; - - case SDL_KEYDOWN: - break; - } - - guiInput->pushInput(event); - } - - if (Net::getGeneralHandler()) - Net::getGeneralHandler()->flushNetwork(); - - gui->logic(); - gui->draw(); - graphics->updateScreen(); - - // TODO: Add connect timeouts - if (state == STATE_CONNECT_GAME && - Net::getGameHandler()->isConnected()) - { - Net::getLoginHandler()->disconnect(); - } - else if (state == STATE_CONNECT_SERVER && oldstate == STATE_CHOOSE_SERVER) - { - Net::connectToServer(currentServer); - } - else if (state == STATE_CONNECT_SERVER && - oldstate != STATE_CHOOSE_SERVER && - Net::getLoginHandler()->isConnected()) - { - state = STATE_LOGIN; - } - else if (state == STATE_WORLD_SELECT && oldstate == STATE_UPDATE) - { - if (Net::getLoginHandler()->getWorlds().size() < 2) - { - state = STATE_LOGIN; - } - } - else if (oldstate == STATE_START || oldstate == STATE_GAME) - { - desktop = new Desktop; - top->add(desktop); - setupButton = new Button(_("Setup"), "Setup", &listener); - setupButton->setPosition(top->getWidth() - setupButton->getWidth() - - 3, 3); - top->add(setupButton); - - int screenWidth = (int) config.getValue("screenwidth", - defaultScreenWidth); - int screenHeight = (int) config.getValue("screenheight", - defaultScreenHeight); - - desktop->setSize(screenWidth, screenHeight); - } - - if (state == STATE_SWITCH_LOGIN && oldstate == STATE_GAME) - { - Net::getGameHandler()->disconnect(); - } - - if (state != oldstate) - { - oldstate = state; - - // Get rid of the dialog of the previous state - if (currentDialog) - { - delete currentDialog; - currentDialog = NULL; - } - // State has changed, while the quitDialog was active, it might - // not be correct anymore - if (quitDialog) - quitDialog->scheduleDelete(); - - switch (state) - { - case STATE_CHOOSE_SERVER: - logger->log("State: CHOOSE SERVER"); - - // Allow changing this using a server choice dialog - // We show the dialog box only if the command-line - // options weren't set. - if (options.serverName.empty() && options.serverPort == 0) - { - // Don't allow an alpha opacity - // lower than the default value - SkinLoader::instance()->setMinimumOpacity(0.8f); - - currentDialog = new ServerDialog(¤tServer, - homeDir); - } - else - { - state = STATE_CONNECT_SERVER; - - // Reset options so that cancelling or connect - // timeout will show the server dialog. - options.serverName.clear(); - options.serverPort = 0; - } - break; - - case STATE_CONNECT_SERVER: - logger->log("State: CONNECT SERVER"); - currentDialog = new ConnectionDialog( - _("Connecting to server"), STATE_SWITCH_SERVER); - break; - - case STATE_LOGIN: - logger->log("State: LOGIN"); - // Don't allow an alpha opacity - // lower than the default value - SkinLoader::instance()->setMinimumOpacity(0.8f); - - if (options.username.empty() - || options.password.empty()) - { - currentDialog = new LoginDialog(&loginData); - } - else - { - state = STATE_LOGIN_ATTEMPT; - // Clear the password so that when login fails, the - // dialog will show up next time. - options.password.clear(); - } - break; - - case STATE_LOGIN_ATTEMPT: - logger->log("State: LOGIN ATTEMPT"); - accountLogin(&loginData); - currentDialog = new ConnectionDialog( - _("Logging in"), STATE_SWITCH_SERVER); - break; - - case STATE_WORLD_SELECT: - logger->log("State: WORLD SELECT"); - { - Worlds worlds = Net::getLoginHandler()->getWorlds(); - - if (worlds.size() == 0) - { - // Trust that the netcode knows what it's doing - state = STATE_UPDATE; - } - else if (worlds.size() == 1) - { - Net::getLoginHandler()->chooseServer(0); - state = STATE_UPDATE; - } - else - { - currentDialog = new WorldSelectDialog(worlds); - if (options.chooseDefault) - { - ((WorldSelectDialog*) currentDialog)->action( - gcn::ActionEvent(NULL, "ok")); - } - } - } - break; - - case STATE_WORLD_SELECT_ATTEMPT: - logger->log("State: WORLD SELECT ATTEMPT"); - currentDialog = new ConnectionDialog( - _("Entering game world"), STATE_WORLD_SELECT); - break; - - case STATE_UPDATE: - - // Determine which source to use for the update host - if (!options.updateHost.empty()) - updateHost = options.updateHost; - else - updateHost = loginData.updateHost; - setUpdatesDir(); - - if (options.skipUpdate) - { - state = STATE_LOAD_DATA; - } - else - { - logger->log("State: UPDATE"); - currentDialog = new UpdaterWindow(updateHost, - homeDir + "/" + updatesDir,options.dataPath.empty()); - } - break; - - case STATE_LOAD_DATA: - logger->log("State: LOAD DATA"); - - // If another data path has been set, - // we don't load any other files... - if (options.dataPath.empty()) - { - // Add customdata directory - ResourceManager::getInstance()->searchAndAddArchives( - "customdata/", - "zip", - false); - } - - // Load XML databases - ColorDB::load(); - ItemDB::load(); - Being::load(); // Hairstyles - MonsterDB::load(); - NPCDB::load(); - EmoteDB::load(); - StatusEffect::load(); - Units::loadUnits(); - - desktop->reloadWallpaper(); - - state = STATE_GET_CHARACTERS; - break; - - case STATE_GET_CHARACTERS: - logger->log("State: GET CHARACTERS"); - Net::getCharHandler()->requestCharacters(); - currentDialog = new ConnectionDialog( - _("Requesting characters"), - STATE_SWITCH_SERVER); - break; - - case STATE_CHAR_SELECT: - logger->log("State: CHAR SELECT"); - // Don't allow an alpha opacity - // lower than the default value - SkinLoader::instance()->setMinimumOpacity(0.8f); - - currentDialog = new CharSelectDialog(&loginData); - - if (!((CharSelectDialog*) currentDialog)->selectByName( - options.character, CharSelectDialog::Choose)) - { - ((CharSelectDialog*) currentDialog)->selectByName( - config.getValue("lastCharacter", ""), - options.chooseDefault ? - CharSelectDialog::Choose : - CharSelectDialog::Focus); - } - - break; - - case STATE_CONNECT_GAME: - logger->log("State: CONNECT GAME"); - - Net::getGameHandler()->connect(); - currentDialog = new ConnectionDialog( - _("Connecting to the game server"), - STATE_SWITCH_CHARACTER); - break; - - case STATE_GAME: - logger->log("Memorizing selected character %s", - player_node->getName().c_str()); - config.setValue("lastCharacter", player_node->getName()); - - Net::getGameHandler()->inGame(); - - // Fade out logon-music here too to give the desired effect - // of "flowing" into the game. - sound.fadeOutMusic(1000); - - // Allow any alpha opacity - SkinLoader::instance()->setMinimumOpacity(-1.0f); - - delete setupButton; - delete desktop; - setupButton = NULL; - desktop = NULL; - - currentDialog = NULL; - - logger->log("State: GAME"); - game = new Game; - game->exec(); - delete game; - game = 0; - - if (state == STATE_GAME) - state = STATE_EXIT; - - break; - - case STATE_LOGIN_ERROR: - logger->log("State: LOGIN ERROR"); - currentDialog = new OkDialog(_("Error"), errorMessage); - currentDialog->addActionListener(&loginListener); - currentDialog = NULL; // OkDialog deletes itself - break; - - case STATE_ACCOUNTCHANGE_ERROR: - logger->log("State: ACCOUNT CHANGE ERROR"); - currentDialog = new OkDialog(_("Error"), errorMessage); - currentDialog->addActionListener(&accountListener); - currentDialog = NULL; // OkDialog deletes itself - break; - - case STATE_REGISTER_PREP: - logger->log("State: REGISTER_PREP"); - Net::getLoginHandler()->getRegistrationDetails(); - currentDialog = new ConnectionDialog( - _("Requesting registration details"), STATE_LOGIN); - break; - - case STATE_REGISTER: - logger->log("State: REGISTER"); - currentDialog = new RegisterDialog(&loginData); - break; - - case STATE_REGISTER_ATTEMPT: - logger->log("Username is %s", loginData.username.c_str()); - Net::getLoginHandler()->registerAccount(&loginData); - break; - - case STATE_CHANGEPASSWORD: - logger->log("State: CHANGE PASSWORD"); - currentDialog = new ChangePasswordDialog(&loginData); - break; - - case STATE_CHANGEPASSWORD_ATTEMPT: - logger->log("State: CHANGE PASSWORD ATTEMPT"); - Net::getLoginHandler()->changePassword(loginData.username, - loginData.password, - loginData.newPassword); - break; - - case STATE_CHANGEPASSWORD_SUCCESS: - logger->log("State: CHANGE PASSWORD SUCCESS"); - currentDialog = new OkDialog(_("Password Change"), - _("Password changed successfully!")); - currentDialog->addActionListener(&accountListener); - currentDialog = NULL; // OkDialog deletes itself - loginData.password = loginData.newPassword; - loginData.newPassword = ""; - break; - - case STATE_CHANGEEMAIL: - logger->log("State: CHANGE EMAIL"); - currentDialog = new ChangeEmailDialog(&loginData); - break; - - case STATE_CHANGEEMAIL_ATTEMPT: - logger->log("State: CHANGE EMAIL ATTEMPT"); - Net::getLoginHandler()->changeEmail(loginData.email); - break; - - case STATE_CHANGEEMAIL_SUCCESS: - logger->log("State: CHANGE EMAIL SUCCESS"); - currentDialog = new OkDialog(_("Email Change"), - _("Email changed successfully!")); - currentDialog->addActionListener(&accountListener); - currentDialog = NULL; // OkDialog deletes itself - break; - - case STATE_UNREGISTER: - logger->log("State: UNREGISTER"); - currentDialog = new UnRegisterDialog(&loginData); - break; - - case STATE_UNREGISTER_ATTEMPT: - logger->log("State: UNREGISTER ATTEMPT"); - Net::getLoginHandler()->unregisterAccount( - loginData.username, loginData.password); - break; - - case STATE_UNREGISTER_SUCCESS: - logger->log("State: UNREGISTER SUCCESS"); - Net::getLoginHandler()->disconnect(); - - currentDialog = new OkDialog(_("Unregister Successful"), - _("Farewell, come back any time...")); - loginData.clear(); - //The errorlistener sets the state to STATE_CHOOSE_SERVER - currentDialog->addActionListener(&errorListener); - currentDialog = NULL; // OkDialog deletes itself - break; - - case STATE_SWITCH_SERVER: - logger->log("State: SWITCH SERVER"); - - Net::getLoginHandler()->disconnect(); - Net::getGameHandler()->disconnect(); - - state = STATE_CHOOSE_SERVER; - break; - - case STATE_SWITCH_LOGIN: - logger->log("State: SWITCH LOGIN"); - - Net::getLoginHandler()->logout(); - - state = STATE_LOGIN; - break; - - case STATE_SWITCH_CHARACTER: - logger->log("State: SWITCH CHARACTER"); - - // Done with game - Net::getGameHandler()->disconnect(); - - Net::getCharHandler()->requestCharacters(); - break; - - case STATE_LOGOUT_ATTEMPT: - logger->log("State: LOGOUT ATTEMPT"); - // TODO - break; - - case STATE_WAIT: - logger->log("State: WAIT"); - break; - - case STATE_EXIT: - logger->log("State: EXIT"); - Net::unload(); - break; - - case STATE_FORCE_QUIT: - logger->log("State: FORCE QUIT"); - if (Net::getGeneralHandler()) - Net::getGeneralHandler()->unload(); - state = STATE_EXIT; - break; - - case STATE_ERROR: - logger->log("State: ERROR"); - currentDialog = new OkDialog(_("Error"), errorMessage); - currentDialog->addActionListener(&errorListener); - currentDialog = NULL; // OkDialog deletes itself - Net::getGameHandler()->disconnect(); - break; - - default: - state = STATE_FORCE_QUIT; - break; - } - } - - /* - * This loop can really stress the CPU, for no reason since it's - * just constantly redrawing the wallpaper. Added the following - * usleep to limit it to 40 FPS during the login sequence - */ - if (!handledEvents) - usleep(25000); + if (!PHYSFS_init(argv[0])) { + std::cout << "Error while initializing PhysFS: " + << PHYSFS_getLastError() << std::endl; + return 1; } + atexit((void(*)()) PHYSFS_deinit); - delete guiPalette; - - logger->log("Quitting"); - exitEngine(); - PHYSFS_deinit(); - delete logger; - - return 0; -} - -void SetupListener::action(const gcn::ActionEvent &event) -{ - Window *window = NULL; - - if (event.getId() == "Setup") - window = setupWindow; + initXML(); - if (window) - { - window->setVisible(!window->isVisible()); - if (window->isVisible()) - window->requestMoveToTop(); - } + Client client(options); + return client.exec(); } @@ -59,12 +59,6 @@ #define PACKAGE_VERSION "0.0.29.1" #endif -#include "net/logindata.h" - -#include <guichan/actionlistener.hpp> - -#include <string> - #ifdef PACKAGE_VERSION #define FULL_VERSION "v" PACKAGE_VERSION #else @@ -75,65 +69,4 @@ #define PKG_DATADIR "" #endif -#define MAX_CHARACTER_COUNT 3 - -//manaserv uses 9601 -//#define DEFAULT_PORT 9601 -#define DEFAULT_PORT 6901 - -const std::string &getHomeDirectory(); - -/* - * Client different States - */ -enum State { - STATE_ERROR = -1, - STATE_START = 0, - STATE_CHOOSE_SERVER, - STATE_CONNECT_SERVER, - STATE_LOGIN, - STATE_LOGIN_ATTEMPT, - STATE_WORLD_SELECT, // 5 - STATE_WORLD_SELECT_ATTEMPT, - STATE_UPDATE, - STATE_LOAD_DATA, - STATE_GET_CHARACTERS, - STATE_CHAR_SELECT, // 10 - STATE_CONNECT_GAME, - STATE_GAME, - STATE_CHANGE_MAP, // Switch map-server/gameserver - STATE_LOGIN_ERROR, - STATE_ACCOUNTCHANGE_ERROR, // 15 - STATE_REGISTER_PREP, - STATE_REGISTER, - STATE_REGISTER_ATTEMPT, - STATE_CHANGEPASSWORD, - STATE_CHANGEPASSWORD_ATTEMPT, // 20 - STATE_CHANGEPASSWORD_SUCCESS, - STATE_CHANGEEMAIL, - STATE_CHANGEEMAIL_ATTEMPT, - STATE_CHANGEEMAIL_SUCCESS, - STATE_UNREGISTER, // 25 - STATE_UNREGISTER_ATTEMPT, - STATE_UNREGISTER_SUCCESS, - STATE_SWITCH_SERVER, - STATE_SWITCH_LOGIN, - STATE_SWITCH_CHARACTER, // 30 - STATE_LOGOUT_ATTEMPT, - STATE_WAIT, - STATE_EXIT, - STATE_FORCE_QUIT -}; - -class ErrorListener : public gcn::ActionListener -{ - public: - void action(const gcn::ActionEvent &event); -}; - -extern State state; -extern std::string errorMessage; -extern ErrorListener errorListener; -extern LoginData loginData; - #endif diff --git a/src/map.cpp b/src/map.cpp index 7c45050b..6d332d48 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -19,11 +19,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "map.h" + #include "beingmanager.h" +#include "client.h" #include "configuration.h" -#include "game.h" #include "graphics.h" -#include "map.h" #include "particle.h" #include "simpleanimation.h" #include "sprite.h" @@ -38,8 +39,6 @@ #include <queue> -extern volatile int tick_time; - /** * A location on a tile map. Used for pathfinding, open list. */ @@ -291,14 +290,13 @@ bool spriteCompare(const Sprite *a, const Sprite *b) void Map::update(int ticks) { - //update animated tiles + // Update animated tiles for (std::map<int, TileAnimation*>::iterator iAni = mTileAnimations.begin(); iAni != mTileAnimations.end(); iAni++) { iAni->second->update(ticks); } - } void Map::draw(Graphics *graphics, int scrollX, int scrollY) diff --git a/src/monster.cpp b/src/monster.cpp index 044f80a2..dfe84e65 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -19,10 +19,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "monster.h" + #include "animatedsprite.h" -#include "game.h" +#include "client.h" #include "localplayer.h" -#include "monster.h" #include "particle.h" #include "sound.h" #include "text.h" @@ -38,7 +39,7 @@ Monster::Monster(int id, int job, Map *map): Being(id, job, map), mAttackType(1) { - const MonsterInfo& info = getInfo(); + const MonsterInfo &info = getInfo(); // Setup Monster sprites const std::list<std::string> &sprites = info.getSprites(); diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp index 14ed40bc..fd08ecaf 100644 --- a/src/net/ea/beinghandler.cpp +++ b/src/net/ea/beinghandler.cpp @@ -23,8 +23,8 @@ #include "being.h" #include "beingmanager.h" +#include "client.h" #include "effectmanager.h" -#include "game.h" #include "localplayer.h" #include "log.h" #include "npc.h" diff --git a/src/net/ea/charserverhandler.cpp b/src/net/ea/charserverhandler.cpp index 23d88356..f0a13f43 100644 --- a/src/net/ea/charserverhandler.cpp +++ b/src/net/ea/charserverhandler.cpp @@ -21,9 +21,9 @@ #include "net/ea/charserverhandler.h" +#include "client.h" #include "game.h" #include "log.h" -#include "main.h" #include "gui/charcreatedialog.h" #include "gui/okdialog.h" @@ -92,7 +92,7 @@ void CharServerHandler::handleMessage(Net::MessageIn &msg) character->dummy->getName().c_str(), slot); } - state = STATE_CHAR_SELECT; + Client::setState(STATE_CHAR_SELECT); } break; @@ -167,7 +167,7 @@ void CharServerHandler::handleMessage(Net::MessageIn &msg) updateCharSelectDialog(); mNetwork->disconnect(); - state = STATE_CONNECT_GAME; + Client::setState(STATE_CONNECT_GAME); break; } } diff --git a/src/net/ea/gamehandler.cpp b/src/net/ea/gamehandler.cpp index b7b8389a..3625b9ab 100644 --- a/src/net/ea/gamehandler.cpp +++ b/src/net/ea/gamehandler.cpp @@ -21,10 +21,9 @@ #include "net/ea/gamehandler.h" -#include "game.h" +#include "client.h" #include "localplayer.h" #include "log.h" -#include "main.h" #include "gui/widgets/chattab.h" @@ -74,7 +73,7 @@ void GameHandler::handleMessage(Net::MessageIn &msg) logger->log("Protocol: Player start position: (%d, %d), Direction: %d", x, y, direction); // Switch now or we'll have problems - state = STATE_GAME; + Client::setState(STATE_GAME); player_node->setTileCoords(x, y); } break; @@ -91,7 +90,7 @@ void GameHandler::handleMessage(Net::MessageIn &msg) case SMSG_CHAR_SWITCH_RESPONSE: if (msg.readInt8()) { - state = STATE_SWITCH_CHARACTER; + Client::setState(STATE_SWITCH_CHARACTER); } break; diff --git a/src/net/ea/generalhandler.cpp b/src/net/ea/generalhandler.cpp index 7fc5886a..f7d495a6 100644 --- a/src/net/ea/generalhandler.cpp +++ b/src/net/ea/generalhandler.cpp @@ -21,9 +21,9 @@ #include "net/ea/generalhandler.h" +#include "client.h" #include "configuration.h" #include "log.h" -#include "main.h" #include "gui/charselectdialog.h" #include "gui/inventorywindow.h" @@ -129,7 +129,7 @@ void GeneralHandler::handleMessage(Net::MessageIn &msg) errorMessage = _("No servers available."); break; case 2: - if (state == STATE_GAME) + if (Client::getState() == STATE_GAME) errorMessage = _("Someone else is trying to use this " "account."); else @@ -145,7 +145,7 @@ void GeneralHandler::handleMessage(Net::MessageIn &msg) errorMessage = _("Unknown connection error."); break; } - state = STATE_ERROR; + Client::setState(STATE_ERROR); break; } } @@ -199,7 +199,7 @@ void GeneralHandler::flushNetwork() else errorMessage = _("Got disconnected from server!"); - state = STATE_ERROR; + Client::setState(STATE_ERROR); } } diff --git a/src/net/ea/loginhandler.cpp b/src/net/ea/loginhandler.cpp index 3f0fe0d0..bec97ed7 100644 --- a/src/net/ea/loginhandler.cpp +++ b/src/net/ea/loginhandler.cpp @@ -21,8 +21,8 @@ #include "net/ea/loginhandler.h" +#include "client.h" #include "log.h" -#include "main.h" #include "net/logindata.h" #include "net/messagein.h" @@ -68,7 +68,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) // Successful pass change if (errMsg == 1) { - state = STATE_CHANGEPASSWORD_SUCCESS; + Client::setState(STATE_CHANGEPASSWORD_SUCCESS); } // pass change failed else @@ -88,7 +88,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) errorMessage = _("Unknown error."); break; } - state = STATE_ACCOUNTCHANGE_ERROR; + Client::setState(STATE_ACCOUNTCHANGE_ERROR); } } break; @@ -136,7 +136,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) mWorlds.push_back(world); } - state = STATE_WORLD_SELECT; + Client::setState(STATE_WORLD_SELECT); break; case SMSG_LOGIN_ERROR: @@ -175,8 +175,9 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) errorMessage = _("Unknown error."); break; } - state = STATE_ERROR; + Client::setState(STATE_ERROR); break; + case SMSG_SERVER_VERSION_RESPONSE: { // TODO: verify these! @@ -214,7 +215,7 @@ void LoginHandler::disconnect() void LoginHandler::getRegistrationDetails() { // Not supported, so move on - state = STATE_REGISTER; + Client::setState(STATE_REGISTER); } void LoginHandler::loginAccount(LoginData *loginData) @@ -250,7 +251,7 @@ void LoginHandler::chooseServer(unsigned int server) charServer.hostname = ipToString(mWorlds[server]->address); charServer.port = mWorlds[server]->port; - state = STATE_UPDATE; + Client::setState(STATE_UPDATE); } void LoginHandler::registerAccount(LoginData *loginData) diff --git a/src/net/logindata.h b/src/net/logindata.h index 5a96db20..9bbeed4f 100644 --- a/src/net/logindata.h +++ b/src/net/logindata.h @@ -28,8 +28,9 @@ #include <string> -struct LoginData +class LoginData { +public: std::string username; std::string password; std::string newPassword; diff --git a/src/net/manaserv/beinghandler.cpp b/src/net/manaserv/beinghandler.cpp index 86f86d3f..bab5471b 100644 --- a/src/net/manaserv/beinghandler.cpp +++ b/src/net/manaserv/beinghandler.cpp @@ -23,13 +23,12 @@ #include "being.h" #include "beingmanager.h" +#include "client.h" #include "game.h" #include "localplayer.h" #include "log.h" -#include "main.h" #include "npc.h" #include "particle.h" -#include "sound.h" #include "gui/okdialog.h" @@ -103,10 +102,10 @@ Vector BeingHandler::giveSpeedInPixelsPerTicks(float speedInTilesPerSeconds) { speedInTicks.x = speedInTilesPerSeconds * (float)map->getTileWidth() - / 1000 * (float)MILLISECONDS_IN_A_TICK; + / 1000 * (float) MILLISECONDS_IN_A_TICK; speedInTicks.y = speedInTilesPerSeconds * (float)map->getTileHeight() - / 1000 * (float)MILLISECONDS_IN_A_TICK; + / 1000 * (float) MILLISECONDS_IN_A_TICK; } } diff --git a/src/net/manaserv/charhandler.cpp b/src/net/manaserv/charhandler.cpp index 14d2b11f..4b574821 100644 --- a/src/net/manaserv/charhandler.cpp +++ b/src/net/manaserv/charhandler.cpp @@ -21,10 +21,9 @@ #include "net/manaserv/charhandler.h" -#include "game.h" +#include "client.h" #include "localplayer.h" #include "log.h" -#include "main.h" #include "gui/charcreatedialog.h" #include "gui/okdialog.h" @@ -236,14 +235,14 @@ void CharHandler::handleCharacterSelectResponse(Net::MessageIn &msg) mCachedCharacterInfos.clear(); updateCharacters(); - state = STATE_CONNECT_GAME; + Client::setState(STATE_CONNECT_GAME); } else if (errMsg == ERRMSG_FAILURE) { errorMessage = _("No gameservers are available."); delete_all(mCharacters); mCharacters.clear(); - state = STATE_ERROR; + Client::setState(STATE_ERROR); } } @@ -280,7 +279,7 @@ void CharHandler::requestCharacters() else { // The characters are already there, continue to character selection - state = STATE_CHAR_SELECT; + Client::setState(STATE_CHAR_SELECT); } } diff --git a/src/net/manaserv/chathandler.cpp b/src/net/manaserv/chathandler.cpp index d6da557d..a452281f 100644 --- a/src/net/manaserv/chathandler.cpp +++ b/src/net/manaserv/chathandler.cpp @@ -23,10 +23,9 @@ #include "being.h" #include "beingmanager.h" +#include "client.h" #include "channel.h" #include "channelmanager.h" -#include "game.h" -#include "main.h" #include "gui/chat.h" @@ -136,7 +135,7 @@ void ChatHandler::handleMessage(Net::MessageIn &msg) errorMessage = "Chatserver: Unknown error"; break; } - state = STATE_ERROR; + Client::setState(STATE_ERROR); } } break; diff --git a/src/net/manaserv/gamehandler.cpp b/src/net/manaserv/gamehandler.cpp index a7d979a1..dd6120ae 100644 --- a/src/net/manaserv/gamehandler.cpp +++ b/src/net/manaserv/gamehandler.cpp @@ -21,8 +21,8 @@ #include "net/manaserv/gamehandler.h" +#include "client.h" #include "localplayer.h" -#include "main.h" #include "net/manaserv/chathandler.h" #include "net/manaserv/connection.h" @@ -63,7 +63,7 @@ void GameHandler::handleMessage(Net::MessageIn &msg) \ if (!netToken.empty()) { - state = STATE_SWITCH_CHARACTER; + Client::setState(STATE_SWITCH_CHARACTER); } else { @@ -82,7 +82,7 @@ void GameHandler::handleMessage(Net::MessageIn &msg) errorMessage = "Gameserver: Unknown error"; break; } - state = STATE_ERROR; + Client::setState(STATE_ERROR); } } break; diff --git a/src/net/manaserv/generalhandler.cpp b/src/net/manaserv/generalhandler.cpp index 9f14fd38..171ca214 100644 --- a/src/net/manaserv/generalhandler.cpp +++ b/src/net/manaserv/generalhandler.cpp @@ -21,7 +21,7 @@ #include "net/manaserv/generalhandler.h" -#include "main.h" +#include "client.h" #include "gui/changeemaildialog.h" #include "gui/charselectdialog.h" @@ -147,11 +147,11 @@ void GeneralHandler::flushNetwork() { flush(); - if (state == STATE_SWITCH_CHARACTER && + if (Client::getState() == STATE_SWITCH_CHARACTER && Net::getLoginHandler()->isConnected()) { loginHandler->reconnect(); - state = STATE_GET_CHARACTERS; + Client::setState(STATE_GET_CHARACTERS); } } diff --git a/src/net/manaserv/loginhandler.cpp b/src/net/manaserv/loginhandler.cpp index a8395204..cbc8e510 100644 --- a/src/net/manaserv/loginhandler.cpp +++ b/src/net/manaserv/loginhandler.cpp @@ -21,7 +21,7 @@ #include "net/manaserv/loginhandler.h" -#include "main.h" +#include "client.h" #include "net/logindata.h" @@ -75,7 +75,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) // Successful login if (errMsg == ERRMSG_OK) { - state = STATE_CHAR_SELECT; + Client::setState(STATE_CHAR_SELECT); } // Login failed else @@ -95,7 +95,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) errorMessage = _("Unknown error."); break; } - state = STATE_ERROR; + Client::setState(STATE_ERROR); } } break; @@ -106,7 +106,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) // Successful pass change if (errMsg == ERRMSG_OK) { - state = STATE_CHANGEPASSWORD_SUCCESS; + Client::setState(STATE_CHANGEPASSWORD_SUCCESS); } // pass change failed else @@ -126,7 +126,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) errorMessage = _("Unknown error."); break; } - state = STATE_ACCOUNTCHANGE_ERROR; + Client::setState(STATE_ACCOUNTCHANGE_ERROR); } } break; @@ -137,7 +137,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) // Successful pass change if (errMsg == ERRMSG_OK) { - state = STATE_CHANGEEMAIL_SUCCESS; + Client::setState(STATE_CHANGEEMAIL_SUCCESS); } // pass change failed else @@ -160,7 +160,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) errorMessage = _("Unknown error."); break; } - state = STATE_ACCOUNTCHANGE_ERROR; + Client::setState(STATE_ACCOUNTCHANGE_ERROR); } } break; @@ -185,7 +185,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) errorMessage = "Accountserver: Unknown error"; break; } - state = STATE_ERROR; + Client::setState(STATE_ERROR); } } break; @@ -195,7 +195,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) // Successful unregistration if (errMsg == ERRMSG_OK) { - state = STATE_UNREGISTER; + Client::setState(STATE_UNREGISTER); } // Unregistration failed else @@ -210,7 +210,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) errorMessage = "Accountserver: Unknown error"; break; } - state = STATE_ACCOUNTCHANGE_ERROR; + Client::setState(STATE_ACCOUNTCHANGE_ERROR); } } break; @@ -228,7 +228,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) printf("%s: %s\n", captchaURL.c_str(), captchaInstructions.c_str()); - state = STATE_REGISTER; + Client::setState(STATE_REGISTER); } else { @@ -237,7 +237,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) if (errorMessage.empty()) errorMessage = _("Client registration is not allowed. " "Please contact server administration."); - state = STATE_LOGIN_ERROR; + Client::setState(STATE_LOGIN_ERROR); } } break; @@ -252,7 +252,7 @@ void LoginHandler::handleLoginResponse(Net::MessageIn &msg) { readUpdateHost(msg); // No worlds atm, but future use :-D - state = STATE_WORLD_SELECT; + Client::setState(STATE_WORLD_SELECT); } else { @@ -278,7 +278,7 @@ void LoginHandler::handleLoginResponse(Net::MessageIn &msg) errorMessage = _("Unknown error."); break; } - state = STATE_LOGIN_ERROR; + Client::setState(STATE_LOGIN_ERROR); } } @@ -289,7 +289,7 @@ void LoginHandler::handleRegisterResponse(Net::MessageIn &msg) if (errMsg == ERRMSG_OK) { readUpdateHost(msg); - state = STATE_WORLD_SELECT; + Client::setState(STATE_WORLD_SELECT); } else { @@ -315,7 +315,7 @@ void LoginHandler::handleRegisterResponse(Net::MessageIn &msg) errorMessage = _("Unknown error."); break; } - state = STATE_LOGIN_ERROR; + Client::setState(STATE_LOGIN_ERROR); } } @@ -342,9 +342,9 @@ void LoginHandler::disconnect() { accountServerConnection->disconnect(); - if (state == STATE_CONNECT_GAME) + if (Client::getState() == STATE_CONNECT_GAME) { - state = STATE_GAME; + Client::setState(STATE_GAME); } } diff --git a/src/net/manaserv/playerhandler.cpp b/src/net/manaserv/playerhandler.cpp index a78afa8a..3a5d68a2 100644 --- a/src/net/manaserv/playerhandler.cpp +++ b/src/net/manaserv/playerhandler.cpp @@ -22,6 +22,7 @@ #include "net/manaserv/playerhandler.h" #include "net/manaserv/beinghandler.h" +#include "client.h" #include "effectmanager.h" #include "game.h" #include "localplayer.h" @@ -41,9 +42,6 @@ #include "net/manaserv/messageout.h" #include "net/manaserv/protocol.h" -/** @see in game.cpp */ -extern const int MILLISECONDS_IN_A_TICK; - /** * Max. distance we are willing to scroll after a teleport; * everything beyond will reset the port hard. diff --git a/src/player.cpp b/src/player.cpp index 700301d5..18dc0818 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -19,14 +19,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "player.h" + #include "animatedsprite.h" +#include "client.h" #include "configuration.h" -#include "game.h" #include "guild.h" #include "localplayer.h" #include "particle.h" #include "party.h" -#include "player.h" #include "text.h" #include "gui/palette.h" diff --git a/src/statuseffect.cpp b/src/statuseffect.cpp index 1eed3e1c..66e8010d 100644 --- a/src/statuseffect.cpp +++ b/src/statuseffect.cpp @@ -22,6 +22,7 @@ #include "statuseffect.h" #include "log.h" +#include "sound.h" #include "gui/widgets/chattab.h" diff --git a/src/statuseffect.h b/src/statuseffect.h index 320f4004..0fed5748 100644 --- a/src/statuseffect.h +++ b/src/statuseffect.h @@ -24,7 +24,6 @@ #include "particle.h" #include "animatedsprite.h" -#include "sound.h" #include "resources/animation.h" |