summaryrefslogtreecommitdiff
path: root/src/utils
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 /src/utils
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.
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/tokencollector.hpp326
1 files changed, 326 insertions, 0 deletions
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