|
|
|
/*
|
|
|
|
This file is part of the TDE 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 <tqiodevice.h>
|
|
|
|
#include <tqbuffer.h>
|
|
|
|
#include <tqptrlist.h>
|
|
|
|
#include <tqptrqueue.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
#include <tqvaluelist.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#include "kmessageio.h"
|
|
|
|
#include "kmessageserver.h"
|
|
|
|
|
|
|
|
// --------------- internal class KMessageServerSocket
|
|
|
|
|
|
|
|
KMessageServerSocket::KMessageServerSocket (TQ_UINT16 port, TQObject *parent)
|
|
|
|
: TQServerSocket (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 (TQ_UINT32 clientID, const TQByteArray &messageData)
|
|
|
|
: id (clientID), data (messageData) { }
|
|
|
|
~MessageBuffer () {}
|
|
|
|
TQ_UINT32 id;
|
|
|
|
TQByteArray 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;
|
|
|
|
TQ_UINT16 mCookie;
|
|
|
|
TQ_UINT32 mUniqueClientNumber;
|
|
|
|
TQ_UINT32 mAdminID;
|
|
|
|
|
|
|
|
KMessageServerSocket* mServerSocket;
|
|
|
|
|
|
|
|
TQPtrList <KMessageIO> mClientList;
|
|
|
|
TQPtrQueue <MessageBuffer> mMessageQueue;
|
|
|
|
TQTimer mTimer;
|
|
|
|
bool mIsRecursive;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------ KMessageServer
|
|
|
|
|
|
|
|
KMessageServer::KMessageServer (TQ_UINT16 cookie,TQObject* parent)
|
|
|
|
: TQObject(parent, 0)
|
|
|
|
{
|
|
|
|
d = new KMessageServerPrivate;
|
|
|
|
d->mIsRecursive=false;
|
|
|
|
d->mCookie=cookie;
|
|
|
|
connect (&(d->mTimer), TQT_SIGNAL (timeout()),
|
|
|
|
this, TQT_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 (TQ_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, TQT_SIGNAL (newClientConnected (KMessageIO*)),
|
|
|
|
this, TQT_SLOT (addClient (KMessageIO*)));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQ_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)
|
|
|
|
{
|
|
|
|
TQByteArray 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, TQT_SIGNAL (connectionBroken()),
|
|
|
|
this, TQT_SLOT (removeBrokenClient()));
|
|
|
|
connect (client, TQT_SIGNAL (received (const TQByteArray &)),
|
|
|
|
this, TQT_SLOT (getReceivedMessage (const TQByteArray &)));
|
|
|
|
|
|
|
|
// Tell everyone about the new guest
|
|
|
|
// Note: The new client doesn't get this message!
|
|
|
|
TQDataStream (msg, IO_WriteOnly) << TQ_UINT32 (EVNT_CLIENT_CONNECTED) << client->id();
|
|
|
|
broadcastMessage (msg);
|
|
|
|
|
|
|
|
// add to our list
|
|
|
|
d->mClientList.append (client);
|
|
|
|
|
|
|
|
// tell it its ID
|
|
|
|
TQDataStream (msg, IO_WriteOnly) << TQ_UINT32 (ANS_CLIENT_ID) << client->id();
|
|
|
|
client->send (msg);
|
|
|
|
|
|
|
|
// Give it the complete list of client IDs
|
|
|
|
TQDataStream (msg, IO_WriteOnly) << TQ_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
|
|
|
|
TQDataStream (msg, IO_WriteOnly) << TQ_UINT32 (ANS_ADMIN_ID) << adminID();
|
|
|
|
client->send (msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
emit clientConnected (client);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMessageServer::removeClient (KMessageIO* client, bool broken)
|
|
|
|
{
|
|
|
|
TQ_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
|
|
|
|
TQByteArray msg;
|
|
|
|
TQDataStream (msg, IO_WriteOnly) << TQ_UINT32 (EVNT_CLIENT_DISCONNECTED) << client->id() << (TQ_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();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQValueList <TQ_UINT32> KMessageServer::clientIDs () const
|
|
|
|
{
|
|
|
|
TQValueList <TQ_UINT32> list;
|
|
|
|
for (TQPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter)
|
|
|
|
list.append ((*iter)->id());
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
KMessageIO* KMessageServer::findClient (TQ_UINT32 no) const
|
|
|
|
{
|
|
|
|
if (no == 0)
|
|
|
|
no = d->mAdminID;
|
|
|
|
|
|
|
|
TQPtrListIterator <KMessageIO> iter (d->mClientList);
|
|
|
|
while (*iter)
|
|
|
|
{
|
|
|
|
if ((*iter)->id() == no)
|
|
|
|
return (*iter);
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQ_UINT32 KMessageServer::adminID () const
|
|
|
|
{
|
|
|
|
return d->mAdminID;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMessageServer::setAdmin (TQ_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;
|
|
|
|
|
|
|
|
TQByteArray msg;
|
|
|
|
TQDataStream (msg, IO_WriteOnly) << TQ_UINT32 (ANS_ADMIN_ID) << adminID;
|
|
|
|
|
|
|
|
// Tell everyone about the new master
|
|
|
|
broadcastMessage (msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------- ID stuff
|
|
|
|
|
|
|
|
TQ_UINT32 KMessageServer::uniqueClientNumber() const
|
|
|
|
{
|
|
|
|
return d->mUniqueClientNumber++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------- Messages ---------------------------
|
|
|
|
|
|
|
|
void KMessageServer::broadcastMessage (const TQByteArray &msg)
|
|
|
|
{
|
|
|
|
for (TQPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter)
|
|
|
|
(*iter)->send (msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMessageServer::sendMessage (TQ_UINT32 id, const TQByteArray &msg)
|
|
|
|
{
|
|
|
|
KMessageIO *client = findClient (id);
|
|
|
|
if (client)
|
|
|
|
client->send (msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMessageServer::sendMessage (const TQValueList <TQ_UINT32> &ids, const TQByteArray &msg)
|
|
|
|
{
|
|
|
|
for (TQValueListConstIterator <TQ_UINT32> iter = ids.begin(); iter != ids.end(); ++iter)
|
|
|
|
sendMessage (*iter, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KMessageServer::getReceivedMessage (const TQByteArray &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();
|
|
|
|
TQ_UINT32 clientID = client->id();
|
|
|
|
|
|
|
|
//TQByteArray *ta=new TQByteArray;
|
|
|
|
//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();
|
|
|
|
|
|
|
|
TQ_UINT32 clientID = msg_buf->id;
|
|
|
|
TQBuffer in_buffer (msg_buf->data);
|
|
|
|
in_buffer.open (IO_ReadOnly);
|
|
|
|
TQDataStream in_stream (&in_buffer);
|
|
|
|
|
|
|
|
TQByteArray out_msg;
|
|
|
|
TQBuffer out_buffer (out_msg);
|
|
|
|
out_buffer.open (IO_WriteOnly);
|
|
|
|
TQDataStream out_stream (&out_buffer);
|
|
|
|
|
|
|
|
bool unknown = false;
|
|
|
|
|
|
|
|
TQByteArray ttt=in_buffer.buffer();
|
|
|
|
TQ_UINT32 messageID;
|
|
|
|
in_stream >> messageID;
|
|
|
|
//kdDebug(11001) << k_funcinfo << ": got message with messageID=" << messageID << endl;
|
|
|
|
switch (messageID)
|
|
|
|
{
|
|
|
|
case RETQ_BROADCAST:
|
|
|
|
out_stream << TQ_UINT32 (MSG_BROADCAST) << clientID;
|
|
|
|
// FIXME, compiler bug?
|
|
|
|
// this should be okay, since TQBuffer is subclass of TQIODevice! :
|
|
|
|
// out_buffer.writeBlock (in_buffer.readAll());
|
|
|
|
TQT_TQIODEVICE(&out_buffer)->writeBlock (in_buffer.readAll());
|
|
|
|
broadcastMessage (out_msg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RETQ_FORWARD:
|
|
|
|
{
|
|
|
|
TQValueList <TQ_UINT32> clients;
|
|
|
|
in_stream >> clients;
|
|
|
|
out_stream << TQ_UINT32 (MSG_FORWARD) << clientID << clients;
|
|
|
|
// see above!
|
|
|
|
TQT_TQIODEVICE(&out_buffer)->writeBlock (in_buffer.readAll());
|
|
|
|
sendMessage (clients, out_msg);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RETQ_CLIENT_ID:
|
|
|
|
out_stream << TQ_UINT32 (ANS_CLIENT_ID) << clientID;
|
|
|
|
sendMessage (clientID, out_msg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RETQ_ADMIN_ID:
|
|
|
|
out_stream << TQ_UINT32 (ANS_ADMIN_ID) << d->mAdminID;
|
|
|
|
sendMessage (clientID, out_msg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RETQ_ADMIN_CHANGE:
|
|
|
|
if (clientID == d->mAdminID)
|
|
|
|
{
|
|
|
|
TQ_UINT32 newAdmin;
|
|
|
|
in_stream >> newAdmin;
|
|
|
|
setAdmin (newAdmin);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RETQ_REMOVE_CLIENT:
|
|
|
|
if (clientID == d->mAdminID)
|
|
|
|
{
|
|
|
|
TQValueList <TQ_UINT32> client_list;
|
|
|
|
in_stream >> client_list;
|
|
|
|
for (TQValueListIterator <TQ_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 RETQ_MAX_NUM_CLIENTS:
|
|
|
|
if (clientID == d->mAdminID)
|
|
|
|
{
|
|
|
|
TQ_INT32 maximum_clients;
|
|
|
|
in_stream >> maximum_clients;
|
|
|
|
setMaxClients (maximum_clients);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RETQ_CLIENT_LIST:
|
|
|
|
{
|
|
|
|
out_stream << TQ_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"
|