summaryrefslogtreecommitdiffstats
path: root/libkdegames/kgame/kmessageserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libkdegames/kgame/kmessageserver.cpp')
-rw-r--r--libkdegames/kgame/kmessageserver.cpp515
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"