summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRogier Polak <rogier.l.a.polak@gmail.com>2007-03-18 18:10:31 +0000
committerRogier Polak <rogier.l.a.polak@gmail.com>2007-03-18 18:10:31 +0000
commita50c2a7b04c3778a9aa5962ef09ec73e12af228b (patch)
tree3ebf72bf6dd9bfd301c04899c4f99c99b8829aec
parent7e1011ccea542f4bc972c0a9c03eaca4a718566b (diff)
downloadmanaserv-a50c2a7b04c3778a9aa5962ef09ec73e12af228b.tar.gz
manaserv-a50c2a7b04c3778a9aa5962ef09ec73e12af228b.tar.bz2
manaserv-a50c2a7b04c3778a9aa5962ef09ec73e12af228b.tar.xz
manaserv-a50c2a7b04c3778a9aa5962ef09ec73e12af228b.zip
Added a TokenCollector class for matching magic_tokens.
-rw-r--r--ChangeLog17
-rw-r--r--src/Makefile.am2
-rw-r--r--src/account-server/accountclient.cpp3
-rw-r--r--src/account-server/accountclient.hpp9
-rw-r--r--src/account-server/accounthandler.cpp582
-rw-r--r--src/account-server/accounthandler.hpp48
-rw-r--r--src/account-server/serverhandler.cpp7
-rw-r--r--src/account-server/serverhandler.hpp2
-rw-r--r--src/defines.h3
-rw-r--r--src/game-server/accountconnection.cpp5
-rw-r--r--src/game-server/gamehandler.cpp156
-rw-r--r--src/game-server/gamehandler.hpp50
-rw-r--r--src/net/netcomputer.cpp9
-rw-r--r--src/utils/tokencollector.hpp326
14 files changed, 828 insertions, 391 deletions
diff --git a/ChangeLog b/ChangeLog
index cf16c3aa..795bf251 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2007-03-18 Rogier Polak <rogier.l.a.polak@gmail.com>
+
+ * src/net/netcomputer.cpp: Corrected the debug message for big-endian
+ architectures.
+ * src/defines.h, src/Makefile.am,
+ src/account-server/accountclient.hpp,
+ src/account-server/accountclient.cpp,
+ src/account-server/accounthandler.hpp,
+ src/account-server/accounthandler.cpp,
+ src/account-server/serverhandler.hpp,
+ src/account-server/serverhandler.cpp,
+ src/game-server/accountconnection.cpp,
+ src/game-server/gamehandler.hpp, src/game-server/gamehandler.cpp,
+ src/utils/tokencollector.hpp: Added a TokenCollector class, which
+ matches tokens used for moving clients between servers. Improved the
+ handling of connecting clients for the account-server.
+
2007-03-15 Philipp Sehmisch <tmw@crushnet.org>
* src/account-server/accounthandler.cpp: Set the default map
diff --git a/src/Makefile.am b/src/Makefile.am
index 6deadc84..3298bdf0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,6 +60,7 @@ tmwserv_account_SOURCES = \
utils/processorutils.cpp \
utils/stringfilter.h \
utils/stringfilter.cpp \
+ utils/tokencollector.hpp \
utils/tokendispenser.hpp \
utils/tokendispenser.cpp
@@ -130,6 +131,7 @@ tmwserv_game_SOURCES = \
utils/stringfilter.cpp \
utils/timer.h \
utils/timer.cpp \
+ utils/tokencollector.hpp \
utils/tokendispenser.hpp \
utils/tokendispenser.cpp \
utils/xml.hpp \
diff --git a/src/account-server/accountclient.cpp b/src/account-server/accountclient.cpp
index 8be44289..c17e95a8 100644
--- a/src/account-server/accountclient.cpp
+++ b/src/account-server/accountclient.cpp
@@ -30,7 +30,8 @@
AccountClient::AccountClient(ENetPeer *peer):
NetComputer(peer),
mAccountPtr(NULL),
- mCharacterPtr(NULL)
+ mCharacterPtr(NULL),
+ status(CLIENT_LOGIN)
{
}
diff --git a/src/account-server/accountclient.hpp b/src/account-server/accountclient.hpp
index eb88016c..a28c46dd 100644
--- a/src/account-server/accountclient.hpp
+++ b/src/account-server/accountclient.hpp
@@ -32,6 +32,13 @@
class AccountHandler;
+enum
+{
+ CLIENT_LOGIN = 0,
+ CLIENT_CONNECTED,
+ CLIENT_QUEQUED
+};
+
/**
* A connected computer that can have an account and character associated with
* it.
@@ -85,6 +92,8 @@ class AccountClient : public NetComputer
CharacterPtr
getCharacter() const { return mCharacterPtr; }
+ int status;
+
private:
/** Account associated with connection */
AccountPtr mAccountPtr;
diff --git a/src/account-server/accounthandler.cpp b/src/account-server/accounthandler.cpp
index a77516ce..423cd586 100644
--- a/src/account-server/accounthandler.cpp
+++ b/src/account-server/accounthandler.cpp
@@ -38,26 +38,13 @@
#include "net/netcomputer.hpp"
#include "utils/logger.h"
#include "utils/stringfilter.h"
+#include "utils/tokencollector.hpp"
#include "utils/tokendispenser.hpp"
-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);
+AccountHandler::AccountHandler():
+ mTokenCollector(this)
+{
+}
bool
AccountHandler::startListen(enet_uint16 port)
@@ -75,7 +62,13 @@ AccountHandler::computerConnected(ENetPeer *peer)
void
AccountHandler::computerDisconnected(NetComputer *comp)
{
- delete comp;
+ AccountClient* computer = static_cast< AccountClient * >(comp);
+
+ if (computer->status == CLIENT_QUEQUED)
+ // Delete it from the pendingClient list
+ mTokenCollector.deletePendingClient(computer);
+
+ delete computer; // ~AccountClient unsets the account
}
/**
@@ -99,8 +92,6 @@ AccountHandler::processMessage(NetComputer *comp, MessageIn &message)
store.open();
#endif
- MessageOut result;
-
switch (message.getId())
{
case PAMSG_LOGIN:
@@ -129,50 +120,13 @@ AccountHandler::processMessage(NetComputer *comp, MessageIn &message)
break;
case PAMSG_EMAIL_CHANGE:
- {
- LOG_DEBUG("Received msg ... PAMSG_EMAIL_CHANGE");
- result.writeShort(APMSG_EMAIL_CHANGE_RESPONSE);
-
- if (computer.getAccount().get() == NULL) {
- result.writeByte(ERRMSG_NO_LOGIN);
- break;
- }
-
- std::string email = message.readString();
- if (!stringFilter->isEmailValid(email))
- {
- result.writeByte(ERRMSG_INVALID_ARGUMENT);
- }
- else if (stringFilter->findDoubleQuotes(email))
- {
- result.writeByte(ERRMSG_INVALID_ARGUMENT);
- }
- else if (store.doesEmailAddressExist(email))
- {
- result.writeByte(EMAILCHG_EXISTS_EMAIL);
- }
- else
- {
- computer.getAccount()->setEmail(email);
- result.writeByte(ERRMSG_OK);
- }
- }
+ LOG_DEBUG("Received msg ... PAMSG_EMAIL_CHANGE");
+ handleEmailChangeMessage(computer, message);
break;
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);
- break;
- }
- else
- {
- result.writeByte(ERRMSG_OK);
- result.writeString(computer.getAccount()->getEmail());
- }
- }
+ LOG_DEBUG("Received msg ... PAMSG_EMAIL_GET");
+ handleEmailGetMessage(computer);
break;
case PAMSG_PASSWORD_CHANGE:
@@ -186,180 +140,101 @@ AccountHandler::processMessage(NetComputer *comp, MessageIn &message)
break;
case PAMSG_CHAR_SELECT:
- {
- LOG_DEBUG("Received msg ... PAMSG_CHAR_SELECT");
- result.writeShort(APMSG_CHAR_SELECT_RESPONSE);
-
- if (computer.getAccount().get() == NULL)
- {
- result.writeByte(ERRMSG_NO_LOGIN);
- break; // not logged in
- }
-
- unsigned char charNum = message.readByte();
-
- Characters &chars = computer.getAccount()->getCharacters();
- // Character ID = 0 to Number of Characters - 1.
- if (charNum >= chars.size()) {
- // invalid char selection
- result.writeByte(ERRMSG_INVALID_ARGUMENT);
- break;
- }
-
- std::string address;
- short port;
- if (!serverHandler->getGameServerFromMap(
- chars[charNum]->getMapId(), address, port))
- {
- result.writeByte(ERRMSG_FAILURE);
- LOG_ERROR("Character Selection: "
- << "No game server for the map.");
- break;
- }
-
- // set character
- computer.setCharacter(chars[charNum]);
- CharacterPtr selectedChar = computer.getCharacter();
- result.writeByte(ERRMSG_OK);
-
- LOG_DEBUG(selectedChar->getName() <<
- " is trying to enter the servers.");
-
- std::string magic_token(utils::getMagicToken());
- result.writeString(magic_token, MAGIC_TOKEN_LENGTH);
- result.writeString(address);
- result.writeShort(port);
- // TODO: get correct address and port for the chat server
- result.writeString(config.getValue("accountServerAddress",
- "localhost"));
- result.writeShort(int(config.getValue("accountServerPort",
- DEFAULT_SERVER_PORT)) + 2);
-
- serverHandler->registerGameClient(magic_token, selectedChar);
- registerChatClient(magic_token, selectedChar->getName(),
- AL_NORMAL);
- }
+ LOG_DEBUG("Received msg ... PAMSG_CHAR_SELECT");
+ handleCharacterSelectMessage(computer, message);
break;
case PAMSG_CHAR_DELETE:
- {
- LOG_DEBUG("Received msg ... PAMSG_CHAR_DELETE");
- result.writeShort(APMSG_CHAR_DELETE_RESPONSE);
-
- if (computer.getAccount().get() == NULL)
- {
- result.writeByte(ERRMSG_NO_LOGIN);
- break; // not logged in
- }
-
- unsigned char charNum = message.readByte();
-
- Characters &chars = computer.getAccount()->getCharacters();
- // Character ID = 0 to Number of Characters - 1.
- if (charNum >= chars.size()) {
- // invalid char selection
- result.writeByte(ERRMSG_INVALID_ARGUMENT);
- break;
- }
-
- // Delete the character
- // If the character to delete is the current character,
- // get off of it in memory.
- if ( computer.getCharacter().get() != NULL )
- {
- if ( computer.getCharacter()->getName() ==
- chars[charNum].get()->getName() )
- {
- computer.unsetCharacter();
- }
- }
-
- std::string deletedCharacter = chars[charNum].get()->getName();
- computer.getAccount()->delCharacter(deletedCharacter);
- store.flush(computer.getAccount());
- LOG_INFO(deletedCharacter << ": Character deleted...");
- result.writeByte(ERRMSG_OK);
-
- }
+ LOG_DEBUG("Received msg ... PAMSG_CHAR_DELETE");
+ handleCharacterDeleteMessage(computer, message);
break;
default:
LOG_WARN("AccountHandler::processMessage, Invalid message type "
<< message.getId());
- result.writeShort(XXMSG_INVALID);
+ MessageOut result(XXMSG_INVALID);
+ computer.send(result);
break;
}
-
- // return result
- if (result.getLength() > 0)
- computer.send(result);
}
void
AccountHandler::handleLoginMessage(AccountClient &computer, MessageIn &msg)
{
- unsigned long clientVersion = msg.readLong();
- std::string username = msg.readString();
- std::string password = msg.readString();
-
MessageOut reply(APMSG_LOGIN_RESPONSE);
+ if (computer.status != CLIENT_LOGIN)
+ {
+ reply.writeByte(ERRMSG_FAILURE);
+ computer.send(reply);
+ return;
+ }
+
+ unsigned long clientVersion = msg.readLong();
+
if (clientVersion < config.getValue("clientVersion", 0))
{
reply.writeByte(LOGIN_INVALID_VERSION);
+ computer.send(reply);
+ return;
}
+
+ std::string username = msg.readString();
+ std::string password = msg.readString();
+
if (stringFilter->findDoubleQuotes(username))
{
reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ computer.send(reply);
+ return;
}
- if (computer.getAccount().get() != NULL) {
- reply.writeByte(ERRMSG_FAILURE);
- }
+
if (getClientNumber() >= MAX_CLIENTS )
{
reply.writeByte(LOGIN_SERVER_FULL);
+ computer.send(reply);
+ return;
}
- else
+
+ // Check if the account exists
+ Storage &store = Storage::instance("tmw");
+ AccountPtr acc = store.getAccount(username);
+
+ if (!acc.get() || acc->getPassword() != password)
{
- // Check if the account exists
- Storage &store = Storage::instance("tmw");
- AccountPtr acc = store.getAccount(username);
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ computer.send(reply);
+ return;
+ }
- if (!acc.get() || acc->getPassword() != password)
- {
- reply.writeByte(ERRMSG_INVALID_ARGUMENT);
- }
- else
- {
- // Associate account with connection
- computer.setAccount(acc);
+ // Associate account with connection
+ computer.setAccount(acc);
+ computer.status = CLIENT_CONNECTED;
- reply.writeByte(ERRMSG_OK);
- computer.send(reply);
+ reply.writeByte(ERRMSG_OK);
+ computer.send(reply); // Acknowledge login
- // Return information about available characters
- Characters &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_BASE_ATTRIBUTES; ++j)
- charInfo.writeShort(chars[i]->getBaseAttribute(j));
- computer.send(charInfo);
- }
- return;
- }
- }
+ // Return information about available characters
+ Characters &chars = computer.getAccount()->getCharacters();
- computer.send(reply);
+ // 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_BASE_ATTRIBUTES; ++j)
+ charInfo.writeShort(chars[i]->getBaseAttribute(j));
+
+ computer.send(charInfo);
+ }
+ return;
}
void
@@ -367,44 +242,39 @@ AccountHandler::handleLogoutMessage(AccountClient &computer)
{
MessageOut reply(APMSG_LOGOUT_RESPONSE);
- if (computer.getAccount().get() == NULL)
+ if (computer.status == CLIENT_LOGIN)
{
reply.writeByte(ERRMSG_NO_LOGIN);
}
- else
+ else if (computer.status == CLIENT_CONNECTED)
{
computer.unsetAccount();
+ computer.status = CLIENT_LOGIN;
reply.writeByte(ERRMSG_OK);
}
-
+ else if (computer.status == CLIENT_QUEQUED)
+ {
+ // Delete it from the pendingClient list
+ mTokenCollector.deletePendingClient(&computer);
+ // deletePendingClient makes sure that the client get's the message
+ return;
+ }
computer.send(reply);
}
-void
-AccountHandler::handleReconnectMessage(AccountClient &computer, MessageIn &msg)
+void AccountHandler::
+handleReconnectMessage(AccountClient &computer, MessageIn &msg)
{
- if (computer.getAccount().get() == NULL)
+ if (computer.status != CLIENT_LOGIN)
{
- std::string magic_token = msg.readString(MAGIC_TOKEN_LENGTH);
- 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
- }
- // Gameserver communication was faster, connect the client
- int accountID = i->second;
- pendingReconnects.erase(i);
- handleReconnectedAccount(computer, accountID);
+ LOG_DEBUG("Account tried to reconnect, but was allready logged in "
+ << "or quequed.");
return;
}
- LOG_WARN("Account tried to reconnect, but was allready logged in.");
+
+ std::string magic_token = msg.readString(MAGIC_TOKEN_LENGTH);
+ computer.status = CLIENT_QUEQUED; // Before the addPendingClient
+ mTokenCollector.addPendingClient(magic_token, &computer);
}
void
@@ -417,7 +287,11 @@ AccountHandler::handleRegisterMessage(AccountClient &computer, MessageIn &msg)
MessageOut reply(APMSG_REGISTER_RESPONSE);
- if (clientVersion < config.getValue("clientVersion", 0))
+ if (computer.status != CLIENT_LOGIN)
+ {
+ reply.writeByte(ERRMSG_FAILURE);
+ }
+ else if (clientVersion < config.getValue("clientVersion", 0))
{
reply.writeByte(REGISTER_INVALID_VERSION);
}
@@ -471,6 +345,7 @@ AccountHandler::handleRegisterMessage(AccountClient &computer, MessageIn &msg)
// Associate account with connection
computer.setAccount(acc);
+ computer.status = CLIENT_CONNECTED;
}
}
@@ -487,40 +362,101 @@ AccountHandler::handleUnregisterMessage(AccountClient &computer,
MessageOut reply(APMSG_UNREGISTER_RESPONSE);
+ if (computer.status != CLIENT_LOGIN)
+ {
+ reply.writeByte(ERRMSG_FAILURE);
+ computer.send(reply);
+ return;
+ }
+
if (stringFilter->findDoubleQuotes(username))
{
reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ computer.send(reply);
+ return;
+ }
+
+ // See if the account exists
+ Storage &store = Storage::instance("tmw");
+ AccountPtr accPtr = store.getAccount(username);
+
+ if (!accPtr.get() || accPtr->getPassword() != password)
+ {
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ computer.send(reply);
+ return;
+ }
+
+ // Delete account and associated characters
+ LOG_DEBUG("Unregistered \"" << username
+ << "\", AccountID: " << accPtr->getID());
+ store.delAccount(accPtr);
+ reply.writeByte(ERRMSG_OK);
+
+ // If the account to delete is the current account we're loggedin
+ // on, get out of it in memory.
+ if (computer.getAccount().get() != NULL &&
+ computer.getAccount()->getName() == username)
+ {
+ computer.unsetAccount();
+ computer.status = CLIENT_LOGIN;
+ }
+ computer.send(reply);
+}
+
+void AccountHandler::
+handleEmailChangeMessage(AccountClient &computer, MessageIn &msg)
+{
+ MessageOut reply(APMSG_EMAIL_CHANGE_RESPONSE);
+
+ if (computer.status != CLIENT_CONNECTED ||
+ computer.getAccount().get() == NULL)
+ {
+ reply.writeByte(ERRMSG_NO_LOGIN);
+ computer.send(reply);
+ return;
+ }
+
+ std::string email = msg.readString();
+
+ Storage &store = Storage::instance("tmw");
+
+ if (!stringFilter->isEmailValid(email))
+ {
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if (stringFilter->findDoubleQuotes(email))
+ {
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ }
+ else if (store.doesEmailAddressExist(email))
+ {
+ reply.writeByte(EMAILCHG_EXISTS_EMAIL);
}
else
{
- // 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);
+ computer.getAccount()->setEmail(email);
+ reply.writeByte(ERRMSG_OK);
+ }
+ computer.send(reply);
+}
- // If the account to delete is the current account we're loggedin
- // on, get out of it in memory.
- if (computer.getAccount().get() != NULL )
- {
- if (computer.getAccount()->getName() == username)
- {
- computer.unsetAccount();
- }
- }
- }
+void AccountHandler::
+handleEmailGetMessage(AccountClient &computer)
+{
+ MessageOut reply(APMSG_EMAIL_GET_RESPONSE);
+
+ if (computer.status != CLIENT_CONNECTED ||
+ computer.getAccount().get() == NULL)
+ {
+ reply.writeByte(ERRMSG_NO_LOGIN);
+ computer.send(reply);
+ return;
}
+ reply.writeByte(ERRMSG_OK);
+ reply.writeString(computer.getAccount()->getEmail());
+
computer.send(reply);
}
@@ -533,7 +469,8 @@ AccountHandler::handlePasswordChangeMessage(AccountClient &computer,
MessageOut reply(APMSG_PASSWORD_CHANGE_RESPONSE);
- if (computer.getAccount().get() == NULL)
+ if (computer.status != CLIENT_CONNECTED ||
+ computer.getAccount().get() == NULL)
{
reply.writeByte(ERRMSG_NO_LOGIN);
}
@@ -570,7 +507,9 @@ AccountHandler::handleCharacterCreateMessage(AccountClient &computer,
MessageOut reply(APMSG_CHAR_CREATE_RESPONSE);
- if (computer.getAccount().get() == NULL) {
+ if (computer.status != CLIENT_CONNECTED ||
+ computer.getAccount().get() == NULL)
+ {
reply.writeByte(ERRMSG_NO_LOGIN);
}
else if (!stringFilter->filterContent(name))
@@ -703,24 +642,111 @@ AccountHandler::handleCharacterCreateMessage(AccountClient &computer,
computer.send(reply);
}
-void
-registerAccountReconnect(int accountID, const std::string& magic_token)
+void AccountHandler::
+handleCharacterSelectMessage(AccountClient &computer, MessageIn &msg)
{
- AccountPendingClients::iterator i = pendingClients.find(magic_token);
- if (i == pendingClients.end())
+ MessageOut reply(APMSG_CHAR_SELECT_RESPONSE);
+
+ if (computer.status != CLIENT_CONNECTED ||
+ computer.getAccount().get() == NULL)
{
- pendingReconnects.insert(std::make_pair(magic_token, accountID));
+ reply.writeByte(ERRMSG_NO_LOGIN);
+ computer.send(reply);
+ return; // not logged in
}
- else
+
+ unsigned char charNum = msg.readByte();
+ Characters &chars = computer.getAccount()->getCharacters();
+
+ // Character ID = 0 to Number of Characters - 1.
+ if (charNum >= chars.size())
+ {
+ // invalid char selection
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ computer.send(reply);
+ return;
+ }
+
+ std::string address;
+ short port;
+ if (!serverHandler->getGameServerFromMap(
+ chars[charNum]->getMapId(), address, port))
+ {
+ LOG_ERROR("Character Selection: No game server for the map.");
+ reply.writeByte(ERRMSG_FAILURE);
+ computer.send(reply);
+ return;
+ }
+
+ // set character
+ computer.setCharacter(chars[charNum]);
+ CharacterPtr selectedChar = computer.getCharacter();
+ reply.writeByte(ERRMSG_OK);
+
+ LOG_DEBUG(selectedChar->getName() << " is trying to enter the servers.");
+
+ std::string magic_token(utils::getMagicToken());
+ reply.writeString(magic_token, MAGIC_TOKEN_LENGTH);
+ reply.writeString(address);
+ reply.writeShort(port);
+
+ // TODO: get correct address and port for the chat server
+ reply.writeString(config.getValue("accountServerAddress", "localhost"));
+ reply.writeShort(int(config.getValue("accountServerPort",
+ DEFAULT_SERVER_PORT)) + 2);
+
+ serverHandler->registerGameClient(magic_token, selectedChar);
+ registerChatClient(magic_token, selectedChar->getName(), AL_NORMAL);
+
+ computer.send(reply);
+}
+
+void AccountHandler::
+handleCharacterDeleteMessage(AccountClient &computer, MessageIn &msg)
+{
+ MessageOut reply(APMSG_CHAR_DELETE_RESPONSE);
+
+ if (computer.status != CLIENT_CONNECTED ||
+ computer.getAccount().get() == NULL)
+ {
+ reply.writeByte(ERRMSG_NO_LOGIN);
+ computer.send(reply);
+ return; // not logged in
+ }
+
+ unsigned char charNum = msg.readByte();
+ Characters &chars = computer.getAccount()->getCharacters();
+
+ // Character ID = 0 to Number of Characters - 1.
+ if (charNum >= chars.size())
{
- AccountClient &computer = *(i->second);
- handleReconnectedAccount(computer, accountID);
- pendingClients.erase(i);
+ // invalid char selection
+ reply.writeByte(ERRMSG_INVALID_ARGUMENT);
+ computer.send(reply);
+ return; // not logged in
}
+
+ // Delete the character. If the character to delete is the current
+ // character, get off of it in memory.
+ if (computer.getCharacter().get() != NULL &&
+ computer.getCharacter()->getName() == chars[charNum].get()->getName())
+ computer.unsetCharacter();
+
+ std::string deletedCharacter = chars[charNum].get()->getName();
+ computer.getAccount()->delCharacter(deletedCharacter);
+
+ Storage &store = Storage::instance("tmw");
+ store.flush(computer.getAccount());
+
+ LOG_INFO(deletedCharacter << ": Character deleted...");
+
+ reply.writeByte(ERRMSG_OK);
+
+ computer.send(reply);
}
void
-handleReconnectedAccount(AccountClient &computer, int accountID)
+AccountHandler::tokenMatched(AccountClient *computer, int accountID)
{
MessageOut reply(APMSG_RECONNECT_RESPONSE);
@@ -729,13 +755,14 @@ handleReconnectedAccount(AccountClient &computer, int accountID)
AccountPtr acc = store.getAccountByID(accountID);
// Associate account with connection
- computer.setAccount(acc);
+ computer->setAccount(acc);
+ computer->status = CLIENT_CONNECTED;
reply.writeByte(ERRMSG_OK);
- computer.send(reply);
+ computer->send(reply);
// Return information about available characters
- Characters &chars = computer.getAccount()->getCharacters();
+ Characters &chars = computer->getAccount()->getCharacters();
// Send characters list
for (unsigned int i = 0; i < chars.size(); i++)
@@ -753,6 +780,25 @@ handleReconnectedAccount(AccountClient &computer, int accountID)
{
charInfo.writeShort(chars[i]->getBaseAttribute(j));
}
- computer.send(charInfo);
+ computer->send(charInfo);
}
}
+
+void
+AccountHandler::deletePendingClient(AccountClient* computer)
+{
+ // Something might have changed since it was inserted
+ if (computer->status != CLIENT_QUEQUED) return;
+
+ MessageOut msg(APMSG_CONNECTION_TIMEDOUT);
+ computer->disconnect(msg);
+ // The computer will be deleted when the disconnect event is processed
+}
+
+void
+AccountHandler::deletePendingConnect(int accountID)
+{
+ // NOOP
+ // No memory was allocated for the PendingConnect (that was not
+ // allocated to a countedPtr).
+}
diff --git a/src/account-server/accounthandler.hpp b/src/account-server/accounthandler.hpp
index da9140e4..1c0de2e4 100644
--- a/src/account-server/accounthandler.hpp
+++ b/src/account-server/accounthandler.hpp
@@ -25,6 +25,7 @@
#define _TMWSERV_ACCOUNTHANDLER_H_
#include "net/connectionhandler.hpp"
+#include "utils/tokencollector.hpp"
class AccountClient;
@@ -40,11 +41,44 @@ class AccountHandler : public ConnectionHandler
{
public:
/**
+ * Constructor
+ */
+ AccountHandler();
+
+ /**
* Start the handler
*/
bool
startListen(enet_uint16 port);
+ /**
+ * Combines a client with it's account.
+ * (Needed for TokenCollector)
+ */
+ void
+ tokenMatched(AccountClient *computer, int accountID);
+
+ /**
+ * Deletes a pending client's data.
+ * (Needed for TokenCollector)
+ */
+ void
+ deletePendingClient(AccountClient* computer);
+
+ /**
+ * Deletes a pending connection's data.
+ * (Needed for TokenCollector)
+ */
+ void
+ deletePendingConnect(int accountID);
+
+ /**
+ * TokenCollector, used to login a client without the client having to
+ * send username and password a second time.
+ */
+ TokenCollector<AccountHandler, AccountClient*, int>
+ mTokenCollector;
+
protected:
/**
* Process account related messages.
@@ -76,10 +110,24 @@ class AccountHandler : public ConnectionHandler
handleUnregisterMessage(AccountClient &computer, MessageIn &msg);
void
+ handleEmailChangeMessage(AccountClient &computer, MessageIn &msg);
+
+ void
+ handleEmailGetMessage(AccountClient &computer);
+
+ void
handlePasswordChangeMessage(AccountClient &computer, MessageIn &msg);
void
handleCharacterCreateMessage(AccountClient &computer, MessageIn &msg);
+
+ void
+ handleCharacterSelectMessage(AccountClient &computer, MessageIn &msg);
+
+ void
+ handleCharacterDeleteMessage(AccountClient &computer, MessageIn &msg);
};
+extern AccountHandler * accountHandler;
+
#endif
diff --git a/src/account-server/serverhandler.cpp b/src/account-server/serverhandler.cpp
index 53cc53f5..b1fba564 100644
--- a/src/account-server/serverhandler.cpp
+++ b/src/account-server/serverhandler.cpp
@@ -32,9 +32,7 @@
#include "net/netcomputer.hpp"
#include "utils/logger.h"
#include "utils/tokendispenser.hpp"
-
-extern void registerAccountReconnect(int accountID,
- const std::string &magic_token);
+#include "utils/tokencollector.hpp"
bool ServerHandler::startListen(enet_uint16 port)
{
@@ -173,7 +171,8 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg)
CharacterPtr ptr = store.getCharacter(characterID);
int accountID = ptr->getAccountID();
- registerAccountReconnect(accountID, magic_token);
+ accountHandler->
+ mTokenCollector.addPendingConnect(magic_token, accountID);
} break;
diff --git a/src/account-server/serverhandler.hpp b/src/account-server/serverhandler.hpp
index cc269165..9de15a89 100644
--- a/src/account-server/serverhandler.hpp
+++ b/src/account-server/serverhandler.hpp
@@ -25,7 +25,7 @@
#define _TMWSERV_SERVERHANDLER_H_
#include <map>
-
+#include "account-server/accounthandler.hpp"
#include "account-server/characterdata.hpp"
#include "net/connectionhandler.hpp"
#include "utils/countedptr.h"
diff --git a/src/defines.h b/src/defines.h
index c1ad4fb0..9394d3c9 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -142,6 +142,9 @@ enum {
PAMSG_RECONNECT = 0x0065, // B*32 token
APMSG_RECONNECT_RESPONSE = 0x0066, // B error
+ APMSG_CONNECTION_TIMEDOUT = 0x0070, // -
+ GPMSG_CONNECTION_TIMEDOUT = 0x0071, // -
+
// Game
GPMSG_PLAYER_MAP_CHANGE = 0x0100, // S filename, W x, W y
GPMSG_PLAYER_SERVER_CHANGE = 0x0101, // B*32 token, S game address, W game port
diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp
index dea2e82c..e54605ff 100644
--- a/src/game-server/accountconnection.cpp
+++ b/src/game-server/accountconnection.cpp
@@ -31,8 +31,7 @@
#include "net/messageout.hpp"
#include "utils/logger.h"
#include "utils/tokendispenser.hpp"
-
-extern void registerGameClient(std::string const &, Character *);
+#include "utils/tokencollector.hpp"
bool AccountConnection::start()
{
@@ -70,7 +69,7 @@ void AccountConnection::processMessage(MessageIn &msg)
std::string token = msg.readString(MAGIC_TOKEN_LENGTH);
Character *ptr = new Character(msg);
ptr->setSpeed(150); // TODO
- registerGameClient(token, ptr);
+ gameHandler->mTokenCollector.addPendingConnect(token, ptr);
} break;
case AGMSG_ACTIVE_MAP:
diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp
index 7b82c1a8..b4f9340c 100644
--- a/src/game-server/gamehandler.cpp
+++ b/src/game-server/gamehandler.cpp
@@ -39,78 +39,9 @@
#include "utils/logger.h"
#include "utils/tokendispenser.hpp"
-enum
+GameHandler::GameHandler():
+ mTokenCollector(this)
{
- CLIENT_LOGIN = 0,
- CLIENT_CONNECTED,
- CLIENT_CHANGE_SERVER
-};
-
-struct GameClient: NetComputer
-{
- GameClient(ENetPeer *peer)
- : NetComputer(peer), character(NULL), status(CLIENT_LOGIN) {}
- Character *character;
- int status;
-};
-
-struct GamePendingLogin
-{
- Character *character;
- int timeout;
-};
-
-typedef std::map< std::string, GamePendingLogin > GamePendingLogins;
-typedef std::map< std::string, GameClient * > GamePendingClients;
-
-/**
- * The pending logins represent clients who were given a magic token by the
- * account server but who have not yet logged in to the game server.
- */
-static GamePendingLogins pendingLogins;
-
-/**
- * The pending clients represent clients who tried to login to the game server,
- * but for which no magic token is available yet. This can happen when the
- * communication between the account server and client went faster than the
- * communication between the account server and the game server.
- */
-static GamePendingClients pendingClients;
-
-/**
- * Links a client to a character.
- */
-static void linkCharacter(GameClient *computer, Character *ch)
-{
- computer->character = ch;
- computer->status = CLIENT_CONNECTED;
- ch->setClient(computer);
- MessageOut result(GPMSG_CONNECT_RESPONSE);
- result.writeByte(ERRMSG_OK);
- computer->send(result);
- gameState->insert(ch);
- Inventory(ch).sendFull();
-}
-
-/**
- * Notification that a particular token has been given to allow a certain
- * character to enter the game.
- */
-void registerGameClient(std::string const &token, Character *ch)
-{
- GamePendingClients::iterator i = pendingClients.find(token);
- if (i != pendingClients.end())
- {
- linkCharacter(i->second, ch);
- pendingClients.erase(i);
- }
- else
- {
- GamePendingLogin p;
- p.character = ch;
- p.timeout = 300; // world ticks
- pendingLogins.insert(std::make_pair(token, p));
- }
}
bool GameHandler::startListen(enet_uint16 port)
@@ -124,23 +55,20 @@ NetComputer *GameHandler::computerConnected(ENetPeer *peer)
return new GameClient(peer);
}
-void GameHandler::computerDisconnected(NetComputer *computer)
+void GameHandler::computerDisconnected(NetComputer *comp)
{
- for (GamePendingClients::iterator i = pendingClients.begin(),
- i_end = pendingClients.end(); i != i_end; ++i)
+ GameClient &computer = *static_cast< GameClient * >(comp);
+
+ if (computer.status == CLIENT_QUEQUED)
{
- if (i->second == computer)
- {
- pendingClients.erase(i);
- break;
- }
+ mTokenCollector.deletePendingClient(&computer);
}
- if (Character *ch = static_cast< GameClient * >(computer)->character)
+ else if (Character *ch = computer.character)
{
gameState->remove(ch);
delete ch;
}
- delete computer;
+ delete &computer;
}
void GameHandler::kill(Character *ch)
@@ -184,21 +112,6 @@ void GameHandler::completeServerChange(int id, std::string const &token,
void GameHandler::process()
{
ConnectionHandler::process();
-
- // Removes characters that have been left unconnected for too long.
- GamePendingLogins::iterator i = pendingLogins.begin();
- while (i != pendingLogins.end())
- {
- if (--i->second.timeout <= 0)
- {
- delete i->second.character;
- pendingLogins.erase(i++);
- }
- else
- {
- ++i;
- }
- }
}
void GameHandler::processMessage(NetComputer *comp, MessageIn &message)
@@ -209,20 +122,10 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message)
if (computer.status == CLIENT_LOGIN)
{
if (message.getId() != PGMSG_CONNECT) return;
+
std::string magic_token = message.readString(MAGIC_TOKEN_LENGTH);
- GamePendingLogins::iterator i = pendingLogins.find(magic_token);
- if (i == pendingLogins.end())
- {
- for (GamePendingClients::iterator j = pendingClients.begin(),
- j_end = pendingClients.end(); j != j_end; ++j)
- {
- if (j->second == &computer) return;
- }
- pendingClients.insert(std::make_pair(magic_token, &computer));
- return;
- }
- linkCharacter(&computer, i->second.character);
- pendingLogins.erase(i);
+ mTokenCollector.addPendingClient(magic_token, &computer);
+ computer.status == CLIENT_QUEQUED;
return;
}
else if (computer.status != CLIENT_CONNECTED)
@@ -371,3 +274,38 @@ void GameHandler::sendTo(Character *beingPtr, MessageOut &msg)
assert(client && client->status == CLIENT_CONNECTED);
client->send(msg);
}
+
+void
+GameHandler::tokenMatched(GameClient* computer, Character* character)
+{
+ computer->character = character;
+ computer->status = CLIENT_CONNECTED;
+
+ character->setClient(computer);
+
+ MessageOut result(GPMSG_CONNECT_RESPONSE);
+ result.writeByte(ERRMSG_OK);
+ computer->send(result);
+
+ gameState->insert(character);
+
+ Inventory(character).sendFull();
+}
+
+void
+GameHandler::deletePendingClient(GameClient* computer)
+{
+ // Something might have changed since it was inserted
+ if (computer->status != CLIENT_QUEQUED) return;
+
+ MessageOut msg(GPMSG_CONNECTION_TIMEDOUT);
+
+ // The computer will be deleted when the disconnect event is processed
+ computer->disconnect(msg);
+}
+
+void
+GameHandler::deletePendingConnect(Character* character)
+{
+ delete character;
+}
diff --git a/src/game-server/gamehandler.hpp b/src/game-server/gamehandler.hpp
index 9f76e865..312043f2 100644
--- a/src/game-server/gamehandler.hpp
+++ b/src/game-server/gamehandler.hpp
@@ -26,6 +26,24 @@
#include "game-server/character.hpp"
#include "net/connectionhandler.hpp"
+#include "net/netcomputer.hpp"
+#include "utils/tokencollector.hpp"
+
+enum
+{
+ CLIENT_LOGIN = 0,
+ CLIENT_CONNECTED,
+ CLIENT_CHANGE_SERVER,
+ CLIENT_QUEQUED
+};
+
+struct GameClient: NetComputer
+{
+ GameClient(ENetPeer *peer)
+ : NetComputer(peer), character(NULL), status(CLIENT_LOGIN) {}
+ Character *character;
+ int status;
+};
/**
* Manages connections to game client.
@@ -34,6 +52,10 @@ class GameHandler: public ConnectionHandler
{
public:
/**
+ * Constructor
+ */
+ GameHandler();
+ /**
* Processes messages and cleans outdated characters.
*/
void process();
@@ -64,6 +86,34 @@ class GameHandler: public ConnectionHandler
void completeServerChange(int id, std::string const &token,
std::string const &address, int port);
+ /**
+ * Combines a client with it's character.
+ * (Needed for TokenCollector)
+ */
+ void
+ tokenMatched(GameClient* computer, Character* character);
+
+ /**
+ * Deletes a pending client's data.
+ * (Needed for TokenCollector)
+ */
+ void
+ deletePendingClient(GameClient* computer);
+
+ /**
+ * Deletes a pending connection's data.
+ * (Needed for TokenCollector)
+ */
+ void
+ deletePendingConnect(Character* character);
+
+ /**
+ * TokenCollector, used to match a gameclient with the data received
+ * from the accountserver.
+ */
+ TokenCollector<GameHandler, GameClient*, Character*>
+ mTokenCollector;
+
protected:
NetComputer *computerConnected(ENetPeer *);
void computerDisconnected(NetComputer *);
diff --git a/src/net/netcomputer.cpp b/src/net/netcomputer.cpp
index f0b677ec..d35779da 100644
--- a/src/net/netcomputer.cpp
+++ b/src/net/netcomputer.cpp
@@ -92,11 +92,10 @@ operator <<(std::ostream &os, const NetComputer &comp)
<< ((comp.mPeer->address.host & 0xff000000) >> 24);
else
// big-endian
- // TODO: test this
- os << ((comp.mPeer->address.host & 0x000000ff) << 24) << "."
- << ((comp.mPeer->address.host & 0x0000ff00) << 16) << "."
- << ((comp.mPeer->address.host & 0x00ff0000) << 8) << "."
- << ((comp.mPeer->address.host & 0xff000000) << 0);
+ os << ((comp.mPeer->address.host & 0xff000000) >> 24) << "."
+ << ((comp.mPeer->address.host & 0x00ff0000) >> 16) << "."
+ << ((comp.mPeer->address.host & 0x0000ff00) >> 8) << "."
+ << ((comp.mPeer->address.host & 0x000000ff) >> 0);
return os;
}
diff --git a/src/utils/tokencollector.hpp b/src/utils/tokencollector.hpp
new file mode 100644
index 00000000..036ffe36
--- /dev/null
+++ b/src/utils/tokencollector.hpp
@@ -0,0 +1,326 @@
+/*
+ * The Mana World Server
+ * Copyright 2007 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$
+ */
+
+#ifndef _TMW_TOKENCOLLECTOR_HPP
+#define _TMW_TOKENCOLLECTOR_HPP
+
+#include <string>
+#include <list>
+#include <time.h>
+
+#include "utils/logger.h"
+
+#define NB_ACTIONS_BETWEEN_OUTDATED_CHECKS 100
+
+/**
+ * Because the timeStamp is not updated for every new list item and the
+ * removeOutdated function is not called on regular intervals, a list item
+ * might persist significantly longer then this.
+ */
+#define TIMEOUT 10 // in seconds
+
+
+/**
+ * A structure used for the list items,
+ * prefixed with 'TC_' to avoid any conflicts.
+ */
+template <typename T>
+struct TC_ListItem
+{
+ /** The magic_token aka passToken */
+ std::string token;
+
+ /** The actual data, which will be sent back when matched */
+ T payload;
+
+ /**
+ * The amount of seconds since TokenCollector was running,
+ * at the last outdated check.
+ */
+ time_t timeStamp;
+};
+
+// Prototype
+
+/**
+ * \brief A class for storing and matching magic_tokens
+ *
+ * T is used for the class of the owner, U is the client payload,
+ * V is the connect payload
+ *
+ * The owning class must implement as it's member functions;
+ * - destroyPendingClient(U clientPayload),
+ * - destroyPendingConnect(V connectPayload) and
+ * - tokenMatched(U clientPayload, V connectPayload),
+ * in a way that TokenCollector can reach them
+ * (public, or declare TokenCollector a friend class).
+ */
+template <class T, class U, class V>
+class TokenCollector
+{
+ public:
+ /**
+ * Constructor.
+ */
+ TokenCollector(T * owner);
+
+ /**
+ * Destructor.
+ */
+ ~TokenCollector();
+
+ /**
+ * \brief Adds a pending client
+ *
+ * Searches the pending connects for a match.
+ * Calls mOwner->tokenMatched when a match is found.
+ * Inserts in mPendingClients if no match is found.
+ */
+ void addPendingClient(const std::string & token, U clientPayload);
+
+ /**
+ * \brief Removes a pending client from the list
+ *
+ * Searches the pending client for the payload.
+ */
+ void deletePendingClient(U clientPayload);
+
+ /**
+ * \brief Adds a pending connect
+ *
+ * Searches the pending clients for a match.
+ * Calls mOwner->tokenMatched when a match is found.
+ * Inserts in mPendingConnects if no match is found.
+ */
+ void addPendingConnect(const std::string & token, V connectPayload);
+
+ private:
+
+ /**
+ * \brief A simple list of all pending clients
+ *
+ * On a well setup system, this list is mostly empty.
+ * The servers send the magic_token to the other server, before they
+ * send it to the client. In a well setup system, the route
+ * server1 -> server2 is faster then server1 -> client -> server2.
+ *
+ * A std::list is used because the basic work cycle for one client
+ * (search PendingClients, insert PendingConnects, search
+ * PendingConnects, remove pendingConnect) will run in (allmost)
+ * constant time, when the size of PendingClients is small.
+ * This can not be said for a map or sorted vector.
+ *
+ * The list items are pointers to void, because a list can not be
+ * instantiationised with an incomplete type.
+ */
+ std::list< void* > mPendingClients;
+
+ /**
+ * \brief A simple list of all pending clients.
+ *
+ * See mPendingClients for details.
+ */
+ std::list< void* > mPendingConnects;
+
+ /**
+ * \brief The number of actions since the last time that the lists
+ * where checked for outdated pendingClients or
+ * pendingConnects.
+ */
+ int mNumberOfActions;
+
+ /**
+ * \brief The number of seconds between the creation of TokenCollector
+ * and the last removeOutdated.
+ */
+ time_t mTimeNow;
+
+ /**
+ * \brief The time that TokenCollector was created, used for keeping
+ * the variable times low numbers.
+ */
+ time_t mTimeStart;
+
+ /**
+ * \brief Pointer to the owner of this TokenCollector object.
+ */
+ T * mOwner;
+
+ /**
+ * \brief Removes outdated entries.
+ */
+ void removeOutdated();
+};
+
+// Implementation
+
+template <class T, class U, class V>
+TokenCollector<T, U, V>::
+TokenCollector(T * owner):
+ mNumberOfActions(0), mTimeStart(time(NULL)), mTimeNow(0), mOwner(owner)
+{
+}
+
+template <class T, class U, class V>
+TokenCollector<T, U, V>::
+~TokenCollector()
+{
+ if (mPendingClients.size())
+ LOG_INFO("TokenCollector deleted with " <<
+ mPendingClients.size() <<
+ " clients still pending.");
+
+ if (mPendingConnects.size())
+ LOG_INFO("TokenCollector deleted with " <<
+ mPendingConnects.size() <<
+ " connects still pending.");
+}
+
+template <class T, class U, class V>
+void TokenCollector<T, U, V>::
+addPendingClient(const std::string & token, U clientPayload)
+{
+ // Find could also be used for a list, but because new items are
+ // inserted at the top, we want to start looking there.
+ for (std::list< void* >::iterator it = mPendingConnects.begin(),
+ it_end = mPendingConnects.end(); it != it_end; it++)
+ {
+ // Because the pointer to the listItem was stored as a pointer to
+ // void, we have to cast it back to a pointer to a listItem.
+ // Examples: it = void**; *it = void*;
+ // ((TC_ListItem< V >*)*it) = TC_ListItem< V >*;
+ // ---------------------------------------------
+ if (((TC_ListItem< V >*)*it)->token == token) // Found a match
+ {
+ mOwner->tokenMatched(clientPayload,
+ ((TC_ListItem< V >*)*it)->payload);
+ delete ((TC_ListItem< V >*)*it);
+ mPendingConnects.erase(it);
+ return; // Done
+ }
+ }
+
+ TC_ListItem< U >* listItem = new TC_ListItem< U >;
+ listItem->token = token;
+ listItem->payload = clientPayload;
+ listItem->timeStamp = mTimeNow;
+
+ mPendingClients.push_front((void*)listItem);
+
+ if (!(++mNumberOfActions < NB_ACTIONS_BETWEEN_OUTDATED_CHECKS))
+ removeOutdated();
+}
+
+template <class T, class U, class V>
+void TokenCollector<T, U, V>::
+deletePendingClient(U clientPayload)
+{
+ // Find could also be used for a list, but because new items are
+ // inserted at the top, we want to start looking there.
+ for (std::list< void* >::iterator it = mPendingClients.begin(),
+ it_end = mPendingClients.end(); it != it_end; it++)
+ {
+ // Because the pointer to the listItem was stored as a pointer to
+ // void, we have to cast it back to a pointer to a listItem.
+ // Examples: it = void**; *it = void*;
+ // ((TC_ListItem< U >*)*it) = TC_ListItem< U >*;
+ // ---------------------------------------------
+ if (((TC_ListItem< U >*)*it)->payload == clientPayload) // Found a match
+ {
+ delete ((TC_ListItem< U >*)*it);
+ mPendingConnects.erase(it);
+ return; // Done
+ }
+ }
+}
+
+template <class T, class U, class V>
+void TokenCollector<T, U, V>::
+addPendingConnect(const std::string & token, V connectPayload)
+{
+ // Find could also be used for a list, but because new items are
+ // inserted at the top, we want to start looking there.
+ for (std::list< void* >::iterator it = mPendingClients.begin(),
+ it_end = mPendingClients.end(); it != it_end; it++)
+ {
+ // Because the pointer to the listItem was stored as a pointer to
+ // void, we have to cast it back to a pointer to a listItem.
+ // Examples: it = void**; *it = void*;
+ // ((TC_ListItem< U >*)*it) = TC_ListItem< U >*;
+ // ---------------------------------------------
+ if (((TC_ListItem< U >*)*it)->token == token) // Found a match
+ {
+ mOwner->tokenMatched(((TC_ListItem< U >*)*it)->payload,
+ connectPayload);
+ delete ((TC_ListItem< U >*)*it);
+ mPendingClients.erase(it);
+ return; // Done
+ }
+ }
+
+ TC_ListItem< V >* listItem = new TC_ListItem< V >;
+ listItem->token = token;
+ listItem->payload = connectPayload;
+ listItem->timeStamp = mTimeNow;
+
+ mPendingConnects.push_front((void*)listItem);
+
+ if (!(++mNumberOfActions < NB_ACTIONS_BETWEEN_OUTDATED_CHECKS))
+ removeOutdated();
+}
+
+template <class T, class U, class V>
+void TokenCollector<T, U, V>::
+removeOutdated()
+{
+ time_t eraseTime = (mTimeNow > (time_t)TIMEOUT) ?
+ (mTimeNow - (time_t)TIMEOUT) : (time_t) 0;
+
+ // See addPendingClient for a comment about the casting.
+ while ((mPendingClients.size()) &&
+ (((TC_ListItem< U >*)(mPendingClients.back()))->timeStamp < eraseTime))
+ {
+ mOwner->deletePendingClient(
+ ((TC_ListItem< U >*)(mPendingClients.back()))->payload);
+ delete ((TC_ListItem< U >*)(mPendingClients.back()));
+ mPendingClients.pop_back();
+ }
+
+ while ((mPendingConnects.size()) &&
+ (((TC_ListItem< V >*)(mPendingConnects.back()))->timeStamp < eraseTime))
+ {
+ mOwner->deletePendingConnect(
+ ((TC_ListItem< V >*)(mPendingConnects.back()))->payload);
+ delete ((TC_ListItem< U >*)(mPendingConnects.back()));
+ mPendingConnects.pop_back();
+ }
+
+ /**
+ * Change the timeStap after the check, else everything that was just
+ * inserted might be thrown away.
+ */
+ mTimeNow = time(NULL) - mTimeStart;
+
+ mNumberOfActions = 0; // Reset the counter.
+}
+
+#endif // _TMW_TOKENCOLLECTOR_HPP