diff options
Diffstat (limited to 'libkdegames/kgame/kmessageserver.cpp')
-rw-r--r-- | libkdegames/kgame/kmessageserver.cpp | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/libkdegames/kgame/kmessageserver.cpp b/libkdegames/kgame/kmessageserver.cpp new file mode 100644 index 00000000..e857ea31 --- /dev/null +++ b/libkdegames/kgame/kmessageserver.cpp @@ -0,0 +1,515 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qiodevice.h> +#include <qbuffer.h> +#include <qptrlist.h> +#include <qptrqueue.h> +#include <qtimer.h> +#include <qvaluelist.h> + +#include <kdebug.h> + +#include "kmessageio.h" +#include "kmessageserver.h" + +// --------------- internal class KMessageServerSocket + +KMessageServerSocket::KMessageServerSocket (Q_UINT16 port, QObject *parent) + : QServerSocket (port, 0, parent) +{ +} + +KMessageServerSocket::~KMessageServerSocket () +{ +} + +void KMessageServerSocket::newConnection (int socket) +{ + emit newClientConnected (new KMessageSocket (socket)); +} + +// ---------------- class for storing an incoming message + +class MessageBuffer +{ + public: + MessageBuffer (Q_UINT32 clientID, const QByteArray &messageData) + : id (clientID), data (messageData) { } + ~MessageBuffer () {} + Q_UINT32 id; + QByteArray data; +}; + +// ---------------- KMessageServer's private class + +class KMessageServerPrivate +{ +public: + KMessageServerPrivate() + : mMaxClients (-1), mGameId (1), mUniqueClientNumber (1), mAdminID (0), mServerSocket (0) + { + mClientList.setAutoDelete (true); + mMessageQueue.setAutoDelete (true); + } + + int mMaxClients; + int mGameId; + Q_UINT16 mCookie; + Q_UINT32 mUniqueClientNumber; + Q_UINT32 mAdminID; + + KMessageServerSocket* mServerSocket; + + QPtrList <KMessageIO> mClientList; + QPtrQueue <MessageBuffer> mMessageQueue; + QTimer mTimer; + bool mIsRecursive; +}; + + +// ------------------ KMessageServer + +KMessageServer::KMessageServer (Q_UINT16 cookie,QObject* parent) + : QObject(parent, 0) +{ + d = new KMessageServerPrivate; + d->mIsRecursive=false; + d->mCookie=cookie; + connect (&(d->mTimer), SIGNAL (timeout()), + this, SLOT (processOneMessage())); + kdDebug(11001) << "CREATE(KMessageServer=" + << this + << ") cookie=" + << d->mCookie + << " sizeof(this)=" + << sizeof(KMessageServer) + << endl; +} + +KMessageServer::~KMessageServer() +{ + kdDebug(11001) << k_funcinfo << "this=" << this << endl; + Debug(); + stopNetwork(); + deleteClients(); + delete d; + kdDebug(11001) << k_funcinfo << " done" << endl; +} + +//------------------------------------- TCP/IP server stuff + +bool KMessageServer::initNetwork (Q_UINT16 port) +{ + kdDebug(11001) << k_funcinfo << endl; + + if (d->mServerSocket) + { + kdDebug (11001) << k_funcinfo << ": We were already offering connections!" << endl; + delete d->mServerSocket; + } + + d->mServerSocket = new KMessageServerSocket (port); + d->mIsRecursive = false; + + if (!d->mServerSocket || !d->mServerSocket->ok()) + { + kdError(11001) << k_funcinfo << ": Serversocket::ok() == false" << endl; + delete d->mServerSocket; + d->mServerSocket=0; + return false; + } + + kdDebug (11001) << k_funcinfo << ": Now listening to port " + << d->mServerSocket->port() << endl; + connect (d->mServerSocket, SIGNAL (newClientConnected (KMessageIO*)), + this, SLOT (addClient (KMessageIO*))); + return true; +} + +Q_UINT16 KMessageServer::serverPort () const +{ + if (d->mServerSocket) + return d->mServerSocket->port(); + else + return 0; +} + +void KMessageServer::stopNetwork() +{ + if (d->mServerSocket) + { + delete d->mServerSocket; + d->mServerSocket = 0; + } +} + +bool KMessageServer::isOfferingConnections() const +{ + return d->mServerSocket != 0; +} + +//----------------------------------------------- adding / removing clients + +void KMessageServer::addClient (KMessageIO* client) +{ + QByteArray msg; + + // maximum number of clients reached? + if (d->mMaxClients >= 0 && d->mMaxClients <= clientCount()) + { + kdError (11001) << k_funcinfo << ": Maximum number of clients reached!" << endl; + return; + } + + // give it a unique ID + client->setId (uniqueClientNumber()); + kdDebug (11001) << k_funcinfo << ": " << client->id() << endl; + + // connect its signals + connect (client, SIGNAL (connectionBroken()), + this, SLOT (removeBrokenClient())); + connect (client, SIGNAL (received (const QByteArray &)), + this, SLOT (getReceivedMessage (const QByteArray &))); + + // Tell everyone about the new guest + // Note: The new client doesn't get this message! + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_CONNECTED) << client->id(); + broadcastMessage (msg); + + // add to our list + d->mClientList.append (client); + + // tell it its ID + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_ID) << client->id(); + client->send (msg); + + // Give it the complete list of client IDs + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs(); + client->send (msg); + + + if (clientCount() == 1) + { + // if it is the first client, it becomes the admin + setAdmin (client->id()); + } + else + { + // otherwise tell it who is the admin + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID(); + client->send (msg); + } + + emit clientConnected (client); +} + +void KMessageServer::removeClient (KMessageIO* client, bool broken) +{ + Q_UINT32 clientID = client->id(); + if (!d->mClientList.removeRef (client)) + { + kdError(11001) << k_funcinfo << ": Deleting client that wasn't added before!" << endl; + return; + } + + // tell everyone about the removed client + QByteArray msg; + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_DISCONNECTED) << client->id() << (Q_INT8)broken; + broadcastMessage (msg); + + // If it was the admin, select a new admin. + if (clientID == adminID()) + { + if (!d->mClientList.isEmpty()) + setAdmin (d->mClientList.first()->id()); + else + setAdmin (0); + } +} + +void KMessageServer::deleteClients() +{ + d->mClientList.clear(); + d->mAdminID = 0; +} + +void KMessageServer::removeBrokenClient () +{ + if (!sender()->inherits ("KMessageIO")) + { + kdError (11001) << k_funcinfo << ": sender of the signal was not a KMessageIO object!" << endl; + return; + } + + KMessageIO *client = (KMessageIO *) sender(); + + emit connectionLost (client); + removeClient (client, true); +} + + +void KMessageServer::setMaxClients(int c) +{ + d->mMaxClients = c; +} + +int KMessageServer::maxClients() const +{ + return d->mMaxClients; +} + +int KMessageServer::clientCount() const +{ + return d->mClientList.count(); +} + +QValueList <Q_UINT32> KMessageServer::clientIDs () const +{ + QValueList <Q_UINT32> list; + for (QPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter) + list.append ((*iter)->id()); + return list; +} + +KMessageIO* KMessageServer::findClient (Q_UINT32 no) const +{ + if (no == 0) + no = d->mAdminID; + + QPtrListIterator <KMessageIO> iter (d->mClientList); + while (*iter) + { + if ((*iter)->id() == no) + return (*iter); + ++iter; + } + return 0; +} + +Q_UINT32 KMessageServer::adminID () const +{ + return d->mAdminID; +} + +void KMessageServer::setAdmin (Q_UINT32 adminID) +{ + // Trying to set the the client that is already admin => nothing to do + if (adminID == d->mAdminID) + return; + + if (adminID > 0 && findClient (adminID) == 0) + { + kdWarning (11001) << "Trying to set a new admin that doesn't exist!" << endl; + return; + } + + d->mAdminID = adminID; + + QByteArray msg; + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID; + + // Tell everyone about the new master + broadcastMessage (msg); +} + + +//------------------------------------------- ID stuff + +Q_UINT32 KMessageServer::uniqueClientNumber() const +{ + return d->mUniqueClientNumber++; +} + +// --------------------- Messages --------------------------- + +void KMessageServer::broadcastMessage (const QByteArray &msg) +{ + for (QPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter) + (*iter)->send (msg); +} + +void KMessageServer::sendMessage (Q_UINT32 id, const QByteArray &msg) +{ + KMessageIO *client = findClient (id); + if (client) + client->send (msg); +} + +void KMessageServer::sendMessage (const QValueList <Q_UINT32> &ids, const QByteArray &msg) +{ + for (QValueListConstIterator <Q_UINT32> iter = ids.begin(); iter != ids.end(); ++iter) + sendMessage (*iter, msg); +} + +void KMessageServer::getReceivedMessage (const QByteArray &msg) +{ + if (!sender() || !sender()->inherits("KMessageIO")) + { + kdError (11001) << k_funcinfo << ": slot was not called from KMessageIO!" << endl; + return; + } + //kdDebug(11001) << k_funcinfo << ": size=" << msg.size() << endl; + KMessageIO *client = (KMessageIO *) sender(); + Q_UINT32 clientID = client->id(); + + //QByteArray *ta=new QByteArray; + //ta->duplicate(msg); + //d->mMessageQueue.enqueue (new MessageBuffer (clientID, *ta)); + + + d->mMessageQueue.enqueue (new MessageBuffer (clientID, msg)); + if (!d->mTimer.isActive()) + d->mTimer.start(0); // AB: should be , TRUE i guess +} + +void KMessageServer::processOneMessage () +{ + // This shouldn't happen, since the timer should be stopped before. But only to be sure! + if (d->mMessageQueue.isEmpty()) + { + d->mTimer.stop(); + return; + } + if (d->mIsRecursive) + { + return; + } + d->mIsRecursive = true; + + MessageBuffer *msg_buf = d->mMessageQueue.head(); + + Q_UINT32 clientID = msg_buf->id; + QBuffer in_buffer (msg_buf->data); + in_buffer.open (IO_ReadOnly); + QDataStream in_stream (&in_buffer); + + QByteArray out_msg; + QBuffer out_buffer (out_msg); + out_buffer.open (IO_WriteOnly); + QDataStream out_stream (&out_buffer); + + bool unknown = false; + + QByteArray ttt=in_buffer.buffer(); + Q_UINT32 messageID; + in_stream >> messageID; + //kdDebug(11001) << k_funcinfo << ": got message with messageID=" << messageID << endl; + switch (messageID) + { + case REQ_BROADCAST: + out_stream << Q_UINT32 (MSG_BROADCAST) << clientID; + // FIXME, compiler bug? + // this should be okay, since QBuffer is subclass of QIODevice! : + // out_buffer.writeBlock (in_buffer.readAll()); + out_buffer.QIODevice::writeBlock (in_buffer.readAll()); + broadcastMessage (out_msg); + break; + + case REQ_FORWARD: + { + QValueList <Q_UINT32> clients; + in_stream >> clients; + out_stream << Q_UINT32 (MSG_FORWARD) << clientID << clients; + // see above! + out_buffer.QIODevice::writeBlock (in_buffer.readAll()); + sendMessage (clients, out_msg); + } + break; + + case REQ_CLIENT_ID: + out_stream << Q_UINT32 (ANS_CLIENT_ID) << clientID; + sendMessage (clientID, out_msg); + break; + + case REQ_ADMIN_ID: + out_stream << Q_UINT32 (ANS_ADMIN_ID) << d->mAdminID; + sendMessage (clientID, out_msg); + break; + + case REQ_ADMIN_CHANGE: + if (clientID == d->mAdminID) + { + Q_UINT32 newAdmin; + in_stream >> newAdmin; + setAdmin (newAdmin); + } + break; + + case REQ_REMOVE_CLIENT: + if (clientID == d->mAdminID) + { + QValueList <Q_UINT32> client_list; + in_stream >> client_list; + for (QValueListIterator <Q_UINT32> iter = client_list.begin(); iter != client_list.end(); ++iter) + { + KMessageIO *client = findClient (*iter); + if (client) + removeClient (client, false); + else + kdWarning (11001) << k_funcinfo << ": removing non-existing clientID" << endl; + } + } + break; + + case REQ_MAX_NUM_CLIENTS: + if (clientID == d->mAdminID) + { + Q_INT32 maximum_clients; + in_stream >> maximum_clients; + setMaxClients (maximum_clients); + } + break; + + case REQ_CLIENT_LIST: + { + out_stream << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs(); + sendMessage (clientID, out_msg); + } + break; + + default: + unknown = true; + } + + // check if all the data has been used + if (!unknown && !in_buffer.atEnd()) + kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl; + + emit messageReceived (msg_buf->data, clientID, unknown); + + if (unknown) + kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl; + + // remove the message, since we are ready with it + d->mMessageQueue.remove(); + if (d->mMessageQueue.isEmpty()) + d->mTimer.stop(); + d->mIsRecursive = false; +} + +void KMessageServer::Debug() +{ + kdDebug(11001) << "------------------ KMESSAGESERVER -----------------------" << endl; + kdDebug(11001) << "MaxClients : " << maxClients() << endl; + kdDebug(11001) << "NoOfClients : " << clientCount() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +#include "kmessageserver.moc" |