diff options
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" |