KDEGames

kmessageserver.cpp
1 /*
2  This file is part of the KDE games library
3  Copyright (C) 2001 Burkhard Lehner ([email protected])
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License version 2 as published by the Free Software Foundation.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18 */
19 
20 #include "kmessageserver.h"
21 #include "kmessageserver_p.h"
22 
23 #include <QIODevice>
24 #include <QBuffer>
25 #include <QList>
26 #include <QQueue>
27 #include <QTimer>
28 #include <QDataStream>
29 
30 #include "kmessageio.h"
31 
32 // --------------- internal class KMessageServerSocket
33 
34 KMessageServerSocket::KMessageServerSocket (quint16 port, QObject *parent)
35  : QTcpServer (parent)
36 {
37  listen ( QHostAddress::Any, port );
38  connect(this, &KMessageServerSocket::newConnection, this, &KMessageServerSocket::slotNewConnection);
39 }
40 
41 
42 KMessageServerSocket::~KMessageServerSocket ()
43 {
44 }
45 
46 void KMessageServerSocket::slotNewConnection ()
47 {
48  if (hasPendingConnections())
49  {
50  Q_EMIT newClientConnected (new KMessageSocket (nextPendingConnection()));
51  }
52 }
53 
54 // ---------------- class for storing an incoming message
55 
56 class MessageBuffer
57 {
58  public:
59  MessageBuffer (quint32 clientID, const QByteArray &messageData)
60  : id (clientID), data (messageData) { }
61  ~MessageBuffer () {}
62  quint32 id;
63  QByteArray data;
64 };
65 
66 // ---------------- KMessageServer's private class
67 
68 class KMessageServerPrivate
69 {
70 public:
71  KMessageServerPrivate()
72  : mMaxClients (-1), mGameId (1), mUniqueClientNumber (1), mAdminID (0), mServerSocket (nullptr) {}
73 
74  ~KMessageServerPrivate()
75  {
76  qDeleteAll(mClientList);
77  qDeleteAll(mMessageQueue);
78  }
79 
80  int mMaxClients;
81  int mGameId;
82  quint16 mCookie;
83  quint32 mUniqueClientNumber;
84  quint32 mAdminID;
85 
86  KMessageServerSocket* mServerSocket;
87 
88  QList<KMessageIO*> mClientList;
89  QQueue <MessageBuffer*> mMessageQueue;
90  QTimer mTimer;
91  bool mIsRecursive;
92 };
93 
94 
95 // ------------------ KMessageServer
96 
98  : QObject(parent)
99 {
100  d = new KMessageServerPrivate;
101  d->mIsRecursive=false;
102  d->mCookie=cookie;
103  connect (&(d->mTimer), &QTimer::timeout,
105  qCDebug(GAMES_PRIVATE_KGAME) << "CREATE(KMessageServer="
106  << this
107  << ") cookie="
108  << d->mCookie
109  << "sizeof(this)="
110  << sizeof(KMessageServer);
111 }
112 
113 KMessageServer::~KMessageServer()
114 {
115  qCDebug(GAMES_PRIVATE_KGAME) << "this=" << this;
116  Debug();
117  stopNetwork();
118  deleteClients();
119  delete d;
120  qCDebug(GAMES_PRIVATE_KGAME) << "done";
121 }
122 
123 //------------------------------------- TCP/IP server stuff
124 
125 bool KMessageServer::initNetwork (quint16 port)
126 {
127  qCDebug(GAMES_PRIVATE_KGAME) ;
128 
129  if (d->mServerSocket)
130  {
131  qCDebug(GAMES_PRIVATE_KGAME) << ": We were already offering connections!";
132  delete d->mServerSocket;
133  }
134 
135  d->mServerSocket = new KMessageServerSocket (port);
136  d->mIsRecursive = false;
137 
138  if (!d->mServerSocket
139  || !d->mServerSocket->isListening())
140  {
141  qCCritical(GAMES_PRIVATE_KGAME) << ": Serversocket::ok() == false";
142  delete d->mServerSocket;
143  d->mServerSocket=nullptr;
144  return false;
145  }
146 
147  qCDebug(GAMES_PRIVATE_KGAME) << ": Now listening to port "
148  << d->mServerSocket->serverPort();
149  connect(d->mServerSocket, &KMessageServerSocket::newClientConnected, this, &KMessageServer::addClient);
150  return true;
151 }
152 
154 {
155  if (d->mServerSocket)
156  return d->mServerSocket->serverPort();
157  else
158  return 0;
159 }
160 
162 {
163  if (d->mServerSocket)
164  {
165  delete d->mServerSocket;
166  d->mServerSocket = nullptr;
167  }
168 }
169 
171 {
172  return d->mServerSocket != nullptr;
173 }
174 
175 //----------------------------------------------- adding / removing clients
176 
178 {
179  QByteArray msg;
180 
181  // maximum number of clients reached?
182  if (d->mMaxClients >= 0 && d->mMaxClients <= clientCount())
183  {
184  qCCritical(GAMES_PRIVATE_KGAME) << ": Maximum number of clients reached!";
185  return;
186  }
187 
188  // give it a unique ID
189  client->setId (uniqueClientNumber());
190  qCDebug(GAMES_PRIVATE_KGAME) << ":" << client->id();
191 
192  // connect its signals
194  this, &KMessageServer::removeBrokenClient);
195  connect (client, &KMessageIO::received,
197 
198  // Tell everyone about the new guest
199  // Note: The new client doesn't get this message!
200  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (EVNT_CLIENT_CONNECTED) << client->id();
201  broadcastMessage (msg);
202 
203  // add to our list
204  d->mClientList.push_back(client);
205 
206  // tell it its ID
207  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (ANS_CLIENT_ID) << client->id();
208  client->send (msg);
209 
210  // Give it the complete list of client IDs
211  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (ANS_CLIENT_LIST) << clientIDs();
212  client->send (msg);
213 
214 
215  if (clientCount() == 1)
216  {
217  // if it is the first client, it becomes the admin
218  setAdmin (client->id());
219  }
220  else
221  {
222  // otherwise tell it who is the admin
223  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (ANS_ADMIN_ID) << adminID();
224  client->send (msg);
225  }
226 
227  Q_EMIT clientConnected (client);
228 }
229 
230 void KMessageServer::removeClient (KMessageIO* client, bool broken)
231 {
232  quint32 clientID = client->id();
233  if (!d->mClientList.removeAll(client))
234  {
235  qCCritical(GAMES_PRIVATE_KGAME) << ": Deleting client that wasn't added before!";
236  return;
237  }
238 
239  // tell everyone about the removed client
240  QByteArray msg;
241  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (EVNT_CLIENT_DISCONNECTED) << client->id() << (qint8)broken;
242  broadcastMessage (msg);
243 
244  // If it was the admin, select a new admin.
245  if (clientID == adminID())
246  {
247  if (!d->mClientList.isEmpty())
248  setAdmin (d->mClientList.front()->id());
249  else
250  setAdmin (0);
251  }
252 }
253 
255 {
256  qDeleteAll(d->mClientList);
257  d->mClientList.clear();
258  d->mAdminID = 0;
259 }
260 
261 void KMessageServer::removeBrokenClient ()
262 {
263  KMessageIO *client = sender() ? qobject_cast<KMessageIO*>(sender()) : nullptr;
264  if (!client)
265  {
266  qCCritical(GAMES_PRIVATE_KGAME) << ": sender of the signal was not a KMessageIO object!";
267  return;
268  }
269 
270  Q_EMIT connectionLost (client);
271  removeClient (client, true);
272 }
273 
274 
276 {
277  d->mMaxClients = c;
278 }
279 
281 {
282  return d->mMaxClients;
283 }
284 
286 {
287  return d->mClientList.count();
288 }
289 
291 {
292  QList <quint32> list;
293  for (QList<KMessageIO*>::iterator iter(d->mClientList.begin()); iter!=d->mClientList.end(); ++iter)
294  list.append ((*iter)->id());
295  return list;
296 }
297 
299 {
300  if (no == 0)
301  no = d->mAdminID;
302 
303  QList<KMessageIO*>::iterator iter = d->mClientList.begin();
304  while (iter!=d->mClientList.end())
305  {
306  if ((*iter)->id() == no)
307  return (*iter);
308  ++iter;
309  }
310  return nullptr;
311 }
312 
313 quint32 KMessageServer::adminID () const
314 {
315  return d->mAdminID;
316 }
317 
319 {
320  // Trying to set the client that is already admin => nothing to do
321  if (adminID == d->mAdminID)
322  return;
323 
324  if (adminID > 0 && findClient (adminID) == nullptr)
325  {
326  qCWarning(GAMES_PRIVATE_KGAME) << "Trying to set a new admin that doesn't exist!";
327  return;
328  }
329 
330  d->mAdminID = adminID;
331 
332  QByteArray msg;
333  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (ANS_ADMIN_ID) << adminID;
334 
335  // Tell everyone about the new master
336  broadcastMessage (msg);
337 }
338 
339 
340 //------------------------------------------- ID stuff
341 
343 {
344  return d->mUniqueClientNumber++;
345 }
346 
347 // --------------------- Messages ---------------------------
348 
350 {
351  for (QList<KMessageIO*>::iterator iter (d->mClientList.begin()); iter!=d->mClientList.end(); ++iter)
352  (*iter)->send (msg);
353 }
354 
355 void KMessageServer::sendMessage (quint32 id, const QByteArray &msg)
356 {
357  KMessageIO *client = findClient (id);
358  if (client)
359  client->send (msg);
360 }
361 
363 {
364  for (QList<quint32>::ConstIterator iter = ids.begin(); iter != ids.end(); ++iter)
365  sendMessage (*iter, msg);
366 }
367 
369 {
370  KMessageIO *client = sender() ? qobject_cast<KMessageIO*>(sender()) : nullptr;
371  if (!client)
372  {
373  qCCritical(GAMES_PRIVATE_KGAME) << ": slot was not called from KMessageIO!";
374  return;
375  }
376  //qCDebug(GAMES_PRIVATE_KGAME) << ": size=" << msg.size();
377  quint32 clientID = client->id();
378 
379  //QByteArray *ta=new QByteArray;
380  //ta->duplicate(msg);
381  //d->mMessageQueue.enqueue (new MessageBuffer (clientID, *ta));
382 
383 
384  d->mMessageQueue.enqueue (new MessageBuffer (clientID, msg));
385  if (!d->mTimer.isActive())
386  d->mTimer.start(0); // AB: should be , TRUE i guess
387 }
388 
390 {
391  // This shouldn't happen, since the timer should be stopped before. But only to be sure!
392  if (d->mMessageQueue.isEmpty())
393  {
394  d->mTimer.stop();
395  return;
396  }
397  if (d->mIsRecursive)
398  {
399  return;
400  }
401  d->mIsRecursive = true;
402 
403  MessageBuffer *msg_buf = d->mMessageQueue.head();
404 
405  quint32 clientID = msg_buf->id;
406  QBuffer in_buffer (&msg_buf->data);
407  in_buffer.open (QIODevice::ReadOnly);
408  QDataStream in_stream (&in_buffer);
409 
410  QByteArray out_msg;
411  QBuffer out_buffer (&out_msg);
412  out_buffer.open (QIODevice::WriteOnly);
413  QDataStream out_stream (&out_buffer);
414 
415  bool unknown = false;
416 
417  QByteArray ttt=in_buffer.buffer();
418  quint32 messageID;
419  in_stream >> messageID;
420  //qCDebug(GAMES_PRIVATE_KGAME) << ": got message with messageID=" << messageID;
421  switch (messageID)
422  {
423  case REQ_BROADCAST:
424  out_stream << quint32 (MSG_BROADCAST) << clientID;
425  // FIXME, compiler bug?
426  // this should be okay, since QBuffer is subclass of QIODevice! :
427  // out_buffer.write (in_buffer.readAll());
428  out_buffer.QIODevice::write (in_buffer.readAll());
429  broadcastMessage (out_msg);
430  break;
431 
432  case REQ_FORWARD:
433  {
434  QList <quint32> clients;
435  in_stream >> clients;
436  out_stream << quint32 (MSG_FORWARD) << clientID << clients;
437  // see above!
438  out_buffer.QIODevice::write (in_buffer.readAll());
439  sendMessage (clients, out_msg);
440  }
441  break;
442 
443  case REQ_CLIENT_ID:
444  out_stream << quint32 (ANS_CLIENT_ID) << clientID;
445  sendMessage (clientID, out_msg);
446  break;
447 
448  case REQ_ADMIN_ID:
449  out_stream << quint32 (ANS_ADMIN_ID) << d->mAdminID;
450  sendMessage (clientID, out_msg);
451  break;
452 
453  case REQ_ADMIN_CHANGE:
454  if (clientID == d->mAdminID)
455  {
456  quint32 newAdmin;
457  in_stream >> newAdmin;
458  setAdmin (newAdmin);
459  }
460  break;
461 
462  case REQ_REMOVE_CLIENT:
463  if (clientID == d->mAdminID)
464  {
465  QList <quint32> client_list;
466  in_stream >> client_list;
467  for (QList<quint32>::Iterator iter = client_list.begin(); iter != client_list.end(); ++iter)
468  {
469  KMessageIO *client = findClient (*iter);
470  if (client)
471  removeClient (client, false);
472  else
473  qCWarning(GAMES_PRIVATE_KGAME) << ": removing non-existing clientID";
474  }
475  }
476  break;
477 
478  case REQ_MAX_NUM_CLIENTS:
479  if (clientID == d->mAdminID)
480  {
481  qint32 maximum_clients;
482  in_stream >> maximum_clients;
483  setMaxClients (maximum_clients);
484  }
485  break;
486 
487  case REQ_CLIENT_LIST:
488  {
489  out_stream << quint32 (ANS_CLIENT_LIST) << clientIDs();
490  sendMessage (clientID, out_msg);
491  }
492  break;
493 
494  default:
495  unknown = true;
496  }
497 
498  // check if all the data has been used
499  if (!unknown && !in_buffer.atEnd())
500  qCWarning(GAMES_PRIVATE_KGAME) << ": Extra data received for message ID" << messageID;
501 
502  Q_EMIT messageReceived (msg_buf->data, clientID, unknown);
503 
504  if (unknown)
505  qCWarning(GAMES_PRIVATE_KGAME) << ": received unknown message ID" << messageID;
506 
507  // remove the message, since we are ready with it
508  d->mMessageQueue.dequeue();
509  if (d->mMessageQueue.isEmpty())
510  d->mTimer.stop();
511  d->mIsRecursive = false;
512 }
513 
515 {
516  qCDebug(GAMES_PRIVATE_KGAME) << "------------------ KMESSAGESERVER -----------------------";
517  qCDebug(GAMES_PRIVATE_KGAME) << "MaxClients : " << maxClients();
518  qCDebug(GAMES_PRIVATE_KGAME) << "NoOfClients : " << clientCount();
519  qCDebug(GAMES_PRIVATE_KGAME) << "---------------------------------------------------";
520 }
521 
522 #include "moc_kmessageserver.cpp"
quint32 uniqueClientNumber() const
KMessageIO * findClient(quint32 no) const
Find the KMessageIO object to the given client number.
This class implements the message communication using a TCP/IP socket.
Definition: kmessageio.h:175
virtual void Debug()
Gives debug output of the game status.
QObject * sender() const const
void setId(quint32 id)
Sets the ID number of this object.
Definition: kmessageio.cpp:39
void received(const QByteArray &msg)
This signal is emitted when /e send() on the connected KMessageIO object is called.
quint32 adminID() const
Returns the clientID of the admin, if there is a admin, 0 otherwise.
QTcpServer * listen(const QString &protocol, const QHostAddress &address=QHostAddress::Any, quint16 port=0, QObject *parent=nullptr)
void removeClient(KMessageIO *io, bool broken)
Removes the KMessageIO object from the client list and deletes it.
virtual void sendMessage(quint32 id, const QByteArray &msg)
Sends a message to a single client with the given ID.
quint16 serverPort() const
Returns the TCP/IP port number we are listening to for incoming connections.
int clientCount() const
returns the current number of connected clients.
QByteArray & buffer()
quint32 id()
Queries the ID of this object.
Definition: kmessageio.cpp:44
void setAdmin(quint32 adminID)
Sets the admin to a new client with the given ID.
void addClient(KMessageIO *)
Adds a new KMessageIO object to the communication server.
void timeout()
void messageReceived(const QByteArray &data, quint32 clientID, bool &unknown)
This signal is always emitted when a message from a client is received.
void clientConnected(KMessageIO *client)
A new client connected to the game.
virtual bool atEnd() const const override
void append(const T &value)
KMessageServer(quint16 cookie=42, QObject *parent=nullptr)
Create a KGameNetwork object.
QList< quint32 > clientIDs() const
returns a list of the unique IDs of all clients.
void deleteClients()
Deletes all connections to the clients.
This abstract base class represents one end of a message connections between two clients.
Definition: kmessageio.h:62
QByteArray readAll()
virtual bool open(QIODevice::OpenMode flags) override
QList::iterator end()
virtual void getReceivedMessage(const QByteArray &msg)
This slot receives all the messages from the KMessageIO::received signals.
void connectionBroken()
This signal is emitted when the connection is closed.
virtual void broadcastMessage(const QByteArray &msg)
Sends a message to all connected clients.
virtual void processOneMessage()
This slot is called whenever there are elements in the message queue.
bool initNetwork(quint16 port=0)
Starts the Communication server to listen for incoming TCP/IP connections.
int maxClients() const
returns the maximum number of clients
bool isOfferingConnections() const
Are we still offer offering server connections?
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
QObject * parent() const const
virtual void send(const QByteArray &msg)=0
This slot sends the data block in /e msg to the connected object, that will emit /e received()...
QList::iterator begin()
Q_EMITQ_EMIT
void connectionLost(KMessageIO *client)
A network connection got broken.
void setMaxClients(int maxnumber)
sets the maximum number of clients which can connect.
void stopNetwork()
Stops listening for connections.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Nov 30 2020 22:37:54 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.