diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 1041 |
1 files changed, 859 insertions, 182 deletions
diff --git a/src/main.cpp b/src/main.cpp index 014154e2..2ce47ac6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,8 @@ /* - * Aethyra + * The Mana World * Copyright (C) 2004 The Mana World Development Team * - * This file is part of Aethyra based on original code - * from The Mana World. + * This file is part of The Mana World. * * 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 @@ -20,18 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <getopt.h> -#include <iostream> -#include <physfs.h> -#include <SDL_image.h> -#include <unistd.h> -#include <vector> - -#include <guichan/actionlistener.hpp> - -#include <libxml/parser.h> - -#include <SDL/SDL_ttf.h> +#include "main.h" #include "configuration.h" #include "emoteshortcut.h" @@ -43,16 +31,19 @@ #include "lockedarray.h" #include "log.h" #include "logindata.h" -#include "main.h" #ifdef USE_OPENGL #include "openglgraphics.h" #endif #include "player_relations.h" #include "serverinfo.h" #include "sound.h" +#include "statuseffect.h" +#include "units.h" #include "gui/button.h" +#ifdef EATHENA_SUPPORT #include "gui/char_server.h" +#endif #include "gui/char_select.h" #include "gui/gui.h" #include "gui/label.h" @@ -63,13 +54,35 @@ #include "gui/register.h" #include "gui/sdlinput.h" #include "gui/setup.h" +#ifdef TMWSERV_SUPPORT +#include "gui/connection.h" +#include "gui/quitdialog.h" +#include "gui/serverdialog.h" +#endif #include "gui/updatewindow.h" -#include "net/charserverhandler.h" -#include "net/loginhandler.h" -#include "net/maploginhandler.h" +#ifdef TMWSERV_SUPPORT +#include "net/tmwserv/charserverhandler.h" +#include "net/tmwserv/connection.h" +#include "net/tmwserv/loginhandler.h" +#include "net/tmwserv/logouthandler.h" +#include "net/tmwserv/network.h" +#else +#include "net/ea/charserverhandler.h" +#include "net/ea/loginhandler.h" +#include "net/ea/network.h" +#include "net/ea/maploginhandler.h" #include "net/messageout.h" -#include "net/network.h" +#endif + +#ifdef TMWSERV_SUPPORT +#include "net/tmwserv/accountserver/accountserver.h" +#include "net/tmwserv/accountserver/account.h" + +#include "net/tmwserv/chatserver/chatserver.h" + +#include "net/tmwserv/gameserver/gameserver.h" +#endif #include "resources/colordb.h" #include "resources/emotedb.h" @@ -79,8 +92,24 @@ #include "resources/npcdb.h" #include "resources/resourcemanager.h" +#ifdef TMWSERV_SUPPORT +#include "utils/dtor.h" +#endif #include "utils/gettext.h" #include "utils/stringutils.h" +#include "utils/strprintf.h" + +#include <SDL_image.h> + +#include <guichan/actionlistener.hpp> + +#include <libxml/parser.h> + +#include <getopt.h> +#include <iostream> +#include <physfs.h> +#include <unistd.h> +#include <vector> #ifdef __APPLE__ #include <CoreFoundation/CFBundle.h> @@ -109,29 +138,43 @@ namespace } listener; } +#ifdef TMWSERV_SUPPORT +std::string token; //used to store magic_token +#else // Account infos char n_server, n_character; -Graphics *graphics; - // TODO Anyone knows a good location for this? Or a way to make it non-global? class SERVER_INFO; SERVER_INFO **server_info; +#endif + +Graphics *graphics; unsigned char state; std::string errorMessage; -unsigned char screen_mode; Sound sound; Music *bgm; Configuration config; /**< XML file configuration reader */ +Configuration branding; /**< XML branding information reader */ Logger *logger; /**< Log object */ KeyboardConfig keyboard; +#ifdef TMWSERV_SUPPORT +Net::Connection *gameServerConnection = 0; +Net::Connection *chatServerConnection = 0; +Net::Connection *accountServerConnection = 0; +#endif + CharServerHandler charServerHandler; LoginData loginData; -LockedArray<LocalPlayer*> charInfo(MAX_SLOT + 1); +#ifdef TMWSERV_SUPPORT +LoginHandler loginHandler; +LogoutHandler logoutHandler; +#endif +LockedArray<LocalPlayer*> charInfo(maxSlot + 1); Palette *guiPalette; @@ -142,8 +185,10 @@ std::string homeDir; std::string updateHost; std::string updatesDir; +#ifdef EATHENA_SUPPORT LoginHandler loginHandler; MapLoginHandler mapLoginHandler; +#endif SDL_Surface *icon; @@ -160,8 +205,9 @@ struct Options printHelp(false), printVersion(false), skipUpdate(false), - chooseDefault(false) - {}; + chooseDefault(false), + serverPort(0) + {} bool printHelp; bool printVersion; @@ -169,10 +215,13 @@ struct Options bool chooseDefault; std::string username; std::string password; - std::string playername; + std::string character; std::string configPath; std::string updateHost; std::string dataPath; + + std::string serverName; + short serverPort; }; /** @@ -187,7 +236,7 @@ static void setUpdatesDir() if (updateHost.empty()) { updateHost = - config.getValue("updatehost", "http://www.aethyra.org/updates"); + config.getValue("updatehost", "http://updates.themanaworld.org/"); } // Remove any trailing slash at the end of the update host @@ -209,7 +258,7 @@ static void setUpdatesDir() { logger->log("Error: Invalid update host: %s", updateHost.c_str()); errorMessage = _("Invalid update host: ") + updateHost; - state = ERROR_STATE; + state = STATE_ERROR; } } else @@ -242,35 +291,39 @@ static void setUpdatesDir() logger->log("Error: %s can't be made, but doesn't exist!", newDir.c_str()); errorMessage = _("Error creating updates directory!"); - state = ERROR_STATE; + 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 = ERROR_STATE; + state = STATE_ERROR; #endif } } } /** - * Do all initialization stuff + * Initializes the home directory. On UNIX and FreeBSD, ~/.tmw is used. On + * Windows and other systems we use the current working directory. */ -static void init_engine(const Options &options) +static void initHomeDir() { - homeDir = std::string(PHYSFS_getUserDir()) + "/.aethyra"; + homeDir = std::string(PHYSFS_getUserDir()) + + "/." + + branding.getValue("appShort", "tmw"); #if defined WIN32 if (!CreateDirectory(homeDir.c_str(), 0) && GetLastError() != ERROR_ALREADY_EXISTS) #elif defined __APPLE__ - // Use Application Directory instead of .aethyra + // Use Application Directory instead of .tmw homeDir = std::string(PHYSFS_getUserDir()) + - "/Library/Application Support/Aethyra"; + "/Library/Application Support/" + + branding.getValue("appName", "The Mana World"); if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST)) #else - // Checking if /home/user/.Aethyra folder exists. + // Checking if /home/user/.tmw folder exists. if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST)) #endif @@ -280,20 +333,76 @@ static void init_engine(const Options &options) << std::endl; exit(1); } +} + +/** + * 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"); + config.setValue("host", defaultHost); +#ifdef TWMSERV_SUPPORT + int defaultPort = (int)branding.getValue("defaultPort", 9601); +#else + int defaultPort = (int)branding.getValue("defaultPort", 6901); +#endif + config.setValue("port", defaultPort); + config.setValue("hwaccel", 0); +#if (defined __APPLE__ || defined WIN32) && defined USE_OPENGL + config.setValue("opengl", 1); +#else + config.setValue("opengl", 0); +#endif + config.setValue("screen", 0); + config.setValue("sound", 1); + config.setValue("guialpha", 0.8f); + config.setValue("remember", 1); + config.setValue("sfxVolume", 100); + config.setValue("musicVolume", 60); + config.setValue("fpslimit", 0); + std::string defaultUpdateHost = branding.getValue("defaultUpdateHost", + "http://updates.themanaworld.org"); + config.setValue("updatehost", defaultUpdateHost); + config.setValue("customcursor", 1); + 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"; - // Set log file - logger->setLogFile(homeDir + std::string("/aethyra.log")); + configFile = fopen(configPath.c_str(), "r"); - #ifdef PACKAGE_VERSION - logger->log("Starting Aethyra Version %s", PACKAGE_VERSION); - #else - logger->log("Starting Aethyra - Version not defined")); - #endif + // 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) { + std::cout << "Can't create " << configPath << ". " + << "Using Defaults." << std::endl; + } 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) { - std::cerr << _("Could not initialize SDL: ") << + std::cerr << "Could not initialize SDL: " << SDL_GetError() << std::endl; exit(1); } @@ -302,11 +411,13 @@ static void init_engine(const Options &options) SDL_EnableUNICODE(1); SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + SDL_WM_SetCaption(branding.getValue("appName", "The Mana World").c_str(), NULL); + ResourceManager *resman = ResourceManager::getInstance(); if (!resman->setWriteDir(homeDir)) { std::cout << homeDir - << _(" couldn't be set as home directory! Exiting.") + << " couldn't be set as home directory! Exiting." << std::endl; exit(1); } @@ -326,7 +437,7 @@ static void init_engine(const Options &options) if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX)) { - fprintf(stderr, _("Can't find Resources directory\n")); + fprintf(stderr, "Can't find Resources directory\n"); } CFRelease(resourcesURL); strncat(path, "/data", PATH_MAX - 1); @@ -335,51 +446,6 @@ static void init_engine(const Options &options) resman->addToSearchPath(PKG_DATADIR "data", true); #endif - // Fill configuration with defaults - logger->log("Initializing configuration..."); - config.setValue("host", "www.aethyra.org"); - config.setValue("port", 21001); - config.setValue("hwaccel", 0); -#if (defined __APPLE__ || defined WIN32) && defined USE_OPENGL - config.setValue("opengl", 1); -#else - config.setValue("opengl", 0); -#endif - config.setValue("screen", 0); - config.setValue("sound", 1); - config.setValue("guialpha", 0.8f); - config.setValue("remember", 1); - config.setValue("sfxVolume", 100); - config.setValue("musicVolume", 60); - config.setValue("fpslimit", 0); - config.setValue("updatehost", "http://www.aethyra.org/updates"); - config.setValue("customcursor", 1); - config.setValue("ChatLogLength", 128); - - // Checking if the configuration file exists... otherwise creates 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) { - std::cout << "Can't create " << configPath << ". " - "Using Defaults." << std::endl; - } else { - fclose(configFile); - config.init(configPath); - } - - SDL_WM_SetCaption("Aethyra", NULL); #ifdef WIN32 static SDL_SysWMinfo pInfo; SDL_GetWMInfo(&pInfo); @@ -389,7 +455,7 @@ static void init_engine(const Options &options) SetClassLong(pInfo.window, GCL_HICON, (LONG) icon); } #else - icon = IMG_Load(PKG_DATADIR "data/icons/aethyra.png"); + icon = IMG_Load(resman->getPath(branding.getValue("appIcon", "data/icons/tmw.png")).c_str()); if (icon) { SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); @@ -404,10 +470,10 @@ static void init_engine(const Options &options) Image::setLoadAsOpenGL(useOpenGL); // Create the graphics context - graphics = useOpenGL ? new OpenGLGraphics() : new Graphics(); + graphics = useOpenGL ? new OpenGLGraphics : new Graphics; #else // Create the graphics context - graphics = new Graphics(); + graphics = new Graphics; #endif const int width = (int) config.getValue("screenwidth", defaultScreenWidth); @@ -429,13 +495,17 @@ static void init_engine(const Options &options) graphics->_beginDraw(); // Initialize the item shortcuts. - itemShortcut = new ItemShortcut(); + itemShortcut = new ItemShortcut; // Initialize the emote shortcuts. - emoteShortcut = new EmoteShortcut(); + emoteShortcut = new EmoteShortcut; gui = new Gui(graphics); - state = LOGIN_STATE; /**< Initial game state */ +#ifdef TMWSERV_SUPPORT + state = STATE_CHOOSE_SERVER; /**< Initial game state */ +#else + state = STATE_LOGIN; /**< Initial game state */ +#endif // Initialize sound engine try @@ -450,7 +520,7 @@ static void init_engine(const Options &options) } catch (const char *err) { - state = ERROR_STATE; + state = STATE_ERROR; errorMessage = err; logger->log("Warning: %s", err); } @@ -463,7 +533,7 @@ static void init_engine(const Options &options) } /** Clear the engine */ -static void exit_engine() +static void exitEngine() { // Before config.write() since it writes the shortcuts to the config delete itemShortcut; @@ -486,9 +556,9 @@ static void exit_engine() ItemDB::unload(); MonsterDB::unload(); NPCDB::unload(); + StatusEffect::unload(); ResourceManager::deleteInstance(); - delete logger; SDL_FreeSurface(icon); } @@ -496,16 +566,19 @@ static void exit_engine() static void printHelp() { std::cout - << _("aethyra") << std::endl << std::endl + << _("tmw") << std::endl << std::endl << _("Options: ") << std::endl << _(" -C --configfile : Configuration file to use") << std::endl << _(" -d --data : Directory to load game data from") << std::endl << _(" -D --default : Bypass the login process with default settings") << std::endl << _(" -h --help : Display this help") << std::endl + << _(" -S --homedir : Directory to use as home directory") << std::endl << _(" -H --updatehost : Use this update host") << std::endl - << _(" -p --playername : Login with this player") << std::endl << _(" -P --password : Login with this password") << std::endl + << _(" -c --character : Login with this character") << std::endl + << _(" -o --port : Login Server Port") << std::endl + << _(" -s --server : Login Server name or IP") << std::endl << _(" -u --skipupdate : Skip the update downloads") << std::endl << _(" -U --username : Login with this username") << std::endl << _(" -v --version : Display the version") << std::endl; @@ -514,26 +587,28 @@ static void printHelp() static void printVersion() { #ifdef PACKAGE_VERSION - std::cout << _("Aethyra version ") << PACKAGE_VERSION << - std::endl; + std::cout << _("The Mana World version ") << PACKAGE_VERSION << std::endl; #else - std::cout << _("Aethyra version ") << + std::cout << _("The Mana World version ") << _("(local build?, PACKAGE_VERSION is not defined)") << std::endl; #endif } static void parseOptions(int argc, char *argv[], Options &options) { - const char *optstring = "hvud:U:P:Dp:C:H:"; + const char *optstring = "hvud:U:P:Dc:s:o:C:H:S:"; const struct option long_options[] = { { "configfile", required_argument, 0, 'C' }, { "data", required_argument, 0, 'd' }, { "default", no_argument, 0, 'D' }, - { "playername", required_argument, 0, 'p' }, { "password", required_argument, 0, 'P' }, + { "character", required_argument, 0, 'c' }, { "help", no_argument, 0, 'h' }, + { "homedir", required_argument, 0, 'S' }, { "updatehost", required_argument, 0, 'H' }, + { "port", required_argument, 0, 'o' }, + { "server", required_argument, 0, 's' }, { "skipupdate", no_argument, 0, 'u' }, { "username", required_argument, 0, 'U' }, { "version", no_argument, 0, 'v' }, @@ -566,12 +641,18 @@ static void parseOptions(int argc, char *argv[], Options &options) case 'H': options.updateHost = optarg; break; - case 'p': - options.playername = optarg; + case 'c': + options.character = optarg; break; case 'P': options.password = optarg; break; + case 's': + options.serverName = optarg; + break; + case 'o': + options.serverPort = (short)atoi(optarg); + break; case 'u': options.skipUpdate = true; break; @@ -581,6 +662,9 @@ static void parseOptions(int argc, char *argv[], Options &options) case 'v': options.printVersion = true; break; + case 'S': + homeDir = optarg; + break; } } } @@ -610,20 +694,64 @@ struct ErrorListener : public gcn::ActionListener { void action(const gcn::ActionEvent &event) { - state = loginData.registerLogin ? REGISTER_STATE : LOGIN_STATE; +#ifdef TMWSERV_SUPPORT + state = STATE_CHOOSE_SERVER; +#else + state = loginData.registerLogin ? STATE_REGISTER : STATE_LOGIN; +#endif } } errorListener; +#ifdef TMWSERV_SUPPORT +struct AccountListener : public gcn::ActionListener +{ + void action(const gcn::ActionEvent &event) + { + state = STATE_CHAR_SELECT; + } +} accountListener; + +struct LoginListener : public gcn::ActionListener +{ + void action(const gcn::ActionEvent &event) + { + state = STATE_LOGIN; + } +} loginListener; +#endif + +} // namespace + // TODO Find some nice place for these functions +#ifdef TMWSERV_SUPPORT +static void accountLogin(LoginData *loginData) +#else static void accountLogin(Network *network, LoginData *loginData) +#endif { +#ifdef EATHENA_SUPPORT logger->log("Trying to connect to account server..."); +#endif logger->log("Username is %s", loginData->username.c_str()); +#ifdef EATHENA_SUPPORT network->connect(loginData->hostname, loginData->port); network->registerHandler(&loginHandler); +#endif loginHandler.setLoginData(loginData); +#ifdef TMWSERV_SUPPORT + Net::registerHandler(&loginHandler); + + charServerHandler.setCharInfo(&charInfo); + Net::registerHandler(&charServerHandler); +#endif // Send login infos +#ifdef TMWSERV_SUPPORT + Net::AccountServer::login(accountServerConnection, + 0, // client version + loginData->username, + loginData->password); +#else MessageOut outMsg(network); outMsg.writeInt16(0x0064); outMsg.writeInt32(0); // client version @@ -637,16 +765,19 @@ static void accountLogin(Network *network, LoginData *loginData) * 0 here. */ outMsg.writeInt8(0x01); +#endif // Clear the password, avoids auto login when returning to login loginData->password = ""; +#ifdef EATHENA_SUPPORT // Remove _M or _F from username after a login for registration purpose if (loginData->registerLogin) { loginData->username = loginData->username.substr(0, loginData->username.length() - 2); } +#endif // TODO This is not the best place to save the config, but at least better // than the login gui window @@ -658,16 +789,13 @@ static void accountLogin(Network *network, LoginData *loginData) config.setValue("remember", loginData->remember); } -inline int MIN(int x, int y) -{ - return x < y ? x : y; -} +#ifdef EATHENA_SUPPORT static void positionDialog(Window *dialog, int screenWidth, int screenHeight) { dialog->setPosition( - MIN(screenWidth * 5 / 8, screenWidth - dialog->getWidth()), - MIN(screenHeight * 5 / 8, screenHeight - dialog->getHeight())); + (screenWidth - dialog->getWidth()) / 2, + (screenHeight - dialog->getHeight()) / 2); } static void charLogin(Network *network, LoginData *loginData) @@ -719,7 +847,143 @@ static void mapLogin(Network *network, LoginData *loginData) network->skip(4); } -} // namespace +#else + +static void accountRegister(LoginData *loginData) +{ + logger->log("Username is %s", loginData->username.c_str()); + + Net::registerHandler(&loginHandler); + + charServerHandler.setCharInfo(&charInfo); + Net::registerHandler(&charServerHandler); + + Net::AccountServer::registerAccount(accountServerConnection, + 0, // client version + loginData->username, + loginData->password, + loginData->email); +} + +static void accountUnRegister(LoginData *loginData) +{ + Net::registerHandler(&logoutHandler); + + Net::AccountServer::Account::unregister(loginData->username, + loginData->password); + +} + +static void accountChangePassword(LoginData *loginData) +{ + Net::registerHandler(&loginHandler); + + Net::AccountServer::Account::changePassword(loginData->username, + loginData->password, + loginData->newPassword); +} + +static void accountChangeEmail(LoginData *loginData) +{ + Net::registerHandler(&loginHandler); + + Net::AccountServer::Account::changeEmail(loginData->newEmail); +} + +static void switchCharacter(std::string *passToken) +{ + Net::registerHandler(&logoutHandler); + + logoutHandler.reset(); + logoutHandler.setScenario(LOGOUT_SWITCH_CHARACTER, passToken); + + Net::GameServer::logout(true); + Net::ChatServer::logout(); +} + +static void switchAccountServer() +{ + Net::registerHandler(&logoutHandler); + + logoutHandler.reset(); + logoutHandler.setScenario(LOGOUT_SWITCH_ACCOUNTSERVER); + + //Can't logout if we were not logged in ... + if (accountServerConnection->isConnected()) + { + Net::AccountServer::logout(); + } + else + { + logoutHandler.setAccountLoggedOut(); + } + + if (gameServerConnection->isConnected()) + { + Net::GameServer::logout(false); + } + else + { + logoutHandler.setGameLoggedOut(); + } + + if (chatServerConnection->isConnected()) + { + Net::ChatServer::logout(); + } + else + { + logoutHandler.setChatLoggedOut(); + } +} + +static void logoutThenExit() +{ + Net::registerHandler(&logoutHandler); + + logoutHandler.reset(); + logoutHandler.setScenario(LOGOUT_EXIT); + + // Can't logout if we were not logged in ... + if (accountServerConnection->isConnected()) + { + Net::AccountServer::logout(); + } + else + { + logoutHandler.setAccountLoggedOut(); + } + + if (gameServerConnection->isConnected()) + { + Net::GameServer::logout(false); + } + else + { + logoutHandler.setGameLoggedOut(); + } + + if (chatServerConnection->isConnected()) + { + Net::ChatServer::logout(); + } + else + { + logoutHandler.setChatLoggedOut(); + } +} + +static void reconnectAccount(const std::string &passToken) +{ + Net::registerHandler(&loginHandler); + + charServerHandler.setCharInfo(&charInfo); + Net::registerHandler(&charServerHandler); + + Net::AccountServer::reconnectAccount(accountServerConnection, passToken); +} + +#endif static void initInternationalization() { @@ -737,17 +1001,30 @@ static void initInternationalization() #endif } +static void xmlNullLogger(void *ctx, const char *msg, ...) +{ + // Does nothing, that's the whole point of it +} + +// Initialize libxml2 and check for potential ABI mismatches between +// compiled version and the shared library actually used. +static void initXML() +{ + xmlInitParser(); + LIBXML_TEST_VERSION; + + // Suppress libxml2 error messages + xmlSetGenericErrorFunc(NULL, xmlNullLogger); +} + extern "C" char const *_nl_locale_name_default(void); /** Main */ int main(int argc, char *argv[]) { - logger = new Logger(); - + // Parse command line options Options options; - parseOptions(argc, argv, options); - if (options.printHelp) { printHelp(); @@ -761,36 +1038,54 @@ int main(int argc, char *argv[]) initInternationalization(); - // Initialize libxml2 and check for potential ABI mismatches between - // compiled version and the shared library actually used. - xmlInitParser(); - LIBXML_TEST_VERSION; - - // Redirect libxml errors to /dev/null - FILE *nullFile = fopen("/dev/null", "w"); - xmlSetGenericErrorFunc(nullFile, NULL); - // Initialize PhysicsFS PHYSFS_init(argv[0]); - init_engine(options); + initXML(); - SDL_Event event; + // load branding information + branding.init("data/branding.xml"); - unsigned int oldstate = !state; // We start with a status change. + initHomeDir(); + // Configure logger + logger = new Logger(); + logger->setLogFile(homeDir + std::string("/tmw.log")); + logger->setLogToStandardOut(config.getValue("logToStandardOut", 0)); + + // Log the tmw version +#ifdef PACKAGE_VERSION +#ifdef TMWSERV_SUPPORT + logger->log("The Mana World v%s TMWServ", PACKAGE_VERSION); +#else + logger->log("The Mana World v%s eAthena", PACKAGE_VERSION); +#endif +#else + logger->log("The Mana World - version not defined"); +#endif + + initConfiguration(options); + + initEngine(options); // Needs to be created in main, as the updater uses it guiPalette = new Palette; Game *game = NULL; Window *currentDialog = NULL; +#ifdef TMWSERV_SUPPORT + QuitDialog* quitDialog = NULL; +#endif Image *login_wallpaper = NULL; - setupWindow = new Setup(); + setupWindow = new Setup; gcn::Container *top = static_cast<gcn::Container*>(gui->getTop()); #ifdef PACKAGE_VERSION - gcn::Label *versionLabel = new Label(PACKAGE_VERSION); - top->add(versionLabel, 2, 2); +#ifdef TMWSERV_SUPPORT + gcn::Label *versionLabel = new Label(strprintf("%s TMWserv", PACKAGE_VERSION)); +#else + gcn::Label *versionLabel = new Label(strprintf("%s eAthena", PACKAGE_VERSION)); +#endif + top->add(versionLabel, 25, 2); #endif ProgressBar *progressBar = new ProgressBar(0.0f, 100, 20, 168, 116, 31); gcn::Label *progressLabel = new Label(); @@ -802,7 +1097,22 @@ int main(int argc, char *argv[]) setup->setPosition(top->getWidth() - setup->getWidth() - 3, 3); top->add(setup); - sound.playMusic("Magick - Real.ogg"); + sound.playMusic(branding.getValue("loginMusic", "")); + + // Server choice + if (options.serverName.empty()) { + loginData.hostname = config.getValue("MostUsedServerName0", + branding.getValue("defaultServer", "server.themanaworld.org").c_str()); + } + else { + loginData.hostname = options.serverName; + } + if (options.serverPort == 0) { + loginData.port = (short)config.getValue("MostUsedServerPort0", + branding.getValue("defaultPort", 9601)); + } else { + loginData.port = options.serverPort; + } loginData.username = options.username; if (loginData.username.empty()) { @@ -813,18 +1123,30 @@ int main(int argc, char *argv[]) if (!options.password.empty()) { loginData.password = options.password; } - loginData.hostname = config.getValue("host", "www.aethyra.org"); - loginData.port = (short)config.getValue("port", 21001); + +#ifdef EATHENA_SUPPORT + loginData.hostname = config.getValue("host", "server.themanaworld.org"); + loginData.port = (short)config.getValue("port", 6901); +#endif loginData.remember = config.getValue("remember", 0); loginData.registerLogin = false; +#ifdef TMWSERV_SUPPORT + Net::initialize(); + accountServerConnection = Net::getConnection(); + gameServerConnection = Net::getConnection(); + chatServerConnection = Net::getConnection(); +#else SDLNet_Init(); - Network *network = new Network(); + Network *network = new Network; +#endif // Set the most appropriate wallpaper, based on screen width int screenWidth = (int) config.getValue("screenwidth", defaultScreenWidth); +#ifdef EATHENA_SUPPORT int screenHeight = static_cast<int>(config.getValue("screenheight", defaultScreenHeight)); +#endif std::string wallpaperName; wallpaperName = "graphics/images/login_wallpaper.png"; @@ -837,12 +1159,22 @@ int main(int argc, char *argv[]) else if (screenWidth >= 1600) wallpaperName = "graphics/images/login_wallpaper_1600x1200.png"; - login_wallpaper = ResourceManager::getInstance()-> getImage(wallpaperName); + if (!ResourceManager::getInstance()->exists(wallpaperName)) + wallpaperName = "graphics/images/login_wallpaper.png"; + + login_wallpaper = ResourceManager::getInstance()->getImage(wallpaperName); if (!login_wallpaper) logger->log("Couldn't load %s as wallpaper", wallpaperName.c_str()); - while (state != EXIT_STATE) + unsigned int oldstate = !state; // We start with a status change. + + SDL_Event event; +#ifdef TMWSERV_SUPPORT + while (state != STATE_FORCE_QUIT) +#else + while (state != STATE_EXIT) +#endif { // Handle SDL events while (SDL_PollEvent(&event)) @@ -850,32 +1182,50 @@ int main(int argc, char *argv[]) switch (event.type) { case SDL_QUIT: - state = EXIT_STATE; +#ifdef TMWSERV_SUPPORT + state = STATE_FORCE_QUIT; +#else + state = STATE_EXIT; +#endif break; case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) - state = EXIT_STATE; - + { +#ifdef TMWSERV_SUPPORT + if (!quitDialog) + quitDialog = new QuitDialog(NULL, &quitDialog); + else + quitDialog->requestMoveToTop(); +#else + state = STATE_EXIT; +#endif + } break; } guiInput->pushInput(event); } +#ifdef TMWSERV_SUPPORT + Net::flush(); +#else network->flush(); network->dispatchMessages(); +#endif gui->logic(); +#ifdef EATHENA_SUPPORT if (network->getState() == Network::NET_ERROR) { - state = ERROR_STATE; + state = STATE_ERROR; if (!network->getError().empty()) errorMessage = network->getError(); else errorMessage = _("Got disconnected from server!"); } +#endif if (progressBar->isVisible()) { @@ -887,7 +1237,7 @@ int main(int argc, char *argv[]) if (graphics->getWidth() > login_wallpaper->getWidth() || graphics->getHeight() > login_wallpaper->getHeight()) { - graphics->setColor(gcn::Color(255, 255, 255)); + graphics->setColor(gcn::Color(64, 64, 64)); graphics->fillRectangle(gcn::Rectangle( 0, 0, graphics->getWidth(), graphics->getHeight())); } @@ -897,11 +1247,320 @@ int main(int argc, char *argv[]) gui->draw(); graphics->updateScreen(); +#ifdef TMWSERV_SUPPORT + // TODO: Add connect timeouts + if (state == STATE_CONNECT_ACCOUNT && + accountServerConnection->isConnected()) + { + if (options.skipUpdate) { + state = STATE_LOADDATA; + } else { + state = STATE_UPDATE; + } + } + else if (state == STATE_CONNECT_GAME && + gameServerConnection->isConnected() && + chatServerConnection->isConnected()) + { + accountServerConnection->disconnect(); + Net::clearHandlers(); + + state = STATE_GAME; + } + else if (state == STATE_RECONNECT_ACCOUNT && + accountServerConnection->isConnected()) + { + reconnectAccount(token); + state = STATE_WAIT; + } + + if (state != oldstate) + { + // Load updates after exiting the update state + if (oldstate == STATE_UPDATE) + { + loadUpdates(); + // Reload the wallpaper in case that it was updated + login_wallpaper->decRef(); + login_wallpaper = ResourceManager::getInstance()->getImage( + branding.getValue("loginWallpaper", + "graphics/images/login_wallpaper.png")); + } + + 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) { + currentDialog = new ServerDialog(&loginData); + } else { + state = STATE_CONNECT_ACCOUNT; + + // Reset options so that cancelling or connect + // timeout will show the server dialog. + options.serverName.clear(); + options.serverPort = 0; + } + break; + + case STATE_CONNECT_ACCOUNT: + logger->log("State: CONNECT_ACCOUNT"); + logger->log("Trying to connect to account server..."); + accountServerConnection->connect(loginData.hostname, + loginData.port); + currentDialog = new ConnectionDialog( + STATE_SWITCH_ACCOUNTSERVER_ATTEMPT); + 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(); + logger->log("State: UPDATE"); + currentDialog = new UpdaterWindow(updateHost, + homeDir + "/" + updatesDir); + break; + + case STATE_LOGIN: + logger->log("State: LOGIN"); + 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_LOADDATA: + logger->log("State: LOADDATA"); + + // Add customdata directory + ResourceManager::getInstance()->searchAndAddArchives( + "customdata/", + "zip", + false); + + // Load XML databases + ColorDB::load(); + ItemDB::load(); + MonsterDB::load(); + NPCDB::load(); + EmoteDB::load(); + Units::loadUnits(); + + state = STATE_LOGIN; + break; + + case STATE_LOGIN_ATTEMPT: + accountLogin(&loginData); + 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_SWITCH_ACCOUNTSERVER: + logger->log("State: SWITCH_ACCOUNTSERVER"); + + gameServerConnection->disconnect(); + chatServerConnection->disconnect(); + accountServerConnection->disconnect(); + + state = STATE_CHOOSE_SERVER; + break; + + case STATE_SWITCH_ACCOUNTSERVER_ATTEMPT: + logger->log("State: SWITCH_ACCOUNTSERVER_ATTEMPT"); + switchAccountServer(); + + state = STATE_SWITCH_ACCOUNTSERVER; + break; + + case STATE_REGISTER: + logger->log("State: REGISTER"); + currentDialog = new RegisterDialog(&loginData); + break; + + case STATE_REGISTER_ATTEMPT: + accountRegister(&loginData); + break; + + case STATE_CHAR_SELECT: + logger->log("State: CHAR_SELECT"); + currentDialog = + new CharSelectDialog(&charInfo, &loginData); + + if (((CharSelectDialog*) currentDialog)-> + selectByName(options.character)) { + ((CharSelectDialog*) currentDialog)->action( + gcn::ActionEvent(NULL, "ok")); + } else { + ((CharSelectDialog*) currentDialog)->selectByName( + config.getValue("lastCharacter", "")); + } + + break; + + case STATE_CHANGEEMAIL_ATTEMPT: + logger->log("State: CHANGE EMAIL ATTEMPT"); + accountChangeEmail(&loginData); + break; + + case STATE_CHANGEEMAIL: + logger->log("State: CHANGE EMAIL"); + currentDialog = new OkDialog("Email Address change", + "Email Address changed successfully!"); + currentDialog->addActionListener(&accountListener); + currentDialog = NULL; // OkDialog deletes itself + loginData.email = loginData.newEmail; + loginData.newEmail = ""; + break; + + case STATE_CHANGEPASSWORD_ATTEMPT: + logger->log("State: CHANGE PASSWORD ATTEMPT"); + accountChangePassword(&loginData); + break; + + case STATE_CHANGEPASSWORD: + logger->log("State: CHANGE PASSWORD"); + 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_UNREGISTER_ATTEMPT: + logger->log("State: UNREGISTER ATTEMPT"); + accountUnRegister(&loginData); + break; + + case STATE_UNREGISTER: + logger->log("State: UNREGISTER"); + accountServerConnection->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_ACCOUNTCHANGE_ERROR: + logger->log("State: ACCOUNT CHANGE ERROR"); + currentDialog = new OkDialog("Error ", errorMessage); + currentDialog->addActionListener(&accountListener); + currentDialog = NULL; // OkDialog deletes itself + break; + + + case STATE_ERROR: + logger->log("State: ERROR"); + currentDialog = new OkDialog("Error", errorMessage); + currentDialog->addActionListener(&errorListener); + currentDialog = NULL; // OkDialog deletes itself + gameServerConnection->disconnect(); + chatServerConnection->disconnect(); + Net::clearHandlers(); + break; + + case STATE_CONNECT_GAME: + logger->log("State: CONNECT_GAME"); + currentDialog = new ConnectionDialog(STATE_SWITCH_ACCOUNTSERVER_ATTEMPT); + break; + + case STATE_GAME: + logger->log("Memorizing selected character %s", + player_node->getName().c_str()); + config.setValue("lastCharacter", player_node->getName()); + + Net::GameServer::connect(gameServerConnection, token); + Net::ChatServer::connect(chatServerConnection, token); + sound.fadeOutMusic(1000); + +#ifdef PACKAGE_VERSION + delete versionLabel; + versionLabel = NULL; +#endif + currentDialog = NULL; + + logger->log("State: GAME"); + game = new Game; + game->logic(); + delete game; + + // If the quitdialog didn't set the next state + if (state == STATE_GAME) + { + state = STATE_EXIT; + } + break; + + case STATE_SWITCH_CHARACTER: + logger->log("State: SWITCH_CHARACTER"); + switchCharacter(&token); + break; + + case STATE_RECONNECT_ACCOUNT: + logger->log("State: RECONNECT_ACCOUNT"); + + // Done with game & chat + gameServerConnection->disconnect(); + chatServerConnection->disconnect(); + + accountServerConnection->connect( + loginData.hostname, + loginData.port); + break; + + case STATE_WAIT: + break; + + case STATE_EXIT: + logger->log("State: EXIT"); + logoutThenExit(); + break; + + default: + state = STATE_FORCE_QUIT; + break; + } + } + +#else // no TMWSERV_SUPPORT + if (state != oldstate) { switch (oldstate) { - case UPDATE_STATE: + case STATE_UPDATE: loadUpdates(); // Reload the wallpaper in case that it was updated login_wallpaper->decRef(); @@ -910,12 +1569,12 @@ int main(int argc, char *argv[]) break; // Those states don't cause a network disconnect - case LOADDATA_STATE: + case STATE_LOADDATA: break; - case ACCOUNT_STATE: - case CHAR_CONNECT_STATE: - case CONNECTING_STATE: + case STATE_ACCOUNT: + case STATE_CHAR_CONNECT: + case STATE_CONNECTING: progressBar->setVisible(false); progressLabel->setCaption(""); break; @@ -928,8 +1587,8 @@ int main(int argc, char *argv[]) oldstate = state; - if (currentDialog && state != ACCOUNT_STATE && - state != CHAR_CONNECT_STATE) + if (currentDialog && state != STATE_ACCOUNT && + state != STATE_CHAR_CONNECT) { delete currentDialog; currentDialog = NULL; @@ -937,7 +1596,7 @@ int main(int argc, char *argv[]) switch (state) { - case LOADDATA_STATE: + case STATE_LOADDATA: logger->log("State: LOADDATA"); // Add customdata directory @@ -952,18 +1611,22 @@ int main(int argc, char *argv[]) MonsterDB::load(); NPCDB::load(); EmoteDB::load(); + StatusEffect::load(); Being::load(); // Hairstyles - state = CHAR_CONNECT_STATE; + // Load units + Units::loadUnits(); + + state = STATE_CHAR_CONNECT; break; - case LOGIN_STATE: + case STATE_LOGIN: logger->log("State: LOGIN"); if (!loginData.password.empty()) { loginData.registerLogin = false; - state = ACCOUNT_STATE; + state = STATE_ACCOUNT; } else { @@ -973,13 +1636,13 @@ int main(int argc, char *argv[]) } break; - case REGISTER_STATE: + case STATE_REGISTER: logger->log("State: REGISTER"); currentDialog = new RegisterDialog(&loginData); positionDialog(currentDialog, screenWidth, screenHeight); break; - case CHAR_SERVER_STATE: + case STATE_CHAR_SERVER: logger->log("State: CHAR_SERVER"); if (n_server == 1) @@ -988,32 +1651,32 @@ int main(int argc, char *argv[]) loginData.hostname = ipToString(si->address); loginData.port = si->port; loginData.updateHost = si->updateHost; - state = UPDATE_STATE; + state = STATE_UPDATE; } else { - int nextState = UPDATE_STATE; + int nextState = STATE_UPDATE; currentDialog = new ServerSelectDialog(&loginData, nextState); positionDialog(currentDialog, screenWidth, screenHeight); if (options.chooseDefault - || !options.playername.empty()) + || !options.character.empty()) { ((ServerSelectDialog*) currentDialog)->action( gcn::ActionEvent(NULL, "ok")); } } break; - case CHAR_SELECT_STATE: + case STATE_CHAR_SELECT: logger->log("State: CHAR_SELECT"); currentDialog = new CharSelectDialog(network, &charInfo, - (loginData.sex == 0) ? - GENDER_FEMALE : GENDER_MALE); + (loginData.sex == 0) ? + GENDER_FEMALE : GENDER_MALE); positionDialog(currentDialog, screenWidth, screenHeight); if (((CharSelectDialog*) currentDialog)-> - selectByName(options.playername)) + selectByName(options.character)) options.chooseDefault = true; else ((CharSelectDialog*) currentDialog)->selectByName( @@ -1025,7 +1688,7 @@ int main(int argc, char *argv[]) break; - case GAME_STATE: + case STATE_GAME: sound.fadeOutMusic(1000); #ifdef PACKAGE_VERSION @@ -1046,13 +1709,13 @@ int main(int argc, char *argv[]) game = new Game(network); game->logic(); delete game; - state = EXIT_STATE; + state = STATE_EXIT; break; - case UPDATE_STATE: + case STATE_UPDATE: if (options.skipUpdate) { - state = LOADDATA_STATE; + state = STATE_LOADDATA; } else { @@ -1072,7 +1735,7 @@ int main(int argc, char *argv[]) } break; - case ERROR_STATE: + case STATE_ERROR: logger->log("State: ERROR"); currentDialog = new OkDialog(_("Error"), errorMessage); positionDialog(currentDialog, screenWidth, screenHeight); @@ -1082,7 +1745,7 @@ int main(int argc, char *argv[]) network->clearHandlers(); break; - case CONNECTING_STATE: + case STATE_CONNECTING: logger->log("State: CONNECTING"); progressBar->setVisible(true); progressLabel->setCaption( @@ -1091,7 +1754,7 @@ int main(int argc, char *argv[]) mapLogin(network, &loginData); break; - case CHAR_CONNECT_STATE: + case STATE_CHAR_CONNECT: progressBar->setVisible(true); progressLabel->setCaption( _("Connecting to character server...")); @@ -1099,7 +1762,7 @@ int main(int argc, char *argv[]) charLogin(network, &loginData); break; - case ACCOUNT_STATE: + case STATE_ACCOUNT: progressBar->setVisible(true); progressLabel->setCaption( _("Connecting to account server...")); @@ -1108,10 +1771,11 @@ int main(int argc, char *argv[]) break; default: - state = EXIT_STATE; + state = STATE_EXIT; break; } } +#endif /* * This loop can really stress the CPU, for no reason since it's * just constantly redrawing the wallpaper. Added the following @@ -1129,15 +1793,28 @@ int main(int argc, char *argv[]) delete setup; delete setupWindow; +#ifdef TMWSERV_SUPPORT + if (accountServerConnection) + accountServerConnection->disconnect(); + if (gameServerConnection) + gameServerConnection->disconnect(); + if (chatServerConnection) + chatServerConnection->disconnect(); + + delete accountServerConnection; + delete gameServerConnection; + delete chatServerConnection; + Net::finalize(); +#else delete network; SDLNet_Quit(); +#endif - if (nullFile) - fclose(nullFile); - - logger->log("State: EXIT"); - exit_engine(); + logger->log("Quitting"); + exitEngine(); PHYSFS_deinit(); + delete logger; + return 0; } |