diff options
author | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2007-02-04 21:52:17 +0000 |
---|---|---|
committer | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2007-02-04 21:52:17 +0000 |
commit | cade1ba618bd1883e044999a0cbda201f1f76dd4 (patch) | |
tree | 978d8e0d97b0f5b0eec3a77a8d25e54b870ed786 /src/account-server | |
parent | 9c0421f5926d22a44c978fff683c51748a19e976 (diff) | |
download | manaserv-cade1ba618bd1883e044999a0cbda201f1f76dd4.tar.gz manaserv-cade1ba618bd1883e044999a0cbda201f1f76dd4.tar.bz2 manaserv-cade1ba618bd1883e044999a0cbda201f1f76dd4.tar.xz manaserv-cade1ba618bd1883e044999a0cbda201f1f76dd4.zip |
Added support for switching character by reconnecting to the account server and
fixed the issue where a client is not logged in after registering (patch by
Rogier Polak).
Diffstat (limited to 'src/account-server')
-rw-r--r-- | src/account-server/accounthandler.cpp | 141 | ||||
-rw-r--r-- | src/account-server/accounthandler.hpp | 3 | ||||
-rw-r--r-- | src/account-server/dalstorage.cpp | 89 | ||||
-rw-r--r-- | src/account-server/dalstorage.hpp | 10 | ||||
-rw-r--r-- | src/account-server/serverhandler.cpp | 23 | ||||
-rw-r--r-- | src/account-server/storage.hpp | 10 |
6 files changed, 263 insertions, 13 deletions
diff --git a/src/account-server/accounthandler.cpp b/src/account-server/accounthandler.cpp index 3b0663af..c6a086ea 100644 --- a/src/account-server/accounthandler.cpp +++ b/src/account-server/accounthandler.cpp @@ -21,9 +21,11 @@ * $Id$ */ +#include "account-server/accounthandler.hpp" + +#include "defines.h" #include "configuration.h" #include "point.h" -#include "account-server/accounthandler.hpp" #include "account-server/account.hpp" #include "account-server/accountclient.hpp" #include "account-server/serverhandler.hpp" @@ -36,6 +38,27 @@ #include "utils/logger.h" #include "utils/stringfilter.h" +// TODO: Implement a class to handle these tokens more generally + +typedef std::map< std::string, AccountClient* > AccountPendingClients; +typedef std::map< std::string, int > AccountPendingReconnects; + +/** + * Client is faster then game server + */ +static AccountPendingClients pendingClients; + +/** + * Game server is faster then client + */ +static AccountPendingReconnects pendingReconnects; + +void +registerAccountReconnect(int accountID, const std::string& magic_token); + +void +handleReconnectedAccount(AccountClient &computer, int accountID); + bool AccountHandler::startListen(enet_uint16 port) { @@ -81,23 +104,33 @@ AccountHandler::processMessage(NetComputer *comp, MessageIn &message) switch (message.getId()) { case PAMSG_LOGIN: + LOG_DEBUG("Received msg ... PAMSG_LOGIN"); handleLoginMessage(computer, message); break; case PAMSG_LOGOUT: + LOG_DEBUG("Received msg ... PAMSG_LOGOUT"); handleLogoutMessage(computer); break; + case PAMSG_RECONNECT: + LOG_DEBUG("Received msg ... PAMSG_RECONNECT"); + handleReconnectMessage(computer, message); + break; + case PAMSG_REGISTER: + LOG_DEBUG("Received msg ... PAMSG_REGISTER"); handleRegisterMessage(computer, message); break; case PAMSG_UNREGISTER: + LOG_DEBUG("Received msg ... PAMSG_UNREGISTER"); handleUnregisterMessage(computer, message); break; case PAMSG_EMAIL_CHANGE: { + LOG_DEBUG("Received msg ... PAMSG_EMAIL_CHANGE"); result.writeShort(APMSG_EMAIL_CHANGE_RESPONSE); if (computer.getAccount().get() == NULL) { @@ -128,6 +161,7 @@ AccountHandler::processMessage(NetComputer *comp, MessageIn &message) case PAMSG_EMAIL_GET: { + LOG_DEBUG("Received msg ... PAMSG_EMAIL_GET"); result.writeShort(APMSG_EMAIL_GET_RESPONSE); if (computer.getAccount().get() == NULL) { result.writeByte(ERRMSG_NO_LOGIN); @@ -142,15 +176,18 @@ AccountHandler::processMessage(NetComputer *comp, MessageIn &message) break; case PAMSG_PASSWORD_CHANGE: + LOG_DEBUG("Received msg ... PAMSG_PASSWORD_CHANGE"); handlePasswordChangeMessage(computer, message); break; case PAMSG_CHAR_CREATE: + LOG_DEBUG("Received msg ... PAMSG_CHAR_CREATE"); handleCharacterCreateMessage(computer, message); break; case PAMSG_CHAR_SELECT: { + LOG_DEBUG("Received msg ... PAMSG_CHAR_SELECT"); result.writeShort(APMSG_CHAR_SELECT_RESPONSE); if (computer.getAccount().get() == NULL) @@ -205,6 +242,7 @@ AccountHandler::processMessage(NetComputer *comp, MessageIn &message) case PAMSG_CHAR_DELETE: { + LOG_DEBUG("Received msg ... PAMSG_CHAR_DELETE"); result.writeShort(APMSG_CHAR_DELETE_RESPONSE); if (computer.getAccount().get() == NULL) @@ -244,7 +282,8 @@ AccountHandler::processMessage(NetComputer *comp, MessageIn &message) break; default: - LOG_WARN("Invalid message type"); + LOG_WARN("AccountHandler::processMessage, Invalid message type " + << message.getId()); result.writeShort(XXMSG_INVALID); break; } @@ -340,6 +379,34 @@ AccountHandler::handleLogoutMessage(AccountClient &computer) } void +AccountHandler::handleReconnectMessage(AccountClient &computer, MessageIn &msg) +{ + if (computer.getAccount().get() == NULL) + { + std::string magic_token = msg.readString(32); + AccountPendingReconnects::iterator i = pendingReconnects.find(magic_token); + if (i == pendingReconnects.end()) + { + for (AccountPendingClients::iterator j = pendingClients.begin(), + j_end = pendingClients.end(); j != j_end; ++j) + { + if (j->second == &computer) return; //Allready inserted + } + pendingClients.insert(std::make_pair(magic_token, &computer)); + return; //Waiting for the gameserver, note: using return instead of an else + } + // Gameserver communication was faster, connect the client + int accountID = i->second; + pendingReconnects.erase(i); + handleReconnectedAccount(computer, accountID); + } + else + { + LOG_WARN("Account tried to reconnect, but was allready logged in."); + } +} + +void AccountHandler::handleRegisterMessage(AccountClient &computer, MessageIn &msg) { unsigned long clientVersion = msg.readLong(); @@ -400,6 +467,9 @@ AccountHandler::handleRegisterMessage(AccountClient &computer, MessageIn &msg) AccountPtr acc(new Account(username, password, email)); store.addAccount(acc); reply.writeByte(ERRMSG_OK); + + // Associate account with connection + computer.setAccount(acc); } } @@ -410,6 +480,7 @@ void AccountHandler::handleUnregisterMessage(AccountClient &computer, MessageIn &msg) { + LOG_DEBUG("AccountHandler::handleUnregisterMessage"); std::string username = msg.readString(); std::string password = msg.readString(); @@ -424,13 +495,18 @@ AccountHandler::handleUnregisterMessage(AccountClient &computer, // See if the account exists Storage &store = Storage::instance("tmw"); AccountPtr accPtr = store.getAccount(username); - + LOG_INFO("Unregister for " << username << " with passwords #" << accPtr->getPassword() << "#" << password << "#"); if (!accPtr.get() || accPtr->getPassword() != password) { reply.writeByte(ERRMSG_INVALID_ARGUMENT); } else { + // Delete account and associated characters + LOG_DEBUG("Farewell " << username << " ..."); + store.delAccount(accPtr); + reply.writeByte(ERRMSG_OK); + // If the account to delete is the current account we're logged in. // Get out of it in memory. if (computer.getAccount().get() != NULL ) @@ -440,11 +516,6 @@ AccountHandler::handleUnregisterMessage(AccountClient &computer, computer.unsetAccount(); } } - - // Delete account and associated characters - LOG_INFO("Farewell " << username << " ..."); - store.delAccount(accPtr); - reply.writeByte(ERRMSG_OK); } } @@ -627,3 +698,57 @@ AccountHandler::handleCharacterCreateMessage(AccountClient &computer, computer.send(reply); } + +void +registerAccountReconnect(int accountID, const std::string& magic_token) +{ + AccountPendingClients::iterator i = pendingClients.find(magic_token); + if (i == pendingClients.end()) + { + pendingReconnects.insert(std::make_pair(magic_token, accountID)); + } + else + { + AccountClient &computer = *(i->second); + handleReconnectedAccount(computer, accountID); + pendingClients.erase(i); + } +} + +void +handleReconnectedAccount(AccountClient &computer, int accountID) +{ + MessageOut reply(APMSG_RECONNECT_RESPONSE); + + // Check if the account exists + Storage &store = Storage::instance("tmw"); + AccountPtr acc = store.getAccountByID(accountID); + + // Associate account with connection + computer.setAccount(acc); + + reply.writeByte(ERRMSG_OK); + computer.send(reply); + + // Return information about available characters + Players &chars = computer.getAccount()->getCharacters(); + + // Send characters list + for (unsigned int i = 0; i < chars.size(); i++) + { + MessageOut charInfo(APMSG_CHAR_INFO); + charInfo.writeByte(i); // Slot + charInfo.writeString(chars[i]->getName()); + charInfo.writeByte((unsigned char) chars[i]->getGender()); + charInfo.writeByte(chars[i]->getHairStyle()); + charInfo.writeByte(chars[i]->getHairColor()); + charInfo.writeByte(chars[i]->getLevel()); + charInfo.writeShort(chars[i]->getMoney()); + + for (int j = 0; j < NB_RSTAT; ++j) + { + charInfo.writeShort(chars[i]->getRawStat(j)); + } + computer.send(charInfo); + } +} diff --git a/src/account-server/accounthandler.hpp b/src/account-server/accounthandler.hpp index 712cde9a..da9140e4 100644 --- a/src/account-server/accounthandler.hpp +++ b/src/account-server/accounthandler.hpp @@ -67,6 +67,9 @@ class AccountHandler : public ConnectionHandler handleLogoutMessage(AccountClient &computer); void + handleReconnectMessage(AccountClient &computer, MessageIn &msg); + + void handleRegisterMessage(AccountClient &computer, MessageIn &msg); void diff --git a/src/account-server/dalstorage.cpp b/src/account-server/dalstorage.cpp index c4dad80f..5cc8dd0f 100644 --- a/src/account-server/dalstorage.cpp +++ b/src/account-server/dalstorage.cpp @@ -155,9 +155,10 @@ DALStorage::open(void) // we will stick with strategy2 for the moment as we are focusing // on SQLite. - // FIXME: The tables should be checked/created at startup in order to avoid - // a DbSqlQueryExecFailure assert on sqlite while registering. - // Also, this would initialize connection to the database earlier in memory. + // FIXME: The tables should be checked/created at startup in order to + // avoid a DbSqlQueryExecFailure assert on sqlite while registering. + // Also, this would initialize connection to the database earlier in + // memory. createTable(ACCOUNTS_TBL_NAME, SQL_ACCOUNTS_TABLE); createTable(CHARACTERS_TBL_NAME, SQL_CHARACTERS_TABLE); @@ -167,7 +168,7 @@ DALStorage::open(void) createTable(CHANNELS_TBL_NAME, SQL_CHANNELS_TABLE); } catch (const DbConnectionFailure& e) { - LOG_ERROR("unable to connect to the database: " << e.what()); + LOG_ERROR("Unable to connect to the database: " << e.what()); } catch (const DbSqlQueryExecFailure& e) { LOG_ERROR("SQL query failure: " << e.what()); @@ -269,6 +270,85 @@ DALStorage::getAccount(const std::string& userName) } } +/** + * Get an account by ID. + */ +AccountPtr +DALStorage::getAccountByID(int accountID) +{ + // connect to the database (if not connected yet). + open(); + + // look for the account in the list first. + Accounts::iterator it = mAccounts.find(accountID); + + if (it != mAccounts.end()) + return it->second; + + using namespace dal; + + // the account was not in the list, look for it in the database. + try { + std::ostringstream sql; + sql << "select * from " << ACCOUNTS_TBL_NAME << " where id = '" + << accountID << "';"; + const RecordSet& accountInfo = mDb->execSql(sql.str()); + + // if the account is not even in the database then + // we have no choice but to return nothing. + if (accountInfo.isEmpty()) { + return AccountPtr(NULL); + } + + // specialize the string_to functor to convert + // a string to an unsigned int. + string_to< unsigned > toUint; + unsigned id = toUint(accountInfo(0, 0)); + + // create an Account instance + // and initialize it with information about the user. + AccountPtr account(new Account(accountInfo(0, 1), + accountInfo(0, 2), + accountInfo(0, 3), id)); + + mAccounts.insert(std::make_pair(id, account)); + + // load the characters associated with the account. + sql.str(std::string()); + sql << "select id from " << CHARACTERS_TBL_NAME << " where user_id = '" + << accountInfo(0, 0) << "';"; + RecordSet const &charInfo = mDb->execSql(sql.str()); + + if (!charInfo.isEmpty()) + { + int size = charInfo.rows(); + Players players; + + LOG_DEBUG("AccountID: "<< accountID << "; has " << size + << " character(s) in database."); + + // Two steps: it seems like multiple requests cannot be alive at the same time. + std::vector< unsigned > playerIDs; + for (int k = 0; k < size; ++k) + { + playerIDs.push_back(toUint(charInfo(k, 0))); + } + + for (int k = 0; k < size; ++k) + { + players.push_back(getCharacter(playerIDs[k])); + } + + account->setCharacters(players); + } + + return account; + } + catch (const DbSqlQueryExecFailure& e) + { + return AccountPtr(NULL); // TODO: Throw exception here + } +} /** * Gets a character by database ID. @@ -310,6 +390,7 @@ PlayerPtr DALStorage::getCharacter(int id) string_to< unsigned short > toUshort; PlayerData *player = new PlayerData(charInfo(0, 2), toUint(charInfo(0, 0))); + player->setAccountID(toUint(charInfo(0, 1))); player->setGender(toUshort(charInfo(0, 3))); player->setHairStyle(toUshort(charInfo(0, 4))); player->setHairColor(toUshort(charInfo(0, 5))); diff --git a/src/account-server/dalstorage.hpp b/src/account-server/dalstorage.hpp index d2633e23..674b67f6 100644 --- a/src/account-server/dalstorage.hpp +++ b/src/account-server/dalstorage.hpp @@ -66,6 +66,16 @@ class DALStorage: public Storage getAccount(const std::string& userName); /** + * Get an account by ID. + * + * @param accountID the ID of the account. + * + * @return the account associated with the ID. + */ + AccountPtr + getAccountByID(int accountID); + + /** * Gets a character by database ID. * * @param id the ID of the character. diff --git a/src/account-server/serverhandler.cpp b/src/account-server/serverhandler.cpp index 2340d581..e4ec218f 100644 --- a/src/account-server/serverhandler.cpp +++ b/src/account-server/serverhandler.cpp @@ -31,6 +31,9 @@ #include "net/netcomputer.hpp" #include "utils/logger.h" +extern void registerAccountReconnect(int accountID, + const std::string &magic_token); + bool ServerHandler::startListen(enet_uint16 port) { LOG_INFO("Game server handler started:"); @@ -91,6 +94,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) { case GAMSG_REGISTER: { + LOG_DEBUG("GAMSG_REGISTER"); // TODO: check the credentials of the game server std::string address = msg.readString(); int port = msg.readShort(); @@ -118,6 +122,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) case GAMSG_PLAYER_DATA: { + LOG_DEBUG("GAMSG_PLAYER_DATA"); int id = msg.readLong(); Storage &store = Storage::instance("tmw"); PlayerPtr ptr = store.getCharacter(id); @@ -135,6 +140,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) case GAMSG_REDIRECT: { + LOG_DEBUG("GAMSG_REDIRECT"); int id = msg.readLong(); std::string magic_token(32, ' '); for (int i = 0; i < 32; ++i) @@ -162,8 +168,23 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) } } break; + case GAMSG_PLAYER_RECONNECT: + { + LOG_DEBUG("GAMSG_PLAYER_RECONNECT"); + int characterID = msg.readLong(); + std::string magic_token = msg.readString(32); + + Storage &store = Storage::instance("tmw"); + PlayerPtr ptr = store.getCharacter(characterID); + + int accountID = ptr->getAccountID(); + registerAccountReconnect(accountID, magic_token); + + } break; + default: - LOG_WARN("Invalid message type: " << msg.getId()); + LOG_WARN("ServerHandler::processMessage, Invalid message type: " + << msg.getId()); result.writeShort(XXMSG_INVALID); break; } diff --git a/src/account-server/storage.hpp b/src/account-server/storage.hpp index d530f6f2..031acf4d 100644 --- a/src/account-server/storage.hpp +++ b/src/account-server/storage.hpp @@ -173,6 +173,16 @@ class Storage getAccount(const std::string& userName) = 0; /** + * Get an account by ID. + * + * @param accountID the ID of the account. + * + * @return the account associated with the ID. + */ + virtual AccountPtr + getAccountByID(int accountID) = 0; + + /** * Gets a character by database ID. * * @param id the ID of the character. |