summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHuynh Tran <nthuynh75@gmail.com>2005-06-21 19:46:40 +0000
committerHuynh Tran <nthuynh75@gmail.com>2005-06-21 19:46:40 +0000
commit49d79a345c8c4929d9bb787af9f4f0090c888537 (patch)
tree387b3a8d738d570ad1493be0273751c7a145cf20 /src
parent816d8acd16faeb20713c3d09466003a444940f6f (diff)
downloadmanaserv-49d79a345c8c4929d9bb787af9f4f0090c888537.tar.gz
manaserv-49d79a345c8c4929d9bb787af9f4f0090c888537.tar.bz2
manaserv-49d79a345c8c4929d9bb787af9f4f0090c888537.tar.xz
manaserv-49d79a345c8c4929d9bb787af9f4f0090c888537.zip
Improved Storage APIs and moved debug code to unit tests.
Diffstat (limited to 'src')
-rw-r--r--src/dalstorage.cpp259
-rw-r--r--src/dalstorage.h40
-rw-r--r--src/main.cpp4
-rw-r--r--src/storage.cpp76
-rw-r--r--src/storage.h205
5 files changed, 452 insertions, 132 deletions
diff --git a/src/dalstorage.cpp b/src/dalstorage.cpp
index 1bcafedf..14c3ff7a 100644
--- a/src/dalstorage.cpp
+++ b/src/dalstorage.cpp
@@ -24,6 +24,7 @@
#include <sstream>
#include "utils/functors.h"
+#include "utils/logger.h"
#include "dalstorage.h"
#include "dalstoragesql.h"
@@ -50,17 +51,19 @@ DALStorage::DALStorage()
DALStorage::~DALStorage()
throw()
{
- mDb->disconnect();
+ if (mDb->isConnected()) {
+ close();
+ }
- // clean up loaded accounts.
+ // clean up accounts.
for (Accounts::iterator it = mAccounts.begin();
it != mAccounts.end();
++it)
{
- delete (*it);
+ delete it->first;
}
- // clean up loaded characters.
+ // clean up characters.
for (Beings::iterator it = mCharacters.begin();
it != mCharacters.end();
++it)
@@ -71,24 +74,105 @@ DALStorage::~DALStorage()
/**
+ * Connect to the database and initialize it if necessary.
+ */
+void
+DALStorage::open(void)
+{
+ // do nothing if already connected.
+ if (mDb->isConnected()) {
+ return;
+ }
+
+ using namespace dal;
+
+ try {
+ // open a connection to the database.
+#if defined (MYSQL_SUPPORT) || defined (POSTGRE_SUPPORT)
+ mDb->connect(getName(), getUser(), getPassword());
+#else // SQLITE_SUPPORT
+ // create the database file name.
+ std::string dbFile(getName());
+ dbFile += ".db";
+ mDb->connect(dbFile, "", "");
+#endif
+
+ // ensure that the required tables are created.
+ //
+ // strategy1: find a way to obtain the list of tables from the
+ // underlying database and create the tables that are
+ // missing.
+ //
+ // strategy2: try to create the tables and check the exceptions
+ // thrown.
+ //
+ // comments:
+ // - strategy1 is easy to achieve if we are using MysQL as
+ // executing the request "show tables;" returns the list of
+ // tables. However, there is not such a query for SQLite3.
+ // When using SQLite3 from the interactive shell or the
+ // command line, the command ".tables" returns the list of
+ // tables but sqlite3_exec() does not validate this statement
+ // and fails.
+ // The cost of this strategy is:
+ // (num. tables to create + 1) queries at most and
+ // 1 at minimum.
+ //
+ // - strategy2 will work with probably most databases.
+ // The cost of this strategy is:
+ // (num. tables to create) queries.
+
+ // we will stick with strategy2 for the moment as we are focusing
+ // on SQLite.
+
+ createTable(MAPS_TBL_NAME, SQL_MAPS_TABLE);
+ createTable(ACCOUNTS_TBL_NAME, SQL_ACCOUNTS_TABLE);
+ createTable(CHARACTERS_TBL_NAME, SQL_CHARACTERS_TABLE);
+ createTable(ITEMS_TBL_NAME, SQL_ITEMS_TABLE);
+ createTable(WORLD_ITEMS_TBL_NAME, SQL_WORLD_ITEMS_TABLE);
+ createTable(INVENTORIES_TBL_NAME, SQL_INVENTORIES_TABLE);
+ }
+ catch (const DbConnectionFailure& e) {
+ LOG_ERROR("unable to connect to the database: " << e.what())
+ }
+ catch (const DbSqlQueryExecFailure& e) {
+ LOG_ERROR("SQL query failure: " << e.what())
+ }
+
+ mIsOpen = mDb->isConnected();
+}
+
+
+/**
+ * Disconnect from the database.
+ */
+void
+DALStorage::close(void)
+{
+ mDb->disconnect();
+ mIsOpen = mDb->isConnected();
+}
+
+
+/**
* Get an account by user name.
*/
Account*
DALStorage::getAccount(const std::string& userName)
{
// connect to the database (if not connected yet).
- connect();
+ open();
// look for the account in the list first.
Accounts::iterator it =
std::find_if(
mAccounts.begin(),
mAccounts.end(),
- std::bind2nd(obj_name_is<Account*>(), userName)
+ account_by_name(userName)
);
if (it != mAccounts.end()) {
- return (*it);
+ return it->first;
}
using namespace dal;
@@ -116,7 +200,7 @@ DALStorage::getAccount(const std::string& userName)
account->setEmail(accountInfo(0, 3));
// add the new Account to the list.
- mAccounts.push_back(account);
+ mAccounts.insert(std::make_pair(account, AS_ACC_TO_UPDATE));
// load the characters associated with the account.
sql = "select * from ";
@@ -165,123 +249,98 @@ DALStorage::getAccount(const std::string& userName)
* Add a new account.
*/
void
-DALStorage::addAccount(const Account* account)
+DALStorage::addAccount(Account* account)
{
- // TODO
+ if (account == 0) {
+ // maybe we should throw an exception instead
+ return;
+ }
+
+ // mark this account as new so that the next flush will execute a SQL
+ // insert query instead of a SQL update query.
+ mAccounts.insert(std::make_pair(account, AS_NEW_ACCOUNT));
}
/**
- * Save changes to the database permanently.
+ * Delete an account.
*/
void
-DALStorage::flush(void)
+DALStorage::delAccount(const std::string& userName)
{
- // this feature is not currently provided by DAL.
-}
+ // look for the account in memory first.
+ Accounts::iterator it =
+ std::find_if(
+ mAccounts.begin(),
+ mAccounts.end(),
+ account_by_name(userName)
+ );
+ if (it != mAccounts.end()) {
+ switch (it->second) {
+ case AS_NEW_ACCOUNT:
+ // this is a newly added account and it has not even been
+ // saved into the database: remove it immediately.
+ delete it->first;
+ break;
+
+ case AS_ACC_TO_UPDATE:
+ // change the status to AS_ACC_TO_DELETE so that it will be
+ // deleted at the next flush.
+ it->second = AS_ACC_TO_DELETE;
+ break;
+
+ default:
+ break;
+ }
-/**
- * Get the number of Accounts saved in database.
- */
-unsigned int
-DALStorage::getAccountCount(void)
-{
- // connect to the database (if not connected yet).
- connect();
+ // nothing else to do.
+ return;
+ }
using namespace dal;
- unsigned int value = 0;
-
try {
- // query the database.
- std::string sql("select count(*) from ");
+ std::string sql("select id from ");
sql += ACCOUNTS_TBL_NAME;
- sql += ";";
- const RecordSet& rs = mDb->execSql(sql);
+ sql += " where username = '";
+ sql += userName;
+ sql += "';";
+ const RecordSet& accountInfo = mDb->execSql(sql);
- // specialize the string_to functor to convert
- // a string to an unsigned int.
- string_to<unsigned int> toUint;
+ // the account does not even exist in the database,
+ // there is nothing to do then.
+ if (accountInfo.isEmpty()) {
+ return;
+ }
- value = toUint(rs(0, 0));
- } catch (const DbSqlQueryExecFailure& f) {
- std::cout << "Get accounts count failed :'(" << std::endl;
+ // TODO: actually deleting the account from the database.
+ // order of deletion:
+ // 1. inventories of all the characters of the account,
+ // 2. all the characters,
+ // 3. the account itself.
+ }
+ catch (const DbSqlQueryExecFailure& e) {
+ // TODO: throw an exception.
}
-
- return value;
}
/**
- * Connect to the database and initialize it if necessary.
+ * Save changes to the database permanently.
*/
void
-DALStorage::connect(void)
+DALStorage::flush(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", "", "");
-
- // ensure that the required tables are created.
- //
- // strategy1: find a way to obtain the list of tables from the
- // underlying database and create the tables that are
- // missing.
- //
- // strategy2: try to create the tables and check the exceptions
- // thrown.
- //
- // comments:
- // - strategy1 is easy to achieve if we are using MysQL as
- // executing the request "show tables;" returns the list of
- // tables. However, there is not such a query for SQLite3.
- // When using SQLite3 from the interactive shell or the
- // command line, the command ".tables" returns the list of
- // tables but sqlite3_exec() does not validate this statement
- // and fails.
- // The cost of this strategy is:
- // (num. tables to create + 1) queries at most and
- // 1 at minimum.
- //
- // - strategy2 will work with probably most databases.
- // The cost of this strategy is:
- // (num. tables to create) queries.
-
- // we will stick with strategy2 for the moment as we are focusing
- // on SQLite.
-
- createTable(MAPS_TBL_NAME, SQL_MAPS_TABLE);
- createTable(ACCOUNTS_TBL_NAME, SQL_ACCOUNTS_TABLE);
- createTable(CHARACTERS_TBL_NAME, SQL_CHARACTERS_TABLE);
- createTable(ITEMS_TBL_NAME, SQL_ITEMS_TABLE);
- createTable(WORLD_ITEMS_TBL_NAME, SQL_WORLD_ITEMS_TABLE);
- createTable(INVENTORIES_TBL_NAME, SQL_INVENTORIES_TABLE);
-
- // 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;
- }
+ // TODO
+ // For each account in memory:
+ // - get the status
+ // - if AS_NEW_ACCOUNT then insert into database;
+ // - if AS_ACC_TO_UPDATE then update values from the database
+ // - if AS_ACC_TO_DELETE then delete from database
+ // Notes:
+ // - this will probably involve more than one table as the account may
+ // have characters associated to it.
}
diff --git a/src/dalstorage.h b/src/dalstorage.h
index 59a3eb3b..78a689c7 100644
--- a/src/dalstorage.h
+++ b/src/dalstorage.h
@@ -51,6 +51,20 @@ class DALStorage: public Storage
public:
/**
+ * Connect to the database and initialize it if necessary.
+ */
+ void
+ open(void);
+
+
+ /**
+ * Disconnect from the database.
+ */
+ void
+ close(void);
+
+
+ /**
* Get an account by user name.
*
* @param userName the owner of the account.
@@ -67,23 +81,23 @@ class DALStorage: public Storage
* @param account the new account.
*/
void
- addAccount(const Account* account);
+ addAccount(Account* account);
/**
- * Save changes to the database permanently.
+ * Delete an account.
+ *
+ * @param userName the owner of the account.
*/
void
- flush(void);
+ delAccount(const std::string& userName);
/**
- * Get the number of Accounts saved in database (test function).
- *
- * @return the number of Accounts.
+ * Save changes to the database permanently.
*/
- unsigned int
- getAccountCount(void);
+ void
+ flush(void);
private:
@@ -114,13 +128,6 @@ class DALStorage: public Storage
/**
- * Connect to the database and initialize it if necessary.
- */
- void
- connect(void);
-
-
- /**
* Create the specified table.
*
* @param tblName the table name.
@@ -135,9 +142,6 @@ class DALStorage: public Storage
private:
std::auto_ptr<dal::DataProvider> mDb; /**< the data provider */
- typedef std::vector<Account*> Accounts;
- Accounts mAccounts; /**< the loaded accounts */
- Beings mCharacters; /**< the loaded characters */
};
diff --git a/src/main.cpp b/src/main.cpp
index f5de932f..b8a493f6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -182,9 +182,7 @@ int main(int argc, char *argv[])
using namespace tmwserv;
- Storage& store = Storage::instance();
-
- LOG_INFO("Number of accounts on server: " << store.getAccountCount())
+ Storage& store = Storage::instance("tmw");
// Test the database retrieval code
LOG_INFO("Attempting to retrieve account with username 'nym'")
diff --git a/src/storage.cpp b/src/storage.cpp
index b5476426..adea90e6 100644
--- a/src/storage.cpp
+++ b/src/storage.cpp
@@ -31,8 +31,11 @@ namespace tmwserv
{
-// initialize the static attribute.
+// initialize the static attributes.
Storage* Storage::mInstance = 0;
+std::string Storage::mName("");
+std::string Storage::mUser("");
+std::string Storage::mPassword("");
/**
@@ -59,10 +62,13 @@ Storage::~Storage(void)
* Create an instance of Storage.
*/
Storage&
-Storage::instance(void)
+Storage::instance(const std::string& name)
{
if (mInstance == 0) {
mInstance = new DALStorage();
+
+ // set the name of the storage.
+ mName = name;
}
// check that the instance has been created.
@@ -82,7 +88,73 @@ Storage::destroy(void)
{
if (mInstance != 0) {
delete mInstance;
+ mInstance = 0;
}
+
+ // reset the attributes.
+ mName = "";
+ mUser = "";
+ mPassword = "";
+}
+
+
+/**
+ * Check if the storage is open.
+ */
+bool
+Storage::isOpen(void) const
+{
+ return mIsOpen;
+}
+
+
+/**
+ * Get the storage name.
+ */
+const std::string&
+Storage::getName(void) const
+{
+ return mName;
+}
+
+
+/**
+ * Set a user name for the storage.
+ */
+void
+Storage::setUser(const std::string& userName)
+{
+ mUser = userName;
+}
+
+
+/**
+ * Get the user name.
+ */
+const std::string&
+Storage::getUser(void) const
+{
+ return mUser;
+}
+
+
+/**
+ * Set a user password for the storage.
+ */
+void
+Storage::setPassword(const std::string& password)
+{
+ mPassword = password;
+}
+
+
+/**
+ * Get the user password.
+ */
+const std::string&
+Storage::getPassword(void) const
+{
+ return mPassword;
}
diff --git a/src/storage.h b/src/storage.h
index e6c93ba7..5b695bb0 100644
--- a/src/storage.h
+++ b/src/storage.h
@@ -25,6 +25,8 @@
#define _TMWSERV_STORAGE_H_
+#include <map>
+
#include "object.h"
#include "account.h"
@@ -34,6 +36,83 @@ namespace tmwserv
/**
+ * Enumeration type for the account status:
+ *
+ * AS_NEW_ACCOUNT : set to an account that is added to the storage and
+ * hence not yet save to disk (file or database);
+ * accounts with this status will be saved to disk at
+ * the next flush.
+ * AS_ACC_TO_UPDATE: set to an account that was loaded from disk;
+ * accounts with this status will be updated at the next
+ * flush.
+ * AS_ACC_TO_DELETE: set to an account that was loaded from disk;
+ * accounts with this status will be deleted from disk at
+ * the next flush.
+ *
+ * Notes: an account that is requested to be deleted and still has the
+ * status AS_NEW_ACCOUNT (and therefore not yet saved to disk) will
+ * be immediately deleted from memory to save useless transactions
+ * to disk.
+ */
+typedef enum {
+ AS_NEW_ACCOUNT,
+ AS_ACC_TO_UPDATE,
+ AS_ACC_TO_DELETE
+} AccountStatus;
+
+
+/**
+ * Functor to be used as the sorting criterion of the map defined below.
+ */
+struct account_sort_by_name
+ : public std::binary_function<Account*, Account*, bool>
+{
+ bool
+ operator()(Account* acc1, Account* acc2) const
+ {
+ return (acc1->getName() < acc2->getName());
+ }
+};
+
+
+/**
+ * Data type for the list of accounts.
+ */
+typedef std::map<Account*, AccountStatus, account_sort_by_name> Accounts;
+
+
+/**
+ * Functor used to search an Account by name in the map defined above.
+ */
+class account_by_name
+{
+ public:
+ /**
+ * Constructor.
+ */
+ account_by_name(const std::string& name)
+ : mName(name)
+ {
+ // NOP
+ }
+
+
+ /**
+ * Operator().
+ */
+ bool
+ operator()(std::pair<Account*, AccountStatus> elem) const
+ {
+ return (elem.first)->getName() == mName;
+ }
+
+
+ private:
+ std::string mName; /**< the name to look for */
+};
+
+
+/**
* A storage to load and persist dynamic data.
*
* Notes:
@@ -45,14 +124,25 @@ class Storage
{
public:
/**
- * Create an instance of Storage.
+ * Create a named instance of Storage.
+ *
+ * @param name the name of the storage.
*
* @return the unique instance of Storage.
*
* @exception std::bad_alloc if the instance cannot be created.
+ *
+ * Notes:
+ * - it is up to the underlying implementation of Storage to
+ * decide about what to do with the name, it could serve as the
+ * name of the database or the name of the file into which the
+ * storage will be dumped to.
+ * - the name of the storage is saved only when it's the first
+ * invocation of instance() or only when instance() is invoked
+ * after destroy().
*/
static Storage&
- instance(void);
+ instance(const std::string& name);
/**
@@ -63,6 +153,92 @@ class Storage
/**
+ * Open the storage for read/write access.
+ *
+ * Depending on the underlying implementation of Storage, opening
+ * a storage would mean either opening a file or connecting to a
+ * database.
+ */
+ virtual void
+ open(void) = 0;
+
+
+ /**
+ * Close the storage.
+ *
+ * Depending on the underlying implementation of Storage, closing
+ * a storage would mean either closing a file or disconnecting from
+ * a database.
+ */
+ virtual void
+ close(void) = 0;
+
+
+ /**
+ * Check if the storage is open.
+ *
+ * @return true if the storage is open.
+ */
+ bool
+ isOpen(void) const;
+
+
+ /**
+ * Get the storage name.
+ *
+ * @return the storage name.
+ */
+ const std::string&
+ getName(void) const;
+
+
+ /**
+ * Set a user name for the storage.
+ *
+ * Depending on the underlying implementation of Storage, setting
+ * the user name may have no effect (e.g. DALStorage running on
+ * SQLite).
+ *
+ * @param userName the user name.
+ */
+ void
+ setUser(const std::string& userName);
+
+
+ /**
+ * Get the user name.
+ *
+ * @return the user name (it may be an empty string if not set
+ * previously).
+ */
+ const std::string&
+ getUser(void) const;
+
+
+ /**
+ * Set a user password for the storage.
+ *
+ * Depending on the underlying implementation of Storage, setting
+ * the user password may have no effect (e.g. DALStorage running on
+ * SQLite).
+ *
+ * @param password the user password.
+ */
+ void
+ setPassword(const std::string& password);
+
+
+ /**
+ * Get the user password.
+ *
+ * @return the user password (it may be an empty string if not set
+ * previously).
+ */
+ const std::string&
+ getPassword(void) const;
+
+
+ /**
* Get an account by user name.
*
* @param userName the owner of the account.
@@ -79,21 +255,23 @@ class Storage
* @param account the new account.
*/
virtual void
- addAccount(const Account* account) = 0;
+ addAccount(Account* account) = 0;
/**
- * Make sure any changes are saved.
+ * Delete an account.
+ *
+ * @param userName the owner of the account.
*/
virtual void
- flush(void) = 0;
+ delAccount(const std::string& userName) = 0;
/**
- * Account count (test function).
+ * Saves the changes permanently.
*/
- virtual unsigned int
- getAccountCount(void) = 0;
+ virtual void
+ flush(void) = 0;
protected:
@@ -125,8 +303,17 @@ class Storage
operator=(const Storage& rhs);
+ protected:
+ Accounts mAccounts; /**< list of accounts in memory */
+ Beings mCharacters; /**< the loaded characters */
+ bool mIsOpen; /**< flag is true if the storage is open */
+
+
private:
- static Storage* mInstance; /**< the unique instance of Storage */
+ static Storage* mInstance; /**< the unique instance of Storage */
+ static std::string mName; /**< the name of the storage */
+ static std::string mUser; /**< the user name */
+ static std::string mPassword; /**< the user password */
};