summaryrefslogblamecommitdiff
path: root/src/main.cpp
blob: aa7e6b19adda60fdaaa611c9d8f3e30b2a8a48fd (plain) (tree)
1
  


















                                                                             
        
   

                 


                            
                          
                             
                  
                     
                                      
                         
 
                   
                 
                      
                   
                              
                          
                      
                
                       
                 
                       
      
                      
 





                     
 
                


                                         
                                         
 

                                             
                   
 

                            



                         
                          
                     
                                             
                       
                              
 
            
           
 

                                                                  
                                                          
 

                                                  
   



                                                                 



                          



                    




















                                                          


                              

                  
                     





                                                        
 
                         

                                                                               
                            
                    

                                                                               
     
                                       
                                       
 
                                               


                                                                          
 


                                                                       

                              
                                                                               

                
      
 













                                                                       
                                   
                                                              
 
                                                                             
                        
                      


                                                 

                                              
                          
                                                            
                                          
                              
                                                                     

              
                            
                                    
         
     



                                
 
                                              
 




                      
 
                                 
 
                                                 

                                       



                                                    

          
                                                 
                                                                    




                                                          
     
 

                                                        

                                                      
                                                                          

                                                                        
                         

                                                                           
                

     


                                                   
                                                               

          
                                                  

     
                                                 
 
                                                           
                                                 
                                               
                                                 
                                                             
                                            
                                                                      
                                               
                                                                   
                                              
                                                             
                                            
                                                                      
                                               
                                                                   
                                              
                                              
                                              
                                                             


                                           

                               
 

                                  
 

                                                             
                                       
                                                   
                                        
                                                     
                                      
                                                     
 


                                                                             
 
                                                 
                                             
 
                            
                                              
 
                              
         

                                               
         

                                                                      



                                                                
                                        
     








                                                       


                       

                  

                     

                    
                  



                       
                                      
                  

 
                               


                                                             














                                               




                             





                                  


                           

                                             


                    






                                  


                     




















                                                                       


                               










                                                 
                     
            

                    






                                      
 







                                                                  

                                    









                                          


                                                      





                                                     


                                                  

     




                          





                                                                                  
 


                           
 

                             
 
                                             
 







                                                                        







                                 
           

                                

                                                                        
                    

                        



                                             
                           
                         
 

                  
 










                                       

                                       
         
 

                        
                                            
                                                                                


                             
                                                  


                              
                                                  


                             
                                         



                                          
                                           

                                
                           







                                                                       
                                                    

                             
                                         
                      



                              


                             
         
     
                               
                  
                    
             
 
/*
 *  The Mana World
 *  Copyright 2004 The Mana World Development Team
 *
 *  This file is part of The Mana World.
 *
 *  The Mana World 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.
 *
 *  The Mana World 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 The Mana World; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  $Id$
 */

#include "main.h"
#include "gui/char_server.h"
#include "gui/char_select.h"
#include "gui/inventory.h"
#include "gui/ok_dialog.h"
#include "gui/updatewindow.h"
#include "sound.h"
#include "graphics.h"
#include "resources/resourcemanager.h"
#include "net/protocol.h"

#include <iostream>
#include <cstdio>
#include <guichan.hpp>
#include <physfs.h>
#include <libxml/xmlversion.h>
#include <libxml/parser.h>
#include <curl/curl.h>
#include <SDL.h>
#include <SDL_thread.h>
#ifdef USE_OPENGL
#include <SDL_opengl.h>
#endif
#include <SDL_image.h>

#ifdef __USE_UNIX98
#include <sys/stat.h>
#include <pwd.h>
#include <unistd.h>
#include <errno.h>
#endif

// Account infos
int account_ID, session_ID1, session_ID2;
char sex, n_server, n_character;
SERVER_INFO *server_info;
PLAYER_INFO *char_info = new PLAYER_INFO;

Spriteset *hairset = NULL, *playerset = NULL;
Image *login_wallpaper = NULL;
Graphics *graphics;

char username[LEN_USERNAME];
char password[LEN_PASSWORD];
int map_address, char_ID;
short map_port;
char map_name[16];
unsigned char state;
unsigned char screen_mode;
char *homeDir = NULL;
int displayFlags, screenW, screenH, bitDepth;
bool useOpenGL = false;
volatile int framesToDraw = 0;

Sound sound;
Music *bgm;

Configuration config;        /**< Xml file configuration reader */
Logger *logger;              /**< Log object */
ItemManager *itemDb;          /**< Item database object */

UpdateWindow *updateWindow;  /**< Update window */

/**
 * Allows the next frame to be drawn (part of framerate limiting)
 */
Uint32 nextFrame(Uint32 interval, void *param)
{
    if (framesToDraw < 10)
    {
        framesToDraw++;
    }
    return interval;
}

/**
 * Listener used for responding to map start error dialog.
 */
class MapStartErrorListener : public gcn::ActionListener {
    void action(const std::string &eventId) {
        if (eventId == "ok") {
            state = LOGIN;
        }
    }
} mapStartErrorListener;

/**
 * Listener used for responding to init warning.
 */
class InitWarningListener : public gcn::ActionListener {
    void action(const std::string &eventId) {
        if (eventId == "ok") {
            state = LOGIN;
        }
    }
} initWarningListener;

/**
 * Do all initialization stuff
 */
void init_engine()
{
    // Initialize SDL
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
        std::cerr << "Could not initialize SDL: " <<
            SDL_GetError() << std::endl;
        exit(1);
    }
    atexit(SDL_Quit);

    SDL_EnableUNICODE(1);
    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);

    homeDir = new char[256];
#ifndef __USE_UNIX98
    // In Windows and other systems we currently store data next to executable.
    strcpy(homeDir, "");
#else
    // In UNIX we store data in ~/.tmw/
    passwd *pass = getpwuid(geteuid());

    if (pass == NULL || pass->pw_dir == NULL) {
        printf("Couldn't determine the user home directory. Exitting.\n");
        exit(1);
    }

    // Checking if homeuser/.tmw folder exists.
    sprintf(homeDir, "%s/.tmw/", pass->pw_dir);
    if ((mkdir(homeDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) &&
            (errno != EEXIST))
    {
        printf("%s can't be made, but it doesn't exist! Exitting.\n", homeDir);
        exit(1);
    }
#endif

    // Initialize logger
    logger = new Logger(std::string(homeDir) + std::string("tmw.log"));

    // Fill configuration with defaults
    config.setValue("host", "animesites.de");
    config.setValue("port", 6901);
    config.setValue("hwaccel", 0);
    config.setValue("opengl", 0);
    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://themanaworld.org/");

    // Checking if the configuration file exists... otherwise creates it with
    // default options !
    FILE *tmwFile = 0;
    char configPath[256];
    sprintf(configPath, "%sconfig.xml", homeDir);
    tmwFile = fopen(configPath, "r");

    // If we can't read it, it doesn't exist !
    if (tmwFile == NULL) {
        // We reopen the file in write mode and we create it
        tmwFile = fopen(configPath, "wt");
        if (tmwFile == NULL) {
            printf("Can't create %s. Using Defaults.\n", configPath);
        }
        else {
            fclose(tmwFile);
            config.init(configPath);
        }
    }
    else {
        fclose(tmwFile);
        config.init(configPath);
    }

    SDL_WM_SetCaption("The Mana World", NULL);

#ifdef USE_OPENGL
    useOpenGL = true;
#else
    useOpenGL = false;
#endif

    displayFlags = SDL_ANYFORMAT;

    if ((int)config.getValue("screen", 0) == 1) {
        displayFlags |= SDL_FULLSCREEN;
    }

    if (useOpenGL) {
        displayFlags |= SDL_OPENGL;
        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    }
    else {
        if ((int)config.getValue("hwaccel", 0)) {
            logger->log("Attempting to use hardware acceleration.");
            displayFlags |= SDL_HWSURFACE | SDL_DOUBLEBUF;
        }
        else {
            displayFlags |= SDL_SWSURFACE;
        }
    }

    screenW = (int)config.getValue("screenwidth", 800);
    screenH = (int)config.getValue("screenheight", 600);
    bitDepth = (int)config.getValue("colordepth", 16);

    SDL_WM_SetIcon(IMG_Load(TMW_DATADIR "data/icons/tmw-icon.png"), NULL);

    screen = SDL_SetVideoMode(screenW, screenH, bitDepth, displayFlags);
    if (screen == NULL) {
        std::cerr << "Couldn't set " << screenW << "x" << screenH << "x" <<
            bitDepth << " video mode: " << SDL_GetError() << std::endl;
        exit(1);
    }

    char videoDriverName[64];

    if (SDL_VideoDriverName(videoDriverName, 64)) {
        logger->log("Using video driver: %s", videoDriverName);
    }
    else {
        logger->log("Using video driver: unkown");
    }

    const SDL_VideoInfo *vi = SDL_GetVideoInfo();

    logger->log("Possible to create hardware surfaces: %s",
            ((vi->hw_available) ? "yes" : "no"));
    logger->log("Window manager available: %s",
            ((vi->wm_available) ? "yes" : "no"));
    logger->log("Accelerated hardware to hardware blits: %s",
            ((vi->blit_hw) ? "yes" : "no"));
    logger->log("Accelerated hardware to hardware colorkey blits: %s",
            ((vi->blit_hw_CC) ? "yes" : "no"));
    logger->log("Accelerated hardware to hardware alpha blits: %s",
            ((vi->blit_hw_A) ? "yes" : "no"));
    logger->log("Accelerated software to hardware blits: %s",
            ((vi->blit_sw) ? "yes" : "no"));
    logger->log("Accelerated software to hardware colorkey blits: %s",
            ((vi->blit_sw_CC) ? "yes" : "no"));
    logger->log("Accelerated software to hardware alpha blits: %s",
            ((vi->blit_sw_A) ? "yes" : "no"));
    logger->log("Accelerated color fills: %s",
            ((vi->blit_fill) ? "yes" : "no"));
    logger->log("Available video memory: %d", vi->video_mem);

    //vfmt Pixel format of the video device

    // Initialize item manager
    itemDb = new ItemManager();

    // Create the graphics context
    graphics = new Graphics();

    ResourceManager *resman = ResourceManager::getInstance();

    login_wallpaper = resman->getImage(
            "graphics/images/login_wallpaper.png");
    Image *playerImg = resman->getImage(
            "graphics/sprites/player_male_base.png");
    Image *hairImg = resman->getImage(
            "graphics/sprites/player_male_hair.png");

    if (!login_wallpaper) logger->error("Couldn't load login_wallpaper.png");
    if (!playerImg) logger->error("Couldn't load player_male_base.png");
    if (!hairImg) logger->error("Couldn't load player_male_hair.png");

    playerset = new Spriteset(playerImg, 64, 64);
    hairset = new Spriteset(hairImg, 40, 40);

    gui = new Gui(graphics);
    state = UPDATE; /**< Initial game state */

    // Initialize sound engine
    try {
        if (config.getValue("sound", 0) == 1) {
            sound.init();
        }
        sound.setSfxVolume((int)config.getValue("sfxVolume", 100));
        sound.setMusicVolume((int)config.getValue("musicVolume", 60));
    }
    catch (const char *err) {
        state = ERROR;
        new OkDialog("Sound Engine", err, &initWarningListener);
        logger->log("Warning: %s", err);
    }

    // Set frame counter when using fps limit
    int fpsLimit = (int)config.getValue("fpslimit", 0);
    if (fpsLimit)
    {
        if (fpsLimit < 20) fpsLimit = 20;
        if (fpsLimit > 200) fpsLimit = 200;
        SDL_AddTimer(1000 / fpsLimit, nextFrame, NULL);
    }
}

/** Clear the engine */
void exit_engine()
{
    config.write();
    delete[] homeDir;
    delete gui;
    delete graphics;
    delete itemDb;

    // Shutdown libxml
    xmlCleanupParser();

    ResourceManager::deleteInstance();
    delete logger;
}

/** Update progress callback */
int
progressCallback(void *clientp, double dltotal, double dlnow,
                 double utotal, double ulnow)
{
    // update progress bar..
    updateWindow->setProgress(dlnow / dltotal);

    // draw
    gui->logic();
    gui->draw();
    graphics->updateScreen();
    return CURLE_OK;
}

/** Get filename from URL */
const char *urlFilename(const char *url)
{
    for (int i = strlen(url); i > 0; i--)
    {
        if (url[i] == '/') {
            return &url[i+1];
        }
    }
    return NULL;
}

/** Download file from location */
int download(const char *location)
{
    if (location == NULL) {
        return false;
    }
    // find local file location
    const char *name = urlFilename(location);
    if (!name) {
        return true;
    }

    std::string dest = homeDir;
    dest += "/";
    dest += name;

    FILE *fp = NULL;
    fp = fopen(dest.c_str(), "w");
    if (!fp) {
        return false;
    }

    std::cout << "Downloading '" << location << "'";

    // init curl
    curl_global_init(CURL_GLOBAL_ALL);

    // download file
    CURL *curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, location);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, NULL);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    int ret = curl_easy_perform(curl);
    curl_easy_cleanup(curl);

    // cleanup curl
    curl_global_cleanup();

    fclose(fp);

    if (ret != 0) {
        std::cout << " failed";
    }
    std::cout << " (" << ret << ")" << std::endl;

    return (ret == CURLE_OK) ? true : false;
}

/** Check to see if a file exists */
int exists(const std::string &file)
{
    FILE *fp = NULL;
    fp = fopen(file.c_str(), "r");
    if (!fp) {
        return false;
    } else {
        fclose(fp);
        return true;
    }
}

/** Update the game data */
void update()
{
    updateWindow = new UpdateWindow();

    std::string host =
        config.getValue("updatehost", "http://themanaworld.org/");

    // Add / to host if it's not there yet.
    if (host.at(host.length() - 1) != '/')
    {
        host += "/";
    }

    std::string fullLocation = host;
    fullLocation += "resources.txt";

    std::string fullName = homeDir;
    fullName += "/";
    fullName += "resources.txt";

    updateWindow->setLabel(fullLocation);

    // get resources file
    if (!download(fullLocation.c_str())) {
        std::cout << "Error downloading" << std::endl;
        delete updateWindow;
        return;
    }

    std::cout << "Opening " << fullName << std::endl;

    std::ifstream in(fullName.c_str());
    if (!in.is_open()) {
        std::cout << "Error opening" << std::endl;
        delete updateWindow;
        return;
    }

    std::string line;

    while (!in.eof())
    {
        getline(in, line);
	
	// check for XML tag (if it is XML tag it is error)
	if (line[0] == '<') {
	    std::cout << "Error: resources.txt download error (404)" << std::endl;
	    break;
	}

        fullName = homeDir;
        fullName += "/";
        fullName += line;

        fullLocation = host;
        fullLocation += line;

        updateWindow->setLabel(fullLocation);

        if (!exists(fullName)) {
            if (!download(fullLocation.c_str())) {
                std::cout << "Failed to download " << line << std::endl;
            }
        }
        if (exists(fullName)) {
            PHYSFS_addToSearchPath(fullName.c_str(), 1);
        }
    }

    in.close();

    guiTop->remove(updateWindow);
    delete updateWindow;
}

/** Main */
int main(int argc, char *argv[])
{
    // 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();


    SDL_Event event;

    while (state != EXIT)
    {
        // Handle SDL events
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
                case SDL_QUIT:
                    state = EXIT;
                    break;
            }

            guiInput->pushInput(event);
        }

        switch (state) {
            case LOGIN:
                logger->log("State: LOGIN");
                sound.playMusic(TMW_DATADIR "data/music/Ivano(de)Jeanette.ogg");
                login();
                break;
            case CHAR_SERVER:
                logger->log("State: CHAR_SERVER");
                char_server();
                break;
            case CHAR_SELECT:
                logger->log("State: CHAR_SELECT");
                charSelect();
                break;
            case GAME:
                sound.fadeOutMusic(1000);

                login_wallpaper->decRef();
                login_wallpaper = NULL;

                logger->log("State: GAME");
                try {
                    map_start();
                    game();
                }
                catch (const char* err) {
                    state = ERROR;
                    new OkDialog("Error", err, &mapStartErrorListener);
                }
                break;
            case ERROR:
                // Redraw GUI
                login_wallpaper->draw(screen, 0, 0);
                gui->logic();
                gui->draw();
                graphics->updateScreen();
                break;
            case UPDATE:
                update();
                state = LOGIN;
                break;
            default:
                state = EXIT;
                break;
        }
    }
    logger->log("State: EXIT");
    exit_engine();
    PHYSFS_deinit();
    return 0;
}