dalstorage.cpp

Go to the documentation of this file.
00001 /*
00002  *  The Mana World Server
00003  *  Copyright 2004 The Mana World Development Team
00004  *
00005  *  This file is part of The Mana World.
00006  *
00007  *  The Mana World  is free software; you can redistribute  it and/or modify it
00008  *  under the terms of the GNU General  Public License as published by the Free
00009  *  Software Foundation; either version 2 of the License, or any later version.
00010  *
00011  *  The Mana  World is  distributed in  the hope  that it  will be  useful, but
00012  *  WITHOUT ANY WARRANTY; without even  the implied warranty of MERCHANTABILITY
00013  *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
00014  *  more details.
00015  *
00016  *  You should  have received a  copy of the  GNU General Public  License along
00017  *  with The Mana  World; if not, write to the  Free Software Foundation, Inc.,
00018  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00019  *
00020  *  $Id: dalstorage.cpp 3220 2007-03-22 00:34:04Z crush_tmw $
00021  */
00022 
00023 #include "account-server/dalstorage.hpp"
00024 
00025 #include <cassert>
00026 
00027 #include "configuration.h"
00028 #include "point.h"
00029 #include "account-server/characterdata.hpp"
00030 #include "account-server/dalstoragesql.hpp"
00031 #include "dal/dalexcept.h"
00032 #include "dal/dataproviderfactory.h"
00033 #include "utils/functors.h"
00034 #include "utils/logger.h"
00035 
00039 class account_by_name
00040 {
00041     public:
00042         account_by_name(const std::string& name)
00043             : mName(name)
00044         {}
00045 
00046         bool operator()(std::pair<unsigned, AccountPtr> const &elem) const
00047         { return elem.second->getName() == mName; }
00048 
00049     private:
00050         std::string mName; 
00051 };
00052 
00056 class character_by_id
00057 {
00058     public:
00059         character_by_id(int id)
00060             : mID(id)
00061         {}
00062 
00063         bool operator()(CharacterPtr const &elem) const
00064         { return elem->getDatabaseID() == mID; }
00065 
00066     private:
00067         int mID; 
00068 };
00069 
00070 
00074 DALStorage::DALStorage()
00075         : mDb(dal::DataProviderFactory::createDataProvider())
00076 {
00077     // the connection to the database will be made on the first request
00078     // to the database.
00079 }
00080 
00081 
00085 DALStorage::~DALStorage()
00086     throw()
00087 {
00088     if (mDb->isConnected()) {
00089         close();
00090     }
00091 
00092     // mAccounts and mCharacters contain smart pointers that will deallocate
00093     // the memory so nothing else to do here :)
00094 }
00095 
00096 
00100 void
00101 DALStorage::open(void)
00102 {
00103     // Do nothing if already connected.
00104     if (mDb->isConnected()) {
00105         return;
00106     }
00107 
00108     using namespace dal;
00109 
00110     static bool dbFileShown = false;
00111     std::string dbFile(getName());
00112     try {
00113         // open a connection to the database.
00114 #if defined (MYSQL_SUPPORT) || defined (POSTGRESQL_SUPPORT)
00115         mDb->connect(getName(), getUser(), getPassword());
00116         if (!dbFileShown)
00117         {
00118             LOG_INFO("Using " << dbFile << " as Database Name.");
00119             dbFileShown = true;
00120         }
00121 #elif defined (SQLITE_SUPPORT)
00122         // create the database file name.
00123         dbFile += ".db";
00124         mDb->connect(dbFile, "", "");
00125         if (!dbFileShown)
00126         {
00127             LOG_INFO("SQLite uses ./" << dbFile << " as DB.");
00128             dbFileShown = true;
00129         }
00130 #endif
00131 
00132         // ensure that the required tables are created.
00133         //
00134         // strategy1: find a way to obtain the list of tables from the
00135         //            underlying database and create the tables that are
00136         //            missing.
00137         //
00138         // strategy2: try to create the tables and check the exceptions
00139         //            thrown.
00140         //
00141         // comments:
00142         //     - strategy1 is easy to achieve if we are using MysQL as
00143         //       executing the request "show tables;" returns the list of
00144         //       tables. However, there is not such a query for SQLite3.
00145         //       When using SQLite3 from the interactive shell or the
00146         //       command line, the command ".tables" returns the list of
00147         //       tables but sqlite3_exec() does not validate this statement
00148         //       and fails.
00149         //       The cost of this strategy is:
00150         //           (num. tables to create + 1) queries at most and
00151         //           1 at minimum.
00152         //
00153         //     - strategy2 will work with probably most databases.
00154         //       The cost of this strategy is:
00155         //           (num. tables to create) queries.
00156 
00157         // we will stick with strategy2 for the moment as we are focusing
00158         // on SQLite.
00159 
00160         // FIXME: The tables should be checked/created at startup in order to
00161         // avoid a DbSqlQueryExecFailure assert on sqlite while registering.
00162         // Also, this would initialize connection to the database earlier in
00163         // memory.
00164 
00165         createTable(ACCOUNTS_TBL_NAME, SQL_ACCOUNTS_TABLE);
00166         createTable(CHARACTERS_TBL_NAME, SQL_CHARACTERS_TABLE);
00167         createTable(ITEMS_TBL_NAME, SQL_ITEMS_TABLE);
00168         createTable(WORLD_ITEMS_TBL_NAME, SQL_WORLD_ITEMS_TABLE);
00169         createTable(INVENTORIES_TBL_NAME, SQL_INVENTORIES_TABLE);
00170         createTable(CHANNELS_TBL_NAME, SQL_CHANNELS_TABLE);
00171     }
00172     catch (const DbConnectionFailure& e) {
00173         LOG_ERROR("(DALStorage::open #1) Unable to connect to the database: "
00174                      << e.what());
00175     }
00176     catch (const DbSqlQueryExecFailure& e) {
00177         LOG_ERROR("(DALStorage::open #2) SQL query failure: " << e.what());
00178     }
00179 
00180     mIsOpen = mDb->isConnected();
00181 }
00182 
00183 
00187 void
00188 DALStorage::close(void)
00189 {
00190     mDb->disconnect();
00191     mIsOpen = mDb->isConnected();
00192 }
00193 
00194 
00198 AccountPtr
00199 DALStorage::getAccount(const std::string& userName)
00200 {
00201     // connect to the database (if not connected yet).
00202     open();
00203 
00204     // look for the account in the list first.
00205     Accounts::iterator it_end = mAccounts.end(),
00206         it = std::find_if(mAccounts.begin(), it_end, account_by_name(userName));
00207 
00208     if (it != it_end)
00209         return it->second;
00210 
00211     using namespace dal;
00212 
00213     // the account was not in the list, look for it in the database.
00214     try {
00215         std::ostringstream sql;
00216         sql << "select * from " << ACCOUNTS_TBL_NAME << " where username = \""
00217             << userName << "\";";
00218         const RecordSet& accountInfo = mDb->execSql(sql.str());
00219 
00220         // if the account is not even in the database then
00221         // we have no choice but to return nothing.
00222         if (accountInfo.isEmpty()) {
00223             return AccountPtr(NULL);
00224         }
00225 
00226         // specialize the string_to functor to convert
00227         // a string to an unsigned int.
00228         string_to< unsigned > toUint;
00229         unsigned id = toUint(accountInfo(0, 0));
00230 
00231         // create an Account instance
00232         // and initialize it with information about the user.
00233         AccountPtr account(new Account(accountInfo(0, 1),
00234                                        accountInfo(0, 2),
00235                                        accountInfo(0, 3), id));
00236 
00237         mAccounts.insert(std::make_pair(id, account));
00238 
00239         // load the characters associated with the account.
00240         sql.str(std::string());
00241         sql << "select id from " << CHARACTERS_TBL_NAME << " where user_id = '"
00242             << accountInfo(0, 0) << "';";
00243         RecordSet const &charInfo = mDb->execSql(sql.str());
00244 
00245         if (!charInfo.isEmpty())
00246         {
00247             int size = charInfo.rows();
00248             Characters characters;
00249 
00250             LOG_DEBUG(userName << "'s account has " << size
00251                       << " character(s) in database.");
00252 
00253             // Two steps: it seems like multiple requests cannot be alive at the same time.
00254             std::vector< unsigned > characterIDs;
00255             for (int k = 0; k < size; ++k)
00256             {
00257                 characterIDs.push_back(toUint(charInfo(k, 0)));
00258             }
00259 
00260             for (int k = 0; k < size; ++k)
00261             {
00262                 characters.push_back(getCharacter(characterIDs[k]));
00263             }
00264 
00265             account->setCharacters(characters);
00266         }
00267 
00268         return account;
00269     }
00270     catch (const DbSqlQueryExecFailure& e)
00271     {
00272         return AccountPtr(NULL); // TODO: Throw exception here
00273     }
00274 }
00275 
00279 AccountPtr
00280 DALStorage::getAccountByID(int accountID)
00281 {
00282     // connect to the database (if not connected yet).
00283     open();
00284 
00285     // look for the account in the list first.
00286     Accounts::iterator it = mAccounts.find(accountID);
00287 
00288     if (it != mAccounts.end())
00289         return it->second;
00290 
00291     using namespace dal;
00292 
00293     // the account was not in the list, look for it in the database.
00294     try {
00295         std::ostringstream sql;
00296         sql << "select * from " << ACCOUNTS_TBL_NAME << " where id = '"
00297             << accountID << "';";
00298         const RecordSet& accountInfo = mDb->execSql(sql.str());
00299 
00300         // if the account is not even in the database then
00301         // we have no choice but to return nothing.
00302         if (accountInfo.isEmpty()) {
00303             return AccountPtr(NULL);
00304         }
00305 
00306         // specialize the string_to functor to convert
00307         // a string to an unsigned int.
00308         string_to< unsigned > toUint;
00309         unsigned id = toUint(accountInfo(0, 0));
00310 
00311         // create an Account instance
00312         // and initialize it with information about the user.
00313         AccountPtr account(new Account(accountInfo(0, 1),
00314                                        accountInfo(0, 2),
00315                                        accountInfo(0, 3), id));
00316 
00317         mAccounts.insert(std::make_pair(id, account));
00318 
00319         // load the characters associated with the account.
00320         sql.str(std::string());
00321         sql << "select id from " << CHARACTERS_TBL_NAME << " where user_id = '"
00322             << accountInfo(0, 0) << "';";
00323         RecordSet const &charInfo = mDb->execSql(sql.str());
00324 
00325         if (!charInfo.isEmpty())
00326         {
00327             int size = charInfo.rows();
00328             Characters characters;
00329 
00330             LOG_DEBUG("AccountID: "<< accountID << "; has " << size
00331                       << " character(s) in database.");
00332 
00333             // Two steps: it seems like multiple requests cannot be alive at the same time.
00334             std::vector< unsigned > characterIDs;
00335             for (int k = 0; k < size; ++k)
00336             {
00337                 characterIDs.push_back(toUint(charInfo(k, 0)));
00338             }
00339 
00340             for (int k = 0; k < size; ++k)
00341             {
00342                 characters.push_back(getCharacter(characterIDs[k]));
00343             }
00344 
00345             account->setCharacters(characters);
00346         }
00347 
00348         return account;
00349     }
00350     catch (const DbSqlQueryExecFailure& e)
00351     {
00352         return AccountPtr(NULL); // TODO: Throw exception here
00353     }
00354 }
00355 
00359 CharacterPtr DALStorage::getCharacter(int id)
00360 {
00361     // connect to the database (if not connected yet).
00362     open();
00363 
00364     // look for the character in the list first.
00365     Characters::iterator it_end = mCharacters.end(),
00366         it = std::find_if(mCharacters.begin(), it_end, character_by_id(id));
00367 
00368     if (it != it_end)
00369         return *it;
00370 
00371     using namespace dal;
00372 
00373     // the account was not in the list, look for it in the database.
00374     try {
00375         std::ostringstream sql;
00376         sql << "select * from " << CHARACTERS_TBL_NAME << " where id = '"
00377             << id << "';";
00378         RecordSet const &charInfo = mDb->execSql(sql.str());
00379 
00380         // if the character is not even in the database then
00381         // we have no choice but to return nothing.
00382         if (charInfo.isEmpty())
00383         {
00384             return CharacterPtr(NULL);
00385         }
00386 
00387         // specialize the string_to functor to convert
00388         // a string to an unsigned int.
00389         string_to< unsigned > toUint;
00390 
00391         // specialize the string_to functor to convert
00392         // a string to an unsigned short.
00393         string_to< unsigned short > toUshort;
00394 
00395         CharacterData *character = new CharacterData(charInfo(0, 2),
00396                                                       toUint(charInfo(0, 0)));
00397         character->setAccountID(toUint(charInfo(0, 1)));
00398         character->setGender(toUshort(charInfo(0, 3)));
00399         character->setHairStyle(toUshort(charInfo(0, 4)));
00400         character->setHairColor(toUshort(charInfo(0, 5)));
00401         character->setLevel(toUshort(charInfo(0, 6)));
00402         character->setMoney(toUint(charInfo(0, 7)));
00403         Point pos(toUshort(charInfo(0, 8)), toUshort(charInfo(0, 9)));
00404         character->setPosition(pos);
00405         for (int i = 0; i < NB_BASE_ATTRIBUTES; ++i)
00406         {
00407             character->setBaseAttribute(i, toUshort(charInfo(0, 11 + i)));
00408         }
00409 
00410         int mapId = toUint(charInfo(0, 10));
00411         if (mapId > 0)
00412         {
00413             character->setMapId(mapId);
00414         }
00415         else
00416         {
00417             // Set character to default map and one of the default location
00418             // Default map is to be 1, as not found return value will be 0.
00419             character->setMapId((int)config.getValue("defaultMap", 1));
00420         }
00421 
00422         CharacterPtr ptr(character);
00423         mCharacters.push_back(ptr);
00424         return ptr;
00425     }
00426     catch (const DbSqlQueryExecFailure& e)
00427     {
00428         return CharacterPtr(NULL); // TODO: Throw exception here
00429     }
00430 }
00431 
00432 
00436 std::list<std::string>
00437 DALStorage::getEmailList()
00438 {
00439     // If not opened already
00440     open();
00441 
00442     std::list <std::string> emailList;
00443 
00444     try {
00445         std::string sql("select email from ");
00446         sql += ACCOUNTS_TBL_NAME;
00447         sql += ";";
00448         const dal::RecordSet& accountInfo = mDb->execSql(sql);
00449 
00450         // if the account is not even in the database then
00451         // we have no choice but to return nothing.
00452         if (accountInfo.isEmpty()) {
00453             return emailList;
00454         }
00455         for (unsigned int i = 0; i < accountInfo.rows(); i++)
00456         {
00457             // We add all these addresses to the list
00458             emailList.push_front(accountInfo(i, 0));
00459         }
00460     }
00461     catch (const dal::DbSqlQueryExecFailure& e) {
00462         // TODO: throw an exception.
00463         LOG_ERROR("(DALStorage::getEmailList) SQL query failure: " << e.what());
00464     }
00465 
00466     return emailList;
00467 }
00468 
00473 bool DALStorage::doesEmailAddressExist(std::string const &email)
00474 {
00475     // If not opened already
00476     open();
00477 
00478     try {
00479         std::ostringstream sql;
00480         sql << "select count(email) from " << ACCOUNTS_TBL_NAME
00481             << " where upper(email) = upper(\"" << email << "\");";
00482         dal::RecordSet const &accountInfo = mDb->execSql(sql.str());
00483 
00484         std::istringstream ssStream(accountInfo(0, 0));
00485         unsigned int iReturn = 1;
00486         ssStream >> iReturn;
00487         return iReturn != 0;
00488     } catch (std::exception const &e) {
00489         // TODO: throw an exception.
00490         LOG_ERROR("(DALStorage::doesEmailAddressExist) SQL query failure: " << e.what());
00491     }
00492 
00493     return true;
00494 }
00495 
00500 bool DALStorage::doesCharacterNameExist(const std::string& name)
00501 {
00502     // If not opened already
00503     open();
00504 
00505     try {
00506         std::ostringstream sql;
00507         sql << "select count(name) from " << CHARACTERS_TBL_NAME
00508             << " where name = \"" << name << "\";";
00509         dal::RecordSet const &accountInfo = mDb->execSql(sql.str());
00510 
00511         std::istringstream ssStream(accountInfo(0, 0));
00512         int iReturn = 1;
00513         ssStream >> iReturn;
00514         return iReturn != 0;
00515     } catch (std::exception const &e) {
00516         // TODO: throw an exception.
00517         LOG_ERROR("(DALStorage::doesCharacterNameExist) SQL query failure: "
00518                 << e.what());
00519     }
00520 
00521     return true;
00522 }
00523 
00524 bool
00525 DALStorage::updateCharacter(CharacterPtr character)
00526 {
00527     // If not opened already
00528     open();
00529 
00530     // Update the database Character data (see CharacterData for details)
00531     try
00532     {
00533         std::ostringstream sqlUpdateCharacterInfo;
00534         sqlUpdateCharacterInfo
00535             << "update "        << CHARACTERS_TBL_NAME << " "
00536             << "set "
00537             << "gender = '"     << character->getGender()
00538                                << "', "
00539             << "hair_style = '" << (int)character->getHairStyle()
00540                                << "', "
00541             << "hair_color = '" << (int)character->getHairColor()
00542                                << "', "
00543             << "level = '"      << (int)character->getLevel()
00544                                << "', "
00545             << "money = '"      << character->getMoney()
00546                                << "', "
00547             << "x = '"          << character->getPosition().x
00548                                << "', "
00549             << "y = '"          << character->getPosition().y
00550                                << "', "
00551             << "map_id = '"     << character->getMapId()
00552                                << "', "
00553             << "str = '"        << character->getBaseAttribute(BASE_ATTR_STRENGTH)
00554                                << "', "
00555             << "agi = '"        << character->getBaseAttribute(BASE_ATTR_AGILITY)
00556                                << "', "
00557             << "dex = '"        << character->getBaseAttribute(BASE_ATTR_DEXTERITY)
00558                                << "', "
00559             << "vit = '"        << character->getBaseAttribute(BASE_ATTR_VITALITY)
00560                                << "', "
00561 #if defined(MYSQL_SUPPORT) || defined(POSTGRESQL_SUPPORT)
00562             << "`int` = '"
00563 #else
00564             << "int = '"
00565 #endif
00566                                << character->getBaseAttribute(BASE_ATTR_INTELLIGENCE)
00567                                << "', "
00568 
00569             << "will = '"       << character->getBaseAttribute(BASE_ATTR_WILLPOWER)
00570                                 << "', "
00571             << "charisma = '"   << character->getBaseAttribute(BASE_ATTR_CHARISMA)
00572                                 << "' "
00573             << "where id = '"   << character->getDatabaseID()
00574                                << "';";
00575 
00576         mDb->execSql(sqlUpdateCharacterInfo.str());
00577     }
00578     catch (const dal::DbSqlQueryExecFailure& e)
00579     {
00580         // TODO: throw an exception.
00581         LOG_ERROR("(DALStorage::updateCharacter #1) SQL query failure: " << e.what());
00582         return false;
00583     }
00584 
00589     // Delete the old inventory first
00590     try
00591     {
00592         std::ostringstream sqlDeleteCharacterInventory;
00593         sqlDeleteCharacterInventory
00594             << "delete from " << INVENTORIES_TBL_NAME
00595             << " where owner_id = '" << character->getDatabaseID() << "';";
00596         mDb->execSql(sqlDeleteCharacterInventory.str());
00597     }
00598     catch (const dal::DbSqlQueryExecFailure& e)
00599     {
00600         // TODO: throw an exception.
00601         LOG_ERROR("(DALStorage::updateCharacter #2) SQL query failure: " << e.what());
00602         return false;
00603     }
00604 
00605     // Insert the new inventory data
00606     if (character->getNumberOfInventoryItems())
00607     {
00608         try
00609         {
00610             std::ostringstream sqlInsertCharacterInventory;
00611 
00612             sqlInsertCharacterInventory
00613                 << "insert into " << INVENTORIES_TBL_NAME
00614                 << " (owner_id, class_id, amount, equipped) "
00615                 << "values ";
00616 
00617             for (int j = 0; j < character->getNumberOfInventoryItems(); j++)
00618             {
00619                 sqlInsertCharacterInventory
00620                     << "(" << character->getDatabaseID() << ", "
00621                     << character->getInventoryItem(j).itemClassId << ", "
00622                     << character->getInventoryItem(j).numberOfItemsInSlot
00623                     << ", "
00624                     << (unsigned short)
00625                        character->getInventoryItem(j).isEquiped
00626                     << ")";
00627 
00628                 // Adding the comma only if it's needed
00629                 if (j < (character->getNumberOfInventoryItems() - 1))
00630                         sqlInsertCharacterInventory << ", ";
00631             }
00632             sqlInsertCharacterInventory << ";";
00633 
00634             mDb->execSql(sqlInsertCharacterInventory.str());
00635         }
00636         catch (const dal::DbSqlQueryExecFailure& e)
00637         {
00638             // TODO: throw an exception.
00639             LOG_ERROR("(DALStorage::updateCharacter #3) SQL query failure: " << e.what());
00640             return false;
00641         }
00642     }
00643     return true;
00644 }
00645 
00646 std::map<short, ChatChannel>
00647 DALStorage::getChannelList()
00648 {
00649     // If not opened already
00650     open();
00651 
00652     // specialize the string_to functor to convert
00653     // a string to a short.
00654     string_to<short> toShort;
00655     string_to<bool> toBool;
00656 
00657     // The formatted datas
00658     std::map<short, ChatChannel> channels;
00659 
00660     try {
00661         std::stringstream sql;
00662         sql << "select id, name, announcement, password, privacy from ";
00663         sql << CHANNELS_TBL_NAME;
00664         sql << ";";
00665 
00666         const dal::RecordSet& channelInfo = mDb->execSql(sql.str());
00667 
00668         // If the map return is empty then we have no choice but to return false.
00669         if (channelInfo.isEmpty()) {
00670             return channels;
00671         }
00672 
00673         for ( unsigned int i = 0; i < channelInfo.rows(); ++i)
00674         {
00675             channels.insert(std::make_pair(toShort(channelInfo(i,0)),
00676                             ChatChannel(channelInfo(i,1),
00677                                         channelInfo(i,2),
00678                                         channelInfo(i,3),
00679                                         toBool(channelInfo(i,4)))));
00680 
00681             LOG_DEBUG("Channel (" << channelInfo(i,0) << ") loaded: " << channelInfo(i,1)
00682                       << ": " << channelInfo(i,2));
00683         }
00684 
00685         return channels;
00686     }
00687     catch (const dal::DbSqlQueryExecFailure& e) {
00688         // TODO: throw an exception.
00689         LOG_ERROR("(DALStorage::getChannelList) SQL query failure: " << e.what());
00690     }
00691 
00692     return channels;
00693 }
00694 
00695 void
00696 DALStorage::updateChannels(std::map<short, ChatChannel>& channelList)
00697 {
00698 #if defined (SQLITE_SUPPORT)
00699     // Reopen the db in this thread for sqlite, to avoid
00700     // Library Call out of sequence problem due to thread safe.
00701     close();
00702 #endif
00703     open();
00704 
00705     try {
00706         // Empties the table
00707         std::stringstream sql;
00708         sql << "delete from "
00709             << CHANNELS_TBL_NAME
00710             << ";";
00711 
00712         mDb->execSql(sql.str());
00713 
00714         for (std::map<short, ChatChannel>::iterator i = channelList.begin();
00715                 i != channelList.end();)
00716         {
00717             // insert registered channel if id < MAX_PUBLIC_CHANNELS_RANGE;
00718             if ( i->first < (signed)MAX_PUBLIC_CHANNELS_RANGE )
00719             {
00720                 if (i->second.getName() != "")
00721                 {
00722                     sql.str("");
00723                     sql << "insert into "
00724                         << CHANNELS_TBL_NAME
00725                         << " (id, name, announcement, password, privacy)"
00726                         << " values ("
00727                         << i->first << ", \""
00728                         << i->second.getName() << "\", \""
00729                         << i->second.getAnnouncement() << "\", \""
00730                         << i->second.getPassword() << "\", \""
00731                         << i->second.getPrivacy() << "\");";
00732 
00733                         LOG_DEBUG("Channel (" << i->first << ") saved: "
00734                                   << i->second.getName()
00735                                   << ": " << i->second.getAnnouncement());
00736                 }
00737 
00738                 mDb->execSql(sql.str());
00739             }
00740             ++i;
00741         }
00742 
00743     }
00744     catch (const dal::DbSqlQueryExecFailure& e) {
00745         // TODO: throw an exception.
00746         LOG_ERROR("(DALStorage::updateChannels) SQL query failure: " << e.what());
00747     }
00748 }
00749 
00750 
00754 void
00755 DALStorage::createTable(const std::string& tblName,
00756                         const std::string& sql)
00757 {
00758     try {
00759         mDb->execSql(sql);
00760     }
00761     catch (const dal::DbSqlQueryExecFailure& e) {
00762         // error message to check against.
00763 #if defined (MYSQL_SUPPORT)
00764         std::string alreadyExists("Table '");
00765         alreadyExists += tblName;
00766         alreadyExists += "' already exists";
00767 #elif defined (POSTGRESQL_SUPPORT)
00768         std::string alreadyExists("table ");
00769         alreadyExists += tblName;
00770         alreadyExists += " already exists";
00771 #else // SQLITE_SUPPORT
00772         std::string alreadyExists("table ");
00773         alreadyExists += tblName;
00774         alreadyExists += " already exists";
00775 #endif
00776 
00777         const std::string msg(e.what());
00778 
00779         // oops, another problem occurred.
00780         if (msg != alreadyExists) {
00781             // rethrow to let other error handlers manage the problem.
00782             throw;
00783         }
00784     }
00785 }
00786 
00787 
00791 void DALStorage::addAccount(AccountPtr const &account)
00792 {
00793     assert(account->getCharacters().size() == 0);
00794 
00795     using namespace dal;
00796 
00797     // TODO: we should start a transaction here so that in case of problem
00798     // the lost of data would be minimized.
00799 
00800     // insert the account.
00801     std::ostringstream sql1;
00802     sql1 << "insert into " << ACCOUNTS_TBL_NAME
00803          << " (username, password, email, level, banned)"
00804          << " values (\""
00805          << account->getName() << "\", \""
00806          << account->getPassword() << "\", \""
00807          << account->getEmail() << "\", "
00808          << account->getLevel() << ", 0);";
00809     mDb->execSql(sql1.str());
00810 
00811     // get the account id.
00812     std::ostringstream sql2;
00813     sql2 << "select id from " << ACCOUNTS_TBL_NAME
00814          << " where username = \"" << account->getName() << "\";";
00815     const RecordSet& accountInfo = mDb->execSql(sql2.str());
00816     string_to<unsigned int> toUint;
00817     unsigned id = toUint(accountInfo(0, 0));
00818     account->setID(id);
00819     mAccounts.insert(std::make_pair(id, account));
00820 }
00821 
00825 void DALStorage::flushAll()
00826 {
00827     for (Accounts::iterator i = mAccounts.begin(),
00828          i_end = mAccounts.end(); i != i_end; ++i)
00829         flush(i->second);
00830 }
00831 
00835 void DALStorage::flush(AccountPtr const &account)
00836 {
00837     assert(account->getID() >= 0);
00838 
00839     using namespace dal;
00840 
00841     // TODO: we should start a transaction here so that in case of problem
00842     // the loss of data would be minimized.
00843 
00844     // update the account.
00845     std::ostringstream sqlUpdateAccountTable;
00846     sqlUpdateAccountTable << "update " << ACCOUNTS_TBL_NAME
00847          << " set username = \"" << account->getName() << "\", "
00848          << "password = \"" << account->getPassword() << "\", "
00849          << "email = \"" << account->getEmail() << "\", "
00850          << "level = '" << account->getLevel() << "' "
00851          << "where id = '" << account->getID() << "';";
00852     mDb->execSql(sqlUpdateAccountTable.str());
00853 
00854     // get the list of characters that belong to this account.
00855     Characters &characters = account->getCharacters();
00856 
00857     // insert or update the characters.
00858     for (Characters::const_iterator it = characters.begin(),
00859          it_end = characters.end(); it != it_end; ++it)
00860     {
00861         if ((*it)->getDatabaseID() < 0) {
00862             std::ostringstream sqlInsertCharactersTable;
00863             // insert the character
00864             // This assumes that the characters name has been checked for
00865             // uniqueness
00866             sqlInsertCharactersTable
00867                  << "insert into " << CHARACTERS_TBL_NAME
00868                  << " (user_id, name, gender, hair_style, hair_color, level, money,"
00869                  << " x, y, map_id, str, agi, dex, vit, int, will, charisma) values ("
00870                  << account->getID() << ", \""
00871                  << (*it)->getName() << "\", "
00872                  << (*it)->getGender() << ", "
00873                  << (int)(*it)->getHairStyle() << ", "
00874                  << (int)(*it)->getHairColor() << ", "
00875                  << (int)(*it)->getLevel() << ", "
00876                  << (*it)->getMoney() << ", "
00877                  << (*it)->getPosition().x << ", "
00878                  << (*it)->getPosition().y << ", "
00879                  << (*it)->getMapId() << ", "
00880                  << (*it)->getBaseAttribute(BASE_ATTR_STRENGTH) << ", "
00881                  << (*it)->getBaseAttribute(BASE_ATTR_AGILITY) << ", "
00882                  << (*it)->getBaseAttribute(BASE_ATTR_DEXTERITY) << ", "
00883                  << (*it)->getBaseAttribute(BASE_ATTR_VITALITY) << ", "
00884                  << (*it)->getBaseAttribute(BASE_ATTR_INTELLIGENCE) << ", "
00885                  << (*it)->getBaseAttribute(BASE_ATTR_WILLPOWER) << ", "
00886                  << (*it)->getBaseAttribute(BASE_ATTR_CHARISMA) << ");";
00887 
00888             mDb->execSql(sqlInsertCharactersTable.str());
00889         } else {
00890             std::ostringstream sqlUpdateCharactersTable;
00891             sqlUpdateCharactersTable
00892                 << "update " << CHARACTERS_TBL_NAME
00893                 << " set name = \"" << (*it)->getName() << "\", "
00894                 << " gender = " << (*it)->getGender() << ", "
00895                 << " hair_style = " << (int)(*it)->getHairStyle() << ", "
00896                 << " hair_color = " << (int)(*it)->getHairColor() << ", "
00897                 << " level = " << (int)(*it)->getLevel() << ", "
00898                 << " money = " << (*it)->getMoney() << ", "
00899                 << " x = " << (*it)->getPosition().x << ", "
00900                 << " y = " << (*it)->getPosition().y << ", "
00901                 << " map_id = " << (*it)->getMapId() << ", "
00902                 << " str = " << (*it)->getBaseAttribute(BASE_ATTR_STRENGTH) << ", "
00903                 << " agi = " << (*it)->getBaseAttribute(BASE_ATTR_AGILITY) << ", "
00904                 << " dex = " << (*it)->getBaseAttribute(BASE_ATTR_DEXTERITY) << ", "
00905                 << " vit = " << (*it)->getBaseAttribute(BASE_ATTR_VITALITY) << ", "
00906 #if defined(MYSQL_SUPPORT) || defined(POSTGRESQL_SUPPORT)
00907                 << " `int` = "
00908 #else
00909                 << " int = "
00910 #endif
00911                                << (*it)->getBaseAttribute(BASE_ATTR_INTELLIGENCE) << ", "
00912                 << " will = " << (*it)->getBaseAttribute(BASE_ATTR_WILLPOWER) << ", "
00913                 << " charisma = " << (*it)->getBaseAttribute(BASE_ATTR_CHARISMA)
00914                 << " where id = " << (*it)->getDatabaseID() << ";";
00915 
00916             mDb->execSql(sqlUpdateCharactersTable.str());
00917         }
00918 
00919         if ((*it)->getDatabaseID() < 0)
00920         {
00921             // get the character's id
00922             std::ostringstream sqlSelectIdCharactersTable;
00923             sqlSelectIdCharactersTable
00924                  << "select id from " << CHARACTERS_TBL_NAME
00925                  << " where name = \"" << (*it)->getName() << "\";";
00926             RecordSet const &charInfo =
00927                 mDb->execSql(sqlSelectIdCharactersTable.str());
00928 
00929             if (!charInfo.isEmpty()) {
00930                 string_to<unsigned int> toUint;
00931                 (*it)->setDatabaseID(toUint(charInfo(0, 0)));
00932             }
00933             else
00934             {
00935                 // TODO: The character's name is not unique, or some other
00936                 // error has occured
00937             }
00938         }
00939 
00940         // TODO: inventories.
00941     }
00942 
00943     // Existing characters in memory have been inserted or updated in database.
00944     // Now, let's remove those who are no more in memory from database.
00945 
00946     // specialize the string_to functor to convert
00947     // a string to an unsigned int.
00948     string_to<unsigned short> toUint;
00949 
00950     std::ostringstream sqlSelectNameIdCharactersTable;
00951     sqlSelectNameIdCharactersTable
00952          << "select name, id from " << CHARACTERS_TBL_NAME
00953          << " where user_id = '" << account->getID() << "';";
00954     const RecordSet& charInMemInfo =
00955         mDb->execSql(sqlSelectNameIdCharactersTable.str());
00956 
00957     // We compare chars from memory and those existing in db,
00958     // And delete those not in mem but existing in db.
00959     bool charFound;
00960     for (unsigned int i = 0; i < charInMemInfo.rows(); ++i) // in database
00961     {
00962         charFound = false;
00963         for (Characters::const_iterator it = characters.begin(),
00964              it_end = characters.end(); it != it_end; ++it) // In memory
00965         {
00966             if (charInMemInfo(i, 0) == (*it)->getName())
00967             {
00968                 charFound = true;
00969                 break;
00970             }
00971         }
00972         if (!charFound)
00973         {
00974             // The char is db but not in memory,
00975             // It will be removed from database.
00976             // We store the id of the char to delete
00977             // Because as deleted, the RecordSet is also emptied
00978             // That creates an error.
00979             unsigned int charId = toUint(charInMemInfo(i, 1));
00980 
00981                 // delete the inventory.
00982                 std::ostringstream sqlDeleteInventoryTable;
00983                 sqlDeleteInventoryTable
00984                     << "delete from "
00985                     << INVENTORIES_TBL_NAME
00986                     << " where owner_id = '"
00987                     << charId
00988                     << "';";
00989                 mDb->execSql(sqlDeleteInventoryTable.str());
00990 
00991                 // now delete the character.
00992                 std::ostringstream sqlDeleteCharactersTable;
00993                 sqlDeleteCharactersTable
00994                     << "delete from "
00995                     << CHARACTERS_TBL_NAME
00996                     << " where id = '"
00997                     << charId
00998                     << "';";
00999                 mDb->execSql(sqlDeleteCharactersTable.str());
01000         }
01001     }
01002 }
01003 
01004 
01008 void DALStorage::delAccount(AccountPtr const &account)
01009 {
01010     using namespace dal;
01011 
01012     account->setCharacters(Characters());
01013     flush(account);
01014     mAccounts.erase(account->getID());
01015 
01016     // delete the account.
01017     std::ostringstream sql;
01018     sql << "delete from " << ACCOUNTS_TBL_NAME
01019         << " where id = '" << account->getID() << "';";
01020     mDb->execSql(sql.str());
01021 }
01022 
01026 void DALStorage::unloadAccount(AccountPtr const &account)
01027 {
01028     flush(account);
01029     mAccounts.erase(account->getID());
01030 }

Generated on Fri Mar 30 15:39:16 2007 for TMW Server by  doxygen 1.3.9.1