00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "main.h"
00023
00024 #include "configuration.h"
00025 #include "emoteshortcut.h"
00026 #include "game.h"
00027 #include "graphics.h"
00028 #include "itemshortcut.h"
00029 #include "keyboardconfig.h"
00030 #include "localplayer.h"
00031 #include "lockedarray.h"
00032 #include "log.h"
00033 #include "logindata.h"
00034 #ifdef USE_OPENGL
00035 #include "openglgraphics.h"
00036 #endif
00037 #include "player_relations.h"
00038 #include "serverinfo.h"
00039 #include "sound.h"
00040 #include "statuseffect.h"
00041 #include "units.h"
00042
00043 #include "gui/widgets/button.h"
00044 #include "gui/widgets/desktop.h"
00045 #include "gui/widgets/label.h"
00046 #include "gui/widgets/progressbar.h"
00047
00048 #include "gui/char_select.h"
00049 #include "gui/gui.h"
00050 #include "gui/login.h"
00051 #include "gui/ok_dialog.h"
00052 #include "gui/palette.h"
00053 #include "gui/register.h"
00054 #include "gui/sdlinput.h"
00055 #include "gui/serverselectdialog.h"
00056 #include "gui/setup.h"
00057 #ifdef TMWSERV_SUPPORT
00058 #include "gui/connection.h"
00059 #include "gui/quitdialog.h"
00060 #include "gui/serverdialog.h"
00061 #endif
00062 #include "gui/updatewindow.h"
00063
00064 #include "net/charhandler.h"
00065 #include "net/generalhandler.h"
00066 #include "net/loginhandler.h"
00067 #include "net/maphandler.h"
00068 #include "net/net.h"
00069 #ifdef TMWSERV_SUPPORT
00070 #include "net/tmwserv/charserverhandler.h"
00071 #include "net/tmwserv/connection.h"
00072 #include "net/tmwserv/generalhandler.h"
00073 #include "net/tmwserv/loginhandler.h"
00074 #include "net/tmwserv/logouthandler.h"
00075 #include "net/tmwserv/network.h"
00076 #else
00077 #include "net/ea/generalhandler.h"
00078 #include "net/ea/network.h"
00079 #endif
00080
00081 #ifdef TMWSERV_SUPPORT
00082 #include "net/tmwserv/accountserver/accountserver.h"
00083 #include "net/tmwserv/accountserver/account.h"
00084
00085 #include "net/tmwserv/chatserver/chatserver.h"
00086
00087 #include "net/tmwserv/gameserver/gameserver.h"
00088 #endif
00089
00090 #include "resources/colordb.h"
00091 #include "resources/emotedb.h"
00092 #include "resources/image.h"
00093 #include "resources/itemdb.h"
00094 #include "resources/monsterdb.h"
00095 #include "resources/npcdb.h"
00096 #include "resources/resourcemanager.h"
00097
00098 #include "utils/gettext.h"
00099 #include "utils/stringutils.h"
00100 #include "utils/strprintf.h"
00101
00102 #include <SDL_image.h>
00103
00104 #include <guichan/actionlistener.hpp>
00105
00106 #include <libxml/parser.h>
00107
00108 #include <getopt.h>
00109 #include <iostream>
00110 #include <physfs.h>
00111 #include <unistd.h>
00112 #include <vector>
00113
00114 #ifdef __APPLE__
00115 #include <CoreFoundation/CFBundle.h>
00116 #endif
00117
00118 #ifdef __MINGW32__
00119 #include <windows.h>
00120 #define usleep(usec) (Sleep ((usec) / 1000), 0)
00121 #endif
00122
00123 #ifdef WIN32
00124 #include <SDL_syswm.h>
00125 #else
00126 #include <cerrno>
00127 #include <sys/stat.h>
00128 #endif
00129
00130 namespace
00131 {
00132 struct SetupListener : public gcn::ActionListener
00133 {
00137 void action(const gcn::ActionEvent &event);
00138 } listener;
00139 }
00140
00141 static const int defaultScreenWidth = 800;
00142 static const int defaultScreenHeight = 600;
00143
00144 static const int defaultSfxVolume = 100;
00145 static const int defaultMusicVolume = 60;
00146
00147 std::string token;
00148
00149
00150 char n_server, n_character;
00151
00152
00153 class SERVER_INFO;
00154 SERVER_INFO **server_info;
00155
00156 #ifdef TMWSERV_SUPPORT
00157 extern Net::Connection *gameServerConnection;
00158 extern Net::Connection *chatServerConnection;
00159 extern Net::Connection *accountServerConnection;
00160 #endif
00161
00162 Graphics *graphics;
00163
00164 unsigned char state;
00165 std::string errorMessage;
00166
00167 Sound sound;
00168 Music *bgm;
00169
00170 Configuration config;
00171 Configuration branding;
00172 Logger *logger;
00173 KeyboardConfig keyboard;
00174
00175 LoginData loginData;
00176 LockedArray<LocalPlayer*> charInfo(maxSlot + 1);
00177
00178 Palette *guiPalette;
00179
00180
00181 namespace {
00182
00183 std::string homeDir;
00184 std::string updateHost;
00185 std::string updatesDir;
00186
00187 SDL_Surface *icon;
00188
00193 struct Options
00194 {
00198 Options():
00199 printHelp(false),
00200 printVersion(false),
00201 skipUpdate(false),
00202 chooseDefault(false),
00203 serverPort(0)
00204 {}
00205
00206 bool printHelp;
00207 bool printVersion;
00208 bool skipUpdate;
00209 bool chooseDefault;
00210 std::string username;
00211 std::string password;
00212 std::string character;
00213 std::string configPath;
00214 std::string updateHost;
00215 std::string dataPath;
00216
00217 std::string serverName;
00218 short serverPort;
00219 };
00220
00225 static void setUpdatesDir()
00226 {
00227 std::stringstream updates;
00228
00229
00230 if (updateHost.empty())
00231 {
00232 updateHost =
00233 config.getValue("updatehost", "http://updates.themanaworld.org/");
00234 }
00235
00236
00237 if (updateHost.at(updateHost.size() - 1) == '/')
00238 updateHost.resize(updateHost.size() - 1);
00239
00240
00241 size_t pos;
00242 pos = updateHost.find("://");
00243 if (pos != updateHost.npos)
00244 {
00245 if (pos + 3 < updateHost.length())
00246 {
00247 updates << "updates/" << updateHost.substr(pos + 3);
00248 updatesDir = updates.str();
00249 }
00250 else
00251 {
00252 logger->log("Error: Invalid update host: %s", updateHost.c_str());
00253 errorMessage = _("Invalid update host: ") + updateHost;
00254 state = STATE_ERROR;
00255 }
00256 }
00257 else
00258 {
00259 logger->log("Warning: no protocol was specified for the update host");
00260 updates << "updates/" << updateHost << "/" << loginData.port;
00261 updatesDir = updates.str();
00262 }
00263
00264 ResourceManager *resman = ResourceManager::getInstance();
00265
00266
00267 if (!resman->isDirectory("/" + updatesDir))
00268 {
00269 if (!resman->mkdir("/" + updatesDir))
00270 {
00271 #if defined WIN32
00272 std::string newDir = homeDir + "\\" + updatesDir;
00273 std::string::size_type loc = newDir.find("/", 0);
00274
00275 while (loc != std::string::npos)
00276 {
00277 newDir.replace(loc, 1, "\\");
00278 loc = newDir.find("/", loc);
00279 }
00280
00281 if (!CreateDirectory(newDir.c_str(), 0) &&
00282 GetLastError() != ERROR_ALREADY_EXISTS)
00283 {
00284 logger->log("Error: %s can't be made, but doesn't exist!",
00285 newDir.c_str());
00286 errorMessage = _("Error creating updates directory!");
00287 state = STATE_ERROR;
00288 }
00289 #else
00290 logger->log("Error: %s/%s can't be made, but doesn't exist!",
00291 homeDir.c_str(), updatesDir.c_str());
00292 errorMessage = _("Error creating updates directory!");
00293 state = STATE_ERROR;
00294 #endif
00295 }
00296 }
00297 }
00298
00303 static void initHomeDir()
00304 {
00305 homeDir = std::string(PHYSFS_getUserDir()) +
00306 "/." +
00307 branding.getValue("appShort", "tmw");
00308 #if defined WIN32
00309 if (!CreateDirectory(homeDir.c_str(), 0) &&
00310 GetLastError() != ERROR_ALREADY_EXISTS)
00311 #elif defined __APPLE__
00312
00313 homeDir = std::string(PHYSFS_getUserDir()) +
00314 "/Library/Application Support/" +
00315 branding.getValue("appName", "The Mana World");
00316 if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) &&
00317 (errno != EEXIST))
00318 #else
00319
00320 if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) &&
00321 (errno != EEXIST))
00322 #endif
00323 {
00324 std::cout << homeDir
00325 << _(" can't be created, but it doesn't exist! Exiting.")
00326 << std::endl;
00327 exit(1);
00328 }
00329 }
00330
00334 static void initConfiguration(const Options &options)
00335 {
00336
00337 logger->log("Initializing configuration...");
00338 std::string defaultHost = branding.getValue("defaultServer",
00339 "server.themanaworld.org");
00340 config.setValue("host", defaultHost);
00341 #ifdef TWMSERV_SUPPORT
00342 int defaultPort = (int)branding.getValue("defaultPort", 9601);
00343 #else
00344 int defaultPort = (int)branding.getValue("defaultPort", 6901);
00345 #endif
00346 config.setValue("port", defaultPort);
00347 config.setValue("hwaccel", false);
00348 #if (defined __APPLE__ || defined WIN32) && defined USE_OPENGL
00349 config.setValue("opengl", true);
00350 #else
00351 config.setValue("opengl", false);
00352 #endif
00353 config.setValue("screen", false);
00354 config.setValue("sound", true);
00355 config.setValue("guialpha", 0.8f);
00356 config.setValue("remember", true);
00357 config.setValue("sfxVolume", 100);
00358 config.setValue("musicVolume", 60);
00359 config.setValue("fpslimit", 60);
00360 std::string defaultUpdateHost = branding.getValue("defaultUpdateHost",
00361 "http://updates.themanaworld.org");
00362 config.setValue("updatehost", defaultUpdateHost);
00363 config.setValue("customcursor", true);
00364 config.setValue("ChatLogLength", 128);
00365
00366
00367
00368 FILE *configFile = 0;
00369 std::string configPath = options.configPath;
00370
00371 if (configPath.empty())
00372 configPath = homeDir + "/config.xml";
00373
00374 configFile = fopen(configPath.c_str(), "r");
00375
00376
00377 if (configFile == NULL) {
00378
00379 configFile = fopen(configPath.c_str(), "wt");
00380 }
00381 if (configFile == NULL) {
00382 std::cout << "Can't create " << configPath << ". "
00383 << "Using Defaults." << std::endl;
00384 } else {
00385 fclose(configFile);
00386 config.init(configPath);
00387 }
00388 }
00389
00393 static void initEngine(const Options &options)
00394 {
00395
00396 logger->log("Initializing SDL...");
00397 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
00398 std::cerr << "Could not initialize SDL: " <<
00399 SDL_GetError() << std::endl;
00400 exit(1);
00401 }
00402 atexit(SDL_Quit);
00403
00404 SDL_EnableUNICODE(1);
00405 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
00406
00407 SDL_WM_SetCaption(branding.getValue("appName", "The Mana World").c_str(), NULL);
00408
00409 ResourceManager *resman = ResourceManager::getInstance();
00410
00411 if (!resman->setWriteDir(homeDir)) {
00412 std::cout << homeDir
00413 << " couldn't be set as home directory! Exiting."
00414 << std::endl;
00415 exit(1);
00416 }
00417
00418
00419 resman->addToSearchPath(homeDir, false);
00420
00421
00422 if (!options.dataPath.empty()) {
00423 resman->addToSearchPath(options.dataPath, true);
00424 }
00425 resman->addToSearchPath("data", true);
00426 #if defined __APPLE__
00427 CFBundleRef mainBundle = CFBundleGetMainBundle();
00428 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
00429 char path[PATH_MAX];
00430 if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path,
00431 PATH_MAX))
00432 {
00433 fprintf(stderr, "Can't find Resources directory\n");
00434 }
00435 CFRelease(resourcesURL);
00436 strncat(path, "/data", PATH_MAX - 1);
00437 resman->addToSearchPath(path, true);
00438 #else
00439 resman->addToSearchPath(PKG_DATADIR "data", true);
00440 #endif
00441
00442 #ifdef WIN32
00443 static SDL_SysWMinfo pInfo;
00444 SDL_GetWMInfo(&pInfo);
00445 HICON icon = LoadIcon(GetModuleHandle(NULL), "A");
00446 if (icon)
00447 {
00448 SetClassLong(pInfo.window, GCL_HICON, (LONG) icon);
00449 }
00450 #else
00451 icon = IMG_Load(resman->getPath(branding.getValue("appIcon", "data/icons/tmw.png")).c_str());
00452 if (icon)
00453 {
00454 SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE);
00455 SDL_WM_SetIcon(icon, NULL);
00456 }
00457 #endif
00458
00459 #ifdef USE_OPENGL
00460 bool useOpenGL = (config.getValue("opengl", 0) == 1);
00461
00462
00463 Image::setLoadAsOpenGL(useOpenGL);
00464
00465
00466 graphics = useOpenGL ? new OpenGLGraphics : new Graphics;
00467 #else
00468
00469 graphics = new Graphics;
00470 #endif
00471
00472 const int width = (int) config.getValue("screenwidth", defaultScreenWidth);
00473 const int height = (int) config.getValue("screenheight", defaultScreenHeight);
00474 const int bpp = 0;
00475 const bool fullscreen = ((int) config.getValue("screen", 0) == 1);
00476 const bool hwaccel = ((int) config.getValue("hwaccel", 0) == 1);
00477
00478
00479 if (!graphics->setVideoMode(width, height, bpp, fullscreen, hwaccel))
00480 {
00481 std::cerr << _("Couldn't set ")
00482 << width << "x" << height << "x" << bpp << _(" video mode: ")
00483 << SDL_GetError() << std::endl;
00484 exit(1);
00485 }
00486
00487
00488 graphics->_beginDraw();
00489
00490
00491 itemShortcut = new ItemShortcut;
00492
00493
00494 emoteShortcut = new EmoteShortcut;
00495
00496 gui = new Gui(graphics);
00497 #ifdef TMWSERV_SUPPORT
00498 state = STATE_CHOOSE_SERVER;
00499 #else
00500 state = STATE_LOGIN;
00501 #endif
00502
00503
00504 try
00505 {
00506 if (config.getValue("sound", 0) == 1)
00507 sound.init();
00508
00509 sound.setSfxVolume((int) config.getValue("sfxVolume",
00510 defaultSfxVolume));
00511 sound.setMusicVolume((int) config.getValue("musicVolume",
00512 defaultMusicVolume));
00513 }
00514 catch (const char *err)
00515 {
00516 state = STATE_ERROR;
00517 errorMessage = err;
00518 logger->log("Warning: %s", err);
00519 }
00520
00521
00522 keyboard.init();
00523
00524
00525 player_relations.init();
00526 }
00527
00529 static void exitEngine()
00530 {
00531
00532 delete itemShortcut;
00533 delete emoteShortcut;
00534
00535 config.write();
00536
00537 delete gui;
00538 delete graphics;
00539
00540
00541 xmlCleanupParser();
00542
00543
00544 sound.close();
00545
00546
00547 ColorDB::unload();
00548 EmoteDB::unload();
00549 ItemDB::unload();
00550 MonsterDB::unload();
00551 NPCDB::unload();
00552 StatusEffect::unload();
00553
00554 ResourceManager::deleteInstance();
00555
00556 SDL_FreeSurface(icon);
00557 }
00558
00559 static void printHelp()
00560 {
00561 std::cout
00562 << _("tmw") << std::endl << std::endl
00563 << _("Options: ") << std::endl
00564 << _(" -C --configfile : Configuration file to use") << std::endl
00565 << _(" -d --data : Directory to load game data from") << std::endl
00566 << _(" -D --default : Bypass the login process with default settings"
00567 " from config.xml")
00568 << std::endl
00569 << _(" -h --help : Display this help") << std::endl
00570 << _(" -S --homedir : Directory to use as home directory") << std::endl
00571 << _(" -H --updatehost : Use this update host") << std::endl
00572 << _(" -P --password : Login with this password") << std::endl
00573 << _(" -c --character : Login with this character") << std::endl
00574 << _(" -o --port : Login Server Port") << std::endl
00575 << _(" -s --server : Login Server name or IP") << std::endl
00576 << _(" -u --skipupdate : Skip the update downloads") << std::endl
00577 << _(" -U --username : Login with this username") << std::endl
00578 << _(" -v --version : Display the version") << std::endl;
00579 }
00580
00581 static void printVersion()
00582 {
00583 std::cout << _("The Mana World ") << FULL_VERSION << std::endl;
00584 }
00585
00586 static void parseOptions(int argc, char *argv[], Options &options)
00587 {
00588 const char *optstring = "hvud:U:P:Dc:s:o:C:H:S:";
00589
00590 const struct option long_options[] = {
00591 { "configfile", required_argument, 0, 'C' },
00592 { "data", required_argument, 0, 'd' },
00593 { "default", no_argument, 0, 'D' },
00594 { "password", required_argument, 0, 'P' },
00595 { "character", required_argument, 0, 'c' },
00596 { "help", no_argument, 0, 'h' },
00597 { "homedir", required_argument, 0, 'S' },
00598 { "updatehost", required_argument, 0, 'H' },
00599 { "port", required_argument, 0, 'o' },
00600 { "server", required_argument, 0, 's' },
00601 { "skipupdate", no_argument, 0, 'u' },
00602 { "username", required_argument, 0, 'U' },
00603 { "version", no_argument, 0, 'v' },
00604 { 0 }
00605 };
00606
00607 while (optind < argc)
00608 {
00609
00610 int result = getopt_long(argc, argv, optstring, long_options, NULL);
00611
00612 if (result == -1)
00613 break;
00614
00615 switch (result)
00616 {
00617 case 'C':
00618 options.configPath = optarg;
00619 break;
00620 case 'd':
00621 options.dataPath = optarg;
00622 break;
00623 case 'D':
00624 options.chooseDefault = true;
00625 break;
00626 default:
00627 case 'h':
00628 options.printHelp = true;
00629 break;
00630 case 'H':
00631 options.updateHost = optarg;
00632 break;
00633 case 'c':
00634 options.character = optarg;
00635 break;
00636 case 'P':
00637 options.password = optarg;
00638 break;
00639 case 's':
00640 options.serverName = optarg;
00641 break;
00642 case 'o':
00643 options.serverPort = (short)atoi(optarg);
00644 break;
00645 case 'u':
00646 options.skipUpdate = true;
00647 break;
00648 case 'U':
00649 options.username = optarg;
00650 break;
00651 case 'v':
00652 options.printVersion = true;
00653 break;
00654 case 'S':
00655 homeDir = optarg;
00656 break;
00657 }
00658 }
00659 }
00660
00665 static void loadUpdates()
00666 {
00667 if (updatesDir.empty()) return;
00668 const std::string updatesFile = "/" + updatesDir + "/resources2.txt";
00669 ResourceManager *resman = ResourceManager::getInstance();
00670 std::vector<std::string> lines = resman->loadTextFile(updatesFile);
00671
00672 for (unsigned int i = 0; i < lines.size(); ++i)
00673 {
00674 std::stringstream line(lines[i]);
00675 std::string filename;
00676 line >> filename;
00677 resman->addToSearchPath(homeDir + "/" + updatesDir + "/"
00678 + filename, false);
00679 }
00680 }
00681
00682 struct ErrorListener : public gcn::ActionListener
00683 {
00684 void action(const gcn::ActionEvent &event)
00685 {
00686 #ifdef TMWSERV_SUPPORT
00687 state = STATE_CHOOSE_SERVER;
00688 #else
00689 state = loginData.registerLogin ? STATE_REGISTER : STATE_LOGIN;
00690 #endif
00691 }
00692 } errorListener;
00693
00694 #ifdef TMWSERV_SUPPORT
00695 struct AccountListener : public gcn::ActionListener
00696 {
00697 void action(const gcn::ActionEvent &event)
00698 {
00699 state = STATE_CHAR_SELECT;
00700 }
00701 } accountListener;
00702
00703 struct LoginListener : public gcn::ActionListener
00704 {
00705 void action(const gcn::ActionEvent &event)
00706 {
00707 state = STATE_LOGIN;
00708 }
00709 } loginListener;
00710 #endif
00711
00712 }
00713
00714
00715 #ifdef TMWSERV_SUPPORT
00716 static void accountLogin(LoginData *loginData)
00717 #else
00718 static void accountLogin(Network *network, LoginData *loginData)
00719 #endif
00720 {
00721 #ifdef EATHENA_SUPPORT
00722 logger->log("Trying to connect to account server...");
00723 #endif
00724 logger->log("Username is %s", loginData->username.c_str());
00725 #ifdef EATHENA_SUPPORT
00726 network->connect(loginData->hostname, loginData->port);
00727
00728 #endif
00729
00730 #ifdef TMWSERV_SUPPORT
00731 Net::getCharHandler()->setCharInfo(&charInfo);
00732 #endif
00733
00734
00735 if (loginData->registerLogin) {
00736 Net::getLoginHandler()->registerAccount(loginData);
00737 } else {
00738 Net::getLoginHandler()->loginAccount(loginData);
00739 }
00740
00741
00742 loginData->password = "";
00743
00744
00745
00746 if (loginData->remember)
00747 {
00748 config.setValue("host", loginData->hostname);
00749 config.setValue("username", loginData->username);
00750 }
00751 config.setValue("remember", loginData->remember);
00752 }
00753
00754 #ifdef EATHENA_SUPPORT
00755
00756 static void positionDialog(Window *dialog, int screenWidth, int screenHeight)
00757 {
00758 dialog->setPosition(
00759 (screenWidth - dialog->getWidth()) / 2,
00760 (screenHeight - dialog->getHeight()) / 2);
00761 }
00762
00763 static void charLogin(Network *network, LoginData *loginData)
00764 {
00765 logger->log("Trying to connect to char server...");
00766 network->connect(loginData->hostname, loginData->port);
00767
00768 Net::getCharHandler()->setCharInfo(&charInfo);
00769
00770
00771 Net::getCharHandler()->connect(loginData);
00772 }
00773
00774 static void mapLogin(Network *network, LoginData *loginData)
00775 {
00776 logger->log("Memorizing selected character %s",
00777 player_node->getName().c_str());
00778 config.setValue("lastCharacter", player_node->getName());
00779
00780 logger->log("Trying to connect to map server...");
00781 logger->log("Map: %s", map_path.c_str());
00782
00783
00784 network->connect(loginData->hostname, loginData->port);
00785
00786 Net::getMapHandler()->connect(loginData);
00787 }
00788
00789 #else
00790
00791 static void accountRegister(LoginData *loginData)
00792 {
00793 logger->log("Username is %s", loginData->username.c_str());
00794
00795 Net::getCharHandler()->setCharInfo(&charInfo);
00796 Net::getLoginHandler()->registerAccount(loginData);
00797 }
00798
00799 static void switchCharacter(std::string *passToken)
00800 {
00801 Net::getLogoutHandler()->reset();
00802 Net::getLogoutHandler()->setScenario(LOGOUT_SWITCH_CHARACTER, passToken);
00803 }
00804
00805 static void switchAccountServer()
00806 {
00807 Net::getLogoutHandler()->reset();
00808 Net::getLogoutHandler()->setScenario(LOGOUT_SWITCH_LOGIN);
00809 }
00810
00811 static void logoutThenExit()
00812 {
00813 Net::getLogoutHandler()->reset();
00814 Net::getLogoutHandler()->setScenario(LOGOUT_EXIT);
00815 }
00816
00817 static void reconnectAccount(const std::string &passToken)
00818 {
00819 Net::getCharHandler()->setCharInfo(&charInfo);
00820
00821 Net::AccountServer::reconnectAccount(accountServerConnection, passToken);
00822 }
00823
00824 #endif
00825
00826
00827 extern "C" char const *_nl_locale_name_default(void);
00828
00829 static void initInternationalization()
00830 {
00831 #if ENABLE_NLS
00832 #ifdef WIN32
00833 putenv(("LANG=" + std::string(_nl_locale_name_default())).c_str());
00834
00835 bindtextdomain("tmw", "translations/");
00836 #else
00837 bindtextdomain("tmw", LOCALEDIR);
00838 #endif
00839 setlocale(LC_MESSAGES, "");
00840 bind_textdomain_codeset("tmw", "UTF-8");
00841 textdomain("tmw");
00842 #endif
00843 }
00844
00845 static void xmlNullLogger(void *ctx, const char *msg, ...)
00846 {
00847
00848 }
00849
00850
00851
00852 static void initXML()
00853 {
00854 xmlInitParser();
00855 LIBXML_TEST_VERSION;
00856
00857
00858 xmlSetGenericErrorFunc(NULL, xmlNullLogger);
00859 }
00860
00862 int main(int argc, char *argv[])
00863 {
00864
00865 Options options;
00866 parseOptions(argc, argv, options);
00867 if (options.printHelp)
00868 {
00869 printHelp();
00870 return 0;
00871 }
00872 else if (options.printVersion)
00873 {
00874 printVersion();
00875 return 0;
00876 }
00877
00878 initInternationalization();
00879
00880
00881 PHYSFS_init(argv[0]);
00882
00883 initXML();
00884
00885
00886 branding.init("data/branding.xml");
00887
00888 initHomeDir();
00889
00890
00891 logger = new Logger;
00892 logger->setLogFile(homeDir + std::string("/tmw.log"));
00893
00894
00895 logger->log("The Mana World %s", FULL_VERSION);
00896
00897 initConfiguration(options);
00898 logger->setLogToStandardOut(config.getValue("logToStandardOut", 0));
00899
00900 initEngine(options);
00901
00902
00903 guiPalette = new Palette;
00904
00905 Game *game = NULL;
00906 Window *currentDialog = NULL;
00907 #ifdef TMWSERV_SUPPORT
00908 QuitDialog* quitDialog = NULL;
00909 #endif
00910 setupWindow = new Setup;
00911
00912 gcn::Container *top = static_cast<gcn::Container*>(gui->getTop());
00913 Desktop *desktop = new Desktop;
00914 top->add(desktop);
00915 ProgressBar *progressBar = new ProgressBar(0.0f, 100, 20, 168, 116, 31);
00916 gcn::Label *progressLabel = new Label;
00917 top->add(progressBar, 5, top->getHeight() - 5 - progressBar->getHeight());
00918 top->add(progressLabel, 15 + progressBar->getWidth(),
00919 progressBar->getY() + 4);
00920 progressBar->setVisible(false);
00921 gcn::Button *setupButton = new Button(_("Setup"), "Setup", &listener);
00922 setupButton->setPosition(top->getWidth() - setupButton->getWidth() - 3, 3);
00923 top->add(setupButton);
00924
00925 sound.playMusic(branding.getValue("loginMusic", "Login.ogg"));
00926
00927
00928 loginData.hostname = options.serverName;
00929 loginData.port = options.serverPort;
00930 loginData.username = options.username;
00931 loginData.password = options.password;
00932 loginData.remember = config.getValue("remember", 0);
00933 loginData.registerLogin = false;
00934
00935 if (loginData.hostname.empty()) {
00936 loginData.hostname = branding.getValue("defaultServer",
00937 "server.themanaworld.org").c_str();
00938 }
00939 if (options.serverPort == 0) {
00940 loginData.port = (short) branding.getValue("defaultPort", 9601);
00941 }
00942 if (loginData.username.empty() && loginData.remember) {
00943 loginData.username = config.getValue("username", "");
00944 }
00945
00946 #ifdef TMWSERV_SUPPORT
00947 Net::initialize();
00948 new TmwServ::GeneralHandler;
00949 #else
00950 Network *network = new Network;
00951 network->registerHandler(new EAthena::GeneralHandler);
00952 #endif
00953
00954 Net::getGeneralHandler()->load();
00955
00956 int screenWidth = (int) config.getValue("screenwidth", defaultScreenWidth);
00957 int screenHeight = static_cast<int>(config.getValue("screenheight",
00958 defaultScreenHeight));
00959
00960 desktop->setSize(screenWidth, screenHeight);
00961
00962 unsigned int oldstate = !state;
00963
00964 SDL_Event event;
00965
00966 while (state != STATE_EXIT)
00967 {
00968
00969 while (SDL_PollEvent(&event))
00970 {
00971 switch (event.type)
00972 {
00973 case SDL_QUIT:
00974 state = STATE_EXIT;
00975 break;
00976
00977 case SDL_KEYDOWN:
00978 if (event.key.keysym.sym == SDLK_ESCAPE)
00979 {
00980 #ifdef TMWSERV_SUPPORT
00981 if (!quitDialog)
00982 quitDialog = new QuitDialog(NULL, &quitDialog);
00983 else
00984 quitDialog->requestMoveToTop();
00985 #else
00986 state = STATE_EXIT;
00987 #endif
00988 }
00989 break;
00990 }
00991
00992 guiInput->pushInput(event);
00993 }
00994
00995 Net::getGeneralHandler()->flushNetwork();
00996 gui->logic();
00997
00998 Net::getGeneralHandler()->tick();
00999
01000 if (progressBar->isVisible())
01001 {
01002 progressBar->setProgress(progressBar->getProgress() + 0.005f);
01003 if (progressBar->getProgress() == 1.0f)
01004 progressBar->setProgress(0.0f);
01005 }
01006
01007 gui->draw();
01008 graphics->updateScreen();
01009
01010 #ifdef TMWSERV_SUPPORT
01011
01012 if (state == STATE_CONNECT_ACCOUNT &&
01013 accountServerConnection->isConnected())
01014 {
01015 if (options.skipUpdate) {
01016 state = STATE_LOADDATA;
01017 } else {
01018 state = STATE_UPDATE;
01019 }
01020 }
01021 else if (state == STATE_CONNECT_GAME &&
01022 gameServerConnection->isConnected() &&
01023 chatServerConnection->isConnected())
01024 {
01025 accountServerConnection->disconnect();
01026
01027
01028 state = STATE_GAME;
01029 }
01030 else if (state == STATE_RECONNECT_ACCOUNT &&
01031 accountServerConnection->isConnected())
01032 {
01033 reconnectAccount(token);
01034 state = STATE_WAIT;
01035 }
01036
01037 if (state != oldstate)
01038 {
01039
01040 if (oldstate == STATE_UPDATE)
01041 {
01042 loadUpdates();
01043 }
01044
01045 oldstate = state;
01046
01047
01048 if (currentDialog) {
01049 delete currentDialog;
01050 currentDialog = NULL;
01051 }
01052
01053
01054 if (quitDialog) {
01055 quitDialog->scheduleDelete();
01056 }
01057
01058 switch (state) {
01059 case STATE_CHOOSE_SERVER:
01060 logger->log("State: CHOOSE_SERVER");
01061
01062
01063
01064
01065 if (options.serverName.empty() && options.serverPort == 0) {
01066 currentDialog = new ServerDialog(&loginData);
01067 } else {
01068 state = STATE_CONNECT_ACCOUNT;
01069
01070
01071
01072 options.serverName.clear();
01073 options.serverPort = 0;
01074 }
01075 break;
01076
01077 case STATE_CONNECT_ACCOUNT:
01078 logger->log("State: CONNECT_ACCOUNT");
01079 logger->log("Trying to connect to account server...");
01080 accountServerConnection->connect(loginData.hostname,
01081 loginData.port);
01082 currentDialog = new ConnectionDialog(
01083 STATE_SWITCH_ACCOUNTSERVER_ATTEMPT);
01084 break;
01085
01086 case STATE_UPDATE:
01087
01088 if (!options.updateHost.empty())
01089 updateHost = options.updateHost;
01090 else
01091 updateHost = loginData.updateHost;
01092
01093 setUpdatesDir();
01094 logger->log("State: UPDATE");
01095 currentDialog = new UpdaterWindow(updateHost,
01096 homeDir + "/" + updatesDir);
01097 break;
01098
01099 case STATE_LOGIN:
01100 logger->log("State: LOGIN");
01101 if (options.username.empty()
01102 || options.password.empty()) {
01103 currentDialog = new LoginDialog(&loginData);
01104 } else {
01105 state = STATE_LOGIN_ATTEMPT;
01106
01107
01108 options.password.clear();
01109 }
01110 break;
01111
01112 case STATE_LOADDATA:
01113 logger->log("State: LOADDATA");
01114
01115
01116 ResourceManager::getInstance()->searchAndAddArchives(
01117 "customdata/",
01118 "zip",
01119 false);
01120
01121
01122 ColorDB::load();
01123 ItemDB::load();
01124 MonsterDB::load();
01125 NPCDB::load();
01126 EmoteDB::load();
01127 Units::loadUnits();
01128
01129 desktop->reloadWallpaper();
01130
01131 state = STATE_LOGIN;
01132 break;
01133
01134 case STATE_LOGIN_ATTEMPT:
01135 accountLogin(&loginData);
01136 break;
01137
01138 case STATE_LOGIN_ERROR:
01139 logger->log("State: LOGIN ERROR");
01140 currentDialog = new OkDialog("Error ", errorMessage);
01141 currentDialog->addActionListener(&loginListener);
01142 currentDialog = NULL;
01143 break;
01144
01145 case STATE_SWITCH_ACCOUNTSERVER:
01146 logger->log("State: SWITCH_ACCOUNTSERVER");
01147
01148 gameServerConnection->disconnect();
01149 chatServerConnection->disconnect();
01150 accountServerConnection->disconnect();
01151
01152 state = STATE_CHOOSE_SERVER;
01153 break;
01154
01155 case STATE_SWITCH_ACCOUNTSERVER_ATTEMPT:
01156 logger->log("State: SWITCH_ACCOUNTSERVER_ATTEMPT");
01157 switchAccountServer();
01158
01159 state = STATE_SWITCH_ACCOUNTSERVER;
01160 break;
01161
01162 case STATE_REGISTER:
01163 logger->log("State: REGISTER");
01164 currentDialog = new RegisterDialog(&loginData);
01165 break;
01166
01167 case STATE_REGISTER_ATTEMPT:
01168 accountRegister(&loginData);
01169 break;
01170
01171 case STATE_CHAR_SELECT:
01172 logger->log("State: CHAR_SELECT");
01173 currentDialog =
01174 new CharSelectDialog(&charInfo, &loginData);
01175
01176 if (((CharSelectDialog*) currentDialog)->
01177 selectByName(options.character)) {
01178 ((CharSelectDialog*) currentDialog)->action(
01179 gcn::ActionEvent(NULL, "ok"));
01180 } else {
01181 ((CharSelectDialog*) currentDialog)->selectByName(
01182 config.getValue("lastCharacter", ""));
01183 }
01184
01185 break;
01186
01187 case STATE_CHANGEEMAIL_ATTEMPT:
01188 logger->log("State: CHANGE EMAIL ATTEMPT");
01189 Net::getLoginHandler()->changeEmail(loginData.newEmail);
01190 break;
01191
01192 case STATE_CHANGEEMAIL:
01193 logger->log("State: CHANGE EMAIL");
01194 currentDialog = new OkDialog("Email Address change",
01195 "Email Address changed successfully!");
01196 currentDialog->addActionListener(&accountListener);
01197 currentDialog = NULL;
01198 loginData.email = loginData.newEmail;
01199 loginData.newEmail = "";
01200 break;
01201
01202 case STATE_CHANGEPASSWORD_ATTEMPT:
01203 logger->log("State: CHANGE PASSWORD ATTEMPT");
01204 Net::getLoginHandler()->changePassword(loginData.username,
01205 loginData.password,
01206 loginData.newPassword);
01207 break;
01208
01209 case STATE_CHANGEPASSWORD:
01210 logger->log("State: CHANGE PASSWORD");
01211 currentDialog = new OkDialog("Password change",
01212 "Password changed successfully!");
01213 currentDialog->addActionListener(&accountListener);
01214 currentDialog = NULL;
01215 loginData.password = loginData.newPassword;
01216 loginData.newPassword = "";
01217 break;
01218
01219 case STATE_UNREGISTER_ATTEMPT:
01220 logger->log("State: UNREGISTER ATTEMPT");
01221 Net::getLoginHandler()->unregisterAccount(
01222 loginData.username, loginData.password);
01223 break;
01224
01225 case STATE_UNREGISTER:
01226 logger->log("State: UNREGISTER");
01227 accountServerConnection->disconnect();
01228 currentDialog = new OkDialog("Unregister successful",
01229 "Farewell, come back any time ....");
01230 loginData.clear();
01231
01232 currentDialog->addActionListener(&errorListener);
01233 currentDialog = NULL;
01234 break;
01235
01236 case STATE_ACCOUNTCHANGE_ERROR:
01237 logger->log("State: ACCOUNT CHANGE ERROR");
01238 currentDialog = new OkDialog("Error ", errorMessage);
01239 currentDialog->addActionListener(&accountListener);
01240 currentDialog = NULL;
01241 break;
01242
01243
01244 case STATE_ERROR:
01245 logger->log("State: ERROR");
01246 currentDialog = new OkDialog("Error", errorMessage);
01247 currentDialog->addActionListener(&errorListener);
01248 currentDialog = NULL;
01249 gameServerConnection->disconnect();
01250 chatServerConnection->disconnect();
01251 Net::clearHandlers();
01252 break;
01253
01254 case STATE_CONNECT_GAME:
01255 logger->log("State: CONNECT_GAME");
01256 currentDialog = new ConnectionDialog(STATE_SWITCH_ACCOUNTSERVER_ATTEMPT);
01257 break;
01258
01259 case STATE_GAME:
01260 logger->log("Memorizing selected character %s",
01261 player_node->getName().c_str());
01262 config.setValue("lastCharacter", player_node->getName());
01263
01264 Net::GameServer::connect(gameServerConnection, token);
01265 Net::ChatServer::connect(chatServerConnection, token);
01266 sound.fadeOutMusic(1000);
01267
01268 delete setupButton;
01269 delete desktop;
01270 setupButton = NULL;
01271 desktop = NULL;
01272
01273 currentDialog = NULL;
01274
01275 logger->log("State: GAME");
01276 game = new Game;
01277 game->logic();
01278 delete game;
01279
01280 state = STATE_EXIT;
01281
01282 logoutThenExit();
01283 Net::getGeneralHandler()->unload();
01284
01285 break;
01286
01287 case STATE_SWITCH_CHARACTER:
01288 logger->log("State: SWITCH_CHARACTER");
01289 switchCharacter(&token);
01290 break;
01291
01292 case STATE_RECONNECT_ACCOUNT:
01293 logger->log("State: RECONNECT_ACCOUNT");
01294
01295
01296 gameServerConnection->disconnect();
01297 chatServerConnection->disconnect();
01298
01299 accountServerConnection->connect(
01300 loginData.hostname,
01301 loginData.port);
01302 break;
01303
01304 case STATE_WAIT:
01305 break;
01306
01307 default:
01308 state = STATE_FORCE_QUIT;
01309 break;
01310 }
01311 }
01312
01313 #else // no TMWSERV_SUPPORT
01314
01315 if (state != oldstate)
01316 {
01317 switch (oldstate)
01318 {
01319 case STATE_UPDATE:
01320 loadUpdates();
01321 break;
01322
01323
01324 case STATE_LOADDATA:
01325 break;
01326
01327 case STATE_ACCOUNT:
01328 case STATE_CHAR_CONNECT:
01329 case STATE_CONNECTING:
01330 progressBar->setVisible(false);
01331 progressLabel->setCaption("");
01332 break;
01333
01334 default:
01335 network->disconnect();
01336 break;
01337 }
01338
01339 oldstate = state;
01340
01341 if (currentDialog && state != STATE_ACCOUNT &&
01342 state != STATE_CHAR_CONNECT)
01343 {
01344 delete currentDialog;
01345 currentDialog = NULL;
01346 }
01347
01348 switch (state)
01349 {
01350 case STATE_LOADDATA:
01351 logger->log("State: LOADDATA");
01352
01353
01354 ResourceManager::getInstance()->searchAndAddArchives(
01355 "customdata/",
01356 "zip",
01357 false);
01358
01359
01360 ColorDB::load();
01361 ItemDB::load();
01362 MonsterDB::load();
01363 NPCDB::load();
01364 EmoteDB::load();
01365 StatusEffect::load();
01366 Being::load();
01367
01368
01369 Units::loadUnits();
01370
01371 desktop->reloadWallpaper();
01372
01373 state = STATE_CHAR_CONNECT;
01374 break;
01375
01376 case STATE_LOGIN:
01377 logger->log("State: LOGIN");
01378
01379 if (!loginData.password.empty())
01380 {
01381 loginData.registerLogin = false;
01382 state = STATE_ACCOUNT;
01383 }
01384 else
01385 {
01386 currentDialog = new LoginDialog(&loginData);
01387 positionDialog(currentDialog, screenWidth,
01388 screenHeight);
01389 }
01390 break;
01391
01392 case STATE_REGISTER:
01393 logger->log("State: REGISTER");
01394 currentDialog = new RegisterDialog(&loginData);
01395 positionDialog(currentDialog, screenWidth, screenHeight);
01396 break;
01397
01398 case STATE_CHAR_SERVER:
01399 logger->log("State: CHAR_SERVER");
01400
01401 if (n_server == 1)
01402 {
01403 SERVER_INFO *si = *server_info;
01404 loginData.hostname = ipToString(si->address);
01405 loginData.port = si->port;
01406 loginData.updateHost = si->updateHost;
01407 state = STATE_UPDATE;
01408 }
01409 else
01410 {
01411 int nextState = STATE_UPDATE;
01412 currentDialog = new ServerSelectDialog(&loginData,
01413 nextState);
01414 positionDialog(currentDialog, screenWidth,
01415 screenHeight);
01416 if (options.chooseDefault
01417 || !options.character.empty())
01418 {
01419 ((ServerSelectDialog*) currentDialog)->action(
01420 gcn::ActionEvent(NULL, "ok"));
01421 }
01422 }
01423 break;
01424 case STATE_CHAR_SELECT:
01425 logger->log("State: CHAR_SELECT");
01426 currentDialog = new CharSelectDialog(&charInfo,
01427 loginData.sex);
01428 positionDialog(currentDialog, screenWidth, screenHeight);
01429
01430 if (((CharSelectDialog*) currentDialog)->
01431 selectByName(options.character))
01432 options.chooseDefault = true;
01433 else
01434 ((CharSelectDialog*) currentDialog)->selectByName(
01435 config.getValue("lastCharacter", ""));
01436
01437 if (options.chooseDefault)
01438 ((CharSelectDialog*) currentDialog)->action(
01439 gcn::ActionEvent(NULL, "ok"));
01440
01441 break;
01442
01443 case STATE_GAME:
01444 sound.fadeOutMusic(1000);
01445
01446 delete progressBar;
01447 delete progressLabel;
01448 delete setupButton;
01449 delete desktop;
01450 progressBar = NULL;
01451 progressLabel = NULL;
01452 currentDialog = NULL;
01453 setupButton = NULL;
01454 desktop = NULL;
01455
01456 logger->log("State: GAME");
01457 game = new Game;
01458 game->logic();
01459 delete game;
01460 state = STATE_EXIT;
01461 break;
01462
01463 case STATE_UPDATE:
01464 if (options.skipUpdate)
01465 {
01466 state = STATE_LOADDATA;
01467 }
01468 else
01469 {
01470
01471 if (!options.updateHost.empty())
01472 updateHost = options.updateHost;
01473 else
01474 updateHost = loginData.updateHost;
01475
01476 setUpdatesDir();
01477 logger->log("State: UPDATE");
01478
01479 currentDialog = new UpdaterWindow(updateHost,
01480 homeDir + "/" + updatesDir);
01481 positionDialog(currentDialog, screenWidth,
01482 screenHeight);
01483 }
01484 break;
01485
01486 case STATE_ERROR:
01487 logger->log("State: ERROR");
01488 currentDialog = new OkDialog(_("Error"), errorMessage);
01489 positionDialog(currentDialog, screenWidth, screenHeight);
01490 currentDialog->addActionListener(&errorListener);
01491 currentDialog = NULL;
01492 network->disconnect();
01493 network->clearHandlers();
01494 break;
01495
01496 case STATE_CONNECTING:
01497 logger->log("State: CONNECTING");
01498 progressBar->setVisible(true);
01499 progressLabel->setCaption(
01500 _("Connecting to map server..."));
01501 progressLabel->adjustSize();
01502 mapLogin(network, &loginData);
01503 break;
01504
01505 case STATE_CHAR_CONNECT:
01506 progressBar->setVisible(true);
01507 progressLabel->setCaption(
01508 _("Connecting to character server..."));
01509 progressLabel->adjustSize();
01510 charLogin(network, &loginData);
01511 break;
01512
01513 case STATE_ACCOUNT:
01514 progressBar->setVisible(true);
01515 progressLabel->setCaption(
01516 _("Connecting to account server..."));
01517 progressLabel->adjustSize();
01518 accountLogin(network, &loginData);
01519 break;
01520
01521 default:
01522 state = STATE_EXIT;
01523 break;
01524 }
01525 }
01526 #endif
01527
01528
01529
01530
01531
01532 usleep(25000);
01533 }
01534
01535 delete guiPalette;
01536
01537 logger->log("Quitting");
01538 exitEngine();
01539 PHYSFS_deinit();
01540 delete logger;
01541
01542 return 0;
01543 }
01544
01545 void SetupListener::action(const gcn::ActionEvent &event)
01546 {
01547 Window *window = NULL;
01548
01549 if (event.getId() == "Setup")
01550 window = setupWindow;
01551
01552 if (window)
01553 {
01554 window->setVisible(!window->isVisible());
01555 if (window->isVisible())
01556 window->requestMoveToTop();
01557 }
01558 }