summaryrefslogblamecommitdiff
path: root/src/dalstorage.cpp
blob: db435b5de1b409f1d91f531ee8694487702cb273 (plain) (tree)
1
2
3
4
5
6
7
8
9





                                                  


                                                                               
  



                                                                               
  


                                                                               



        

                     

                  





















                                                               













































































                                                                              









                        
                        
                                                             
 

                                                                       

 



              
                         
           
 








                                                   
 






                                                   

 















                                                     
 


















                                                                     

 





                                                   
 


















                                                                      
         




















































                                                                         
     

 





                                                          
 



                                       
 
                        

         































                                                                                                                                           

     


                      
/*
 *  The Mana World Server
 *  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 <functional>
#include <sstream>

#include "dalstorage.h"


namespace
{


/**
 * Functor used for the search of an Account by name.
 */
template <typename T>
struct account_name_equals_to
    : public std::binary_function<T, std::string, bool>
{
    bool
    operator()(const T& account, const std::string& name) const
    {
        return account->getName() == name;
    }
};


/* Values for user level could be:
 *  0: Normal user
 *  1: Moderator (has medium level rights)
 *  2: Administrator (can do basically anything)
 */
const char sqlAccountTable[] =
  "create table tmw_accounts ("
    "id        int          unique primary key not null,"
    "username  varchar(32)  not null,"
    "password  varchar(32)  not null,"
    "email     varchar(128) not null,"
    "level     int          not null,"  // User level (normal, admin, etc.)
    "banned    int          not null"   // The UNIX time of unban (0 default)
  ");";

/* Note: The stats will need to be thought over, as we'll be implementing a
 *  much more elaborate skill based system. We should probably have a separate
 *  table for storing the skill levels.
 *
 * Gender is 0 for male, 1 for female.
 */
const char sqlCharacterTable[] =
  "create table tmw_characters ("
    "id        int          unique primary key not null,"
    "user_id   int          not null,"
    "name      varchar(32)  not null,"
    "gender    int          not null,"  // Player information
    "level     int          not null,"
    "money     int          not null,"
    "x         int          not null,"  // Location
    "y         int          not null,"
    "map       text         not null,"
    "str       int          not null,"  // Stats
    "agi       int          not null,"
    "vit       int          not null,"
    "int       int          not null,"
    "dex       int          not null,"
    "luck      int          not null,"
    "foreign key(user_id) references tmw_accounts(id)"
  ");";

/*
 * All items in the game world are stored in this table.
 */
const char sqlItemTable[] =
  "create table tmw_items ("
    "id        int          unique primary key not null,"
    "amount    int          not null,"  // Items of same kind can stack
    "type      int          not null,"  // Type as defined in item database
    "state     text"                    // Optional item state saved by script
  ");";

/*
 * Items on the ground in the game world.
 */
const char sqlWorldItemTable[] =
  "create table tmw_world_items ("
    "id        int          not null,"
    "map       text,"
    "x         int          not null,"  // Location of item on map
    "y         int          not null,"
    "deathtime int          not null,"  // Time to die (UNIX time)
    "primary key(id, map),"
    "foreign key(id) references tmw_items(id)"
  ");";

/*
 * Character Inventory
 */
const char sqlInventoryTable[] =
  "create table tmw_inventory ("
    "id        int          primary key not null," // Item ID
    "owner_id  int          not null," // Owner character ID
    "foreign key(id) references tmw_items(id),"
    "foreign key(owner_id) references tmw_characters(id)"
  ");";


} // anonymous namespace


namespace tmwserv
{


/**
 * Constructor.
 */
DALStorage::DALStorage()
        : mDb(dal::DataProviderFactory::createDataProvider())
{
    // the connection to the database will be made on the first request
    // to the database.
}


/**
 * Destructor.
 */
DALStorage::~DALStorage()
    throw()
{
    mDb->disconnect();

    // clean up loaded accounts.
    for (Accounts::iterator it = mAccounts.begin();
         it != mAccounts.end();
         ++it)
    {
        delete (*it);
    }

    // clean up loaded characters.
    for (Beings::iterator it = mCharacters.begin();
         it != mCharacters.end();
         ++it)
    {
        delete (*it);
    }
}


/**
 * Save changes to the database permanently.
 */
void
DALStorage::flush(void)
{
    // this feature is not currently provided by DAL.
}


/**
 * Get the number of Accounts saved in database.
 */
unsigned int
DALStorage::getAccountCount(void)
{
    // connect to the database (if not connected yet).
    connect();

    using namespace dal;

    try {
        // query the database.
        const std::string sql = "select count(*) from tmw_accounts;";
        const RecordSet& rs = mDb->execSql(sql);

        // convert the result into a number.
        std::istringstream s(rs(0, 0));
        unsigned int value;
        s >> value;

        return value;
    } catch (const DbSqlQueryExecFailure& f) {
        std::cout << "Get accounts count failed :'(" << std::endl;
    }
}


/**
 * Get an account by user name.
 */
Account*
DALStorage::getAccount(const std::string& userName)
{
    // connect to the database (if not connected yet).
    connect();

    // look for the account in the list first.
    Accounts::iterator it =
        std::find_if(
            mAccounts.begin(),
            mAccounts.end(),
            std::bind2nd(account_name_equals_to<Account*>(), userName)
        );

    if (it != mAccounts.end()) {
        return (*it);
    }


    using namespace dal;

    // the account was not in the list, look for it in the database.
    try {
        std::string sql("select * from tmw_accounts where username = '");
        sql += userName;
        sql += "';";
        const RecordSet& accountInfo = mDb->execSql(sql);

        // if the account is not even in the database then
        // we have no choice but to return nothing.
        if (accountInfo.isEmpty()) {
            return NULL;
        }

        // create an Account instance
        // and initialize it with information about the user.
        Account* account = new Account();
        account->setName(accountInfo(0, 1));
        account->setPassword(accountInfo(0, 2));
        account->setEmail(accountInfo(0, 3));

        // add the new Account to the list.
        mAccounts.push_back(account);

        // load the characters associated with the account.
        sql = "select * from tmw_characters where id = '";
        sql += accountInfo(0, 0);
        sql += "';";
        const RecordSet& charInfo = mDb->execSql(sql);

        if (!charInfo.isEmpty()) {
            Beings beings;

            for (unsigned int i = 0; i < charInfo.rows(); ++i) {
                Being* being =
                    new Being(charInfo(i, 2),   // name
                              charInfo(i, 3),   // gender
                              charInfo(i, 4),   // level
                              charInfo(i, 5),   // money
                              charInfo(i, 9),   // strength
                              charInfo(i, 10),  // agility
                              charInfo(i, 11),  // vitality
                              charInfo(i, 13),  // dexterity
                              charInfo(i, 14)); // luck

                mCharacters.push_back(being);
                beings.push_back(being);
            }

            account->setCharacters(beings);
        }

        return account;
    }
    catch (const DbSqlQueryExecFailure& e) {
        return NULL; // TODO: Throw exception here
    }
}


/**
 * Connect to the database and initialize it if necessary.
 */
void
DALStorage::connect(void)
{
    // do nothing if already connected.
    if (mDb->isConnected()) {
        return;
    }

    using namespace dal;

    try {
        // open a connection to the database.
        // TODO: get the database name, the user name and the user password
        // from a configuration manager.
        mDb->connect("tmw", "", "");

        bool doInitDb = true;

        // TODO: check the existence of the tables first and
        // create only those that are missing.

        if (doInitDb) {
            // create the tables.
            mDb->execSql(sqlAccountTable);
            mDb->execSql(sqlCharacterTable);
            mDb->execSql(sqlItemTable);
            mDb->execSql(sqlWorldItemTable);
            mDb->execSql(sqlInventoryTable);

            // Example data :)
            mDb->execSql("insert into tmw_accounts values (0, 'nym', 'tHiSiSHaShEd', 'nym@test', 1, 0);");
            mDb->execSql("insert into tmw_accounts values (1, 'Bjorn', 'tHiSiSHaShEd', 'bjorn@test', 1, 0);");
            mDb->execSql("insert into tmw_accounts values (2, 'Usiu', 'tHiSiSHaShEd', 'usiu@test', 1, 0);");
            mDb->execSql("insert into tmw_accounts values (3, 'ElvenProgrammer', 'tHiSiSHaShEd', 'elven@test', 1, 0);");
            mDb->execSql("insert into tmw_characters values (0, 0, 'Nym the Great', 0, 99, 1000000, 0, 0, 'main.map', 1, 2, 3, 4, 5, 6);");
        }
    }
    catch (const DbConnectionFailure& e) {
        std::cout << "unable to connect to the database: "
                  << e.what() << std::endl;
    }
    catch (const DbSqlQueryExecFailure& e) {
        std::cout << e.what() << std::endl;
    }
}


} // namespace tmwserv