KDEGames

kmessageserver.cpp
1 /*
2  This file is part of the KDE games library
3  SPDX-FileCopyrightText: 2001 Burkhard Lehner <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-only
6 */
7 
8 #include "kmessageserver.h"
9 #include "kmessageserver_p.h"
10 
11 // own
12 #include "kmessageio.h"
13 // Qt
14 #include <QIODevice>
15 #include <QBuffer>
16 #include <QList>
17 #include <QQueue>
18 #include <QTimer>
19 #include <QDataStream>
20 
21 // --------------- internal class KMessageServerSocket
22 
23 KMessageServerSocket::KMessageServerSocket (quint16 port, QObject *parent)
24  : QTcpServer (parent)
25 {
26  listen ( QHostAddress::Any, port );
27  connect(this, &KMessageServerSocket::newConnection, this, &KMessageServerSocket::slotNewConnection);
28 }
29 
30 
31 KMessageServerSocket::~KMessageServerSocket ()
32 {
33 }
34 
35 void KMessageServerSocket::slotNewConnection ()
36 {
37  if (hasPendingConnections())
38  {
39  Q_EMIT newClientConnected (new KMessageSocket (nextPendingConnection()));
40  }
41 }
42 
43 // ---------------- class for storing an incoming message
44 
45 class MessageBuffer
46 {
47  public:
48  MessageBuffer (quint32 clientID, const QByteArray &messageData)
49  : id (clientID), data (messageData) { }
50  ~MessageBuffer () {}
51  quint32 id;
52  QByteArray data;
53 };
54 
55 // ---------------- KMessageServer's private class
56 
57 class KMessageServerPrivate
58 {
59 public:
60  KMessageServerPrivate()
61  : mMaxClients (-1), mGameId (1), mUniqueClientNumber (1), mAdminID (0), mServerSocket (nullptr) {}
62 
63  ~KMessageServerPrivate()
64  {
65  qDeleteAll(mClientList);
66  qDeleteAll(mMessageQueue);
67  }
68 
69  int mMaxClients;
70  int mGameId;
71  quint16 mCookie;
72  quint32 mUniqueClientNumber;
73  quint32 mAdminID;
74 
75  KMessageServerSocket* mServerSocket;
76 
77  QList<KMessageIO*> mClientList;
78  QQueue <MessageBuffer*> mMessageQueue;
79  QTimer mTimer;
80  bool mIsRecursive;
81 };
82 
83 
84 // ------------------ KMessageServer
85 
87  : QObject(parent)
88  , d(new KMessageServerPrivate)
89 {
90  d->mIsRecursive=false;
91  d->mCookie=cookie;
92  connect (&(d->mTimer), &QTimer::timeout,
94  qCDebug(GAMES_PRIVATE_KGAME) << "CREATE(KMessageServer="
95  << this
96  << ") cookie="
97  << d->mCookie
98  << "sizeof(this)="
99  << sizeof(KMessageServer);
100 }
101 
102 KMessageServer::~KMessageServer()
103 {
104  qCDebug(GAMES_PRIVATE_KGAME) << "this=" << this;
105  Debug();
106  stopNetwork();
107  deleteClients();
108  qCDebug(GAMES_PRIVATE_KGAME) << "done";
109 }
110 
111 //------------------------------------- TCP/IP server stuff
112 
113 bool KMessageServer::initNetwork (quint16 port)
114 {
115  qCDebug(GAMES_PRIVATE_KGAME) ;
116 
117  if (d->mServerSocket)
118  {
119  qCDebug(GAMES_PRIVATE_KGAME) << ": We were already offering connections!";
120  delete d->mServerSocket;
121  }
122 
123  d->mServerSocket = new KMessageServerSocket (port);
124  d->mIsRecursive = false;
125 
126  if (!d->mServerSocket
127  || !d->mServerSocket->isListening())
128  {
129  qCCritical(GAMES_PRIVATE_KGAME) << ": Serversocket::ok() == false";
130  delete d->mServerSocket;
131  d->mServerSocket=nullptr;
132  return false;
133  }
134 
135  qCDebug(GAMES_PRIVATE_KGAME) << ": Now listening to port "
136  << d->mServerSocket->serverPort();
137  connect(d->mServerSocket, &KMessageServerSocket::newClientConnected, this, &KMessageServer::addClient);
138  return true;
139 }
140 
142 {
143  if (d->mServerSocket)
144  return d->mServerSocket->serverPort();
145  else
146  return 0;
147 }
148 
150 {
151  if (d->mServerSocket)
152  {
153  delete d->mServerSocket;
154  d->mServerSocket = nullptr;
155  }
156 }
157 
159 {
160  return d->mServerSocket != nullptr;
161 }
162 
163 //----------------------------------------------- adding / removing clients
164 
166 {
167  QByteArray msg;
168 
169  // maximum number of clients reached?
170  if (d->mMaxClients >= 0 && d->mMaxClients <= clientCount())
171  {
172  qCCritical(GAMES_PRIVATE_KGAME) << ": Maximum number of clients reached!";
173  return;
174  }
175 
176  // give it a unique ID
177  client->setId (uniqueClientNumber());
178  qCDebug(GAMES_PRIVATE_KGAME) << ":" << client->id();
179 
180  // connect its signals
182  this, &KMessageServer::removeBrokenClient);
183  connect (client, &KMessageIO::received,
185 
186  // Tell everyone about the new guest
187  // Note: The new client doesn't get this message!
188  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (EVNT_CLIENT_CONNECTED) << client->id();
189  broadcastMessage (msg);
190 
191  // add to our list
192  d->mClientList.push_back(client);
193 
194  // tell it its ID
195  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (ANS_CLIENT_ID) << client->id();
196  client->send (msg);
197 
198  // Give it the complete list of client IDs
199  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (ANS_CLIENT_LIST) << clientIDs();
200  client->send (msg);
201 
202 
203  if (clientCount() == 1)
204  {
205  // if it is the first client, it becomes the admin
206  setAdmin (client->id());
207  }
208  else
209  {
210  // otherwise tell it who is the admin
211  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (ANS_ADMIN_ID) << adminID();
212  client->send (msg);
213  }
214 
215  Q_EMIT clientConnected (client);
216 }
217 
218 void KMessageServer::removeClient (KMessageIO* client, bool broken)
219 {
220  quint32 clientID = client->id();
221  if (!d->mClientList.removeAll(client))
222  {
223  qCCritical(GAMES_PRIVATE_KGAME) << ": Deleting client that wasn't added before!";
224  return;
225  }
226 
227  // tell everyone about the removed client
228  QByteArray msg;
229  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (EVNT_CLIENT_DISCONNECTED) << client->id() << (qint8)broken;
230  broadcastMessage (msg);
231 
232  // If it was the admin, select a new admin.
233  if (clientID == adminID())
234  {
235  if (!d->mClientList.isEmpty())
236  setAdmin (d->mClientList.front()->id());
237  else
238  setAdmin (0);
239  }
240 }
241 
243 {
244  qDeleteAll(d->mClientList);
245  d->mClientList.clear();
246  d->mAdminID = 0;
247 }
248 
249 void KMessageServer::removeBrokenClient ()
250 {
251  KMessageIO *client = sender() ? qobject_cast<KMessageIO*>(sender()) : nullptr;
252  if (!client)
253  {
254  qCCritical(GAMES_PRIVATE_KGAME) << ": sender of the signal was not a KMessageIO object!";
255  return;
256  }
257 
258  Q_EMIT connectionLost (client);
259  removeClient (client, true);
260 }
261 
262 
264 {
265  d->mMaxClients = c;
266 }
267 
269 {
270  return d->mMaxClients;
271 }
272 
274 {
275  return d->mClientList.count();
276 }
277 
279 {
280  QList <quint32> list;
281  for (QList<KMessageIO*>::iterator iter(d->mClientList.begin()); iter!=d->mClientList.end(); ++iter)
282  list.append ((*iter)->id());
283  return list;
284 }
285 
287 {
288  if (no == 0)
289  no = d->mAdminID;
290 
291  QList<KMessageIO*>::iterator iter = d->mClientList.begin();
292  while (iter!=d->mClientList.end())
293  {
294  if ((*iter)->id() == no)
295  return (*iter);
296  ++iter;
297  }
298  return nullptr;
299 }
300 
301 quint32 KMessageServer::adminID () const
302 {
303  return d->mAdminID;
304 }
305 
307 {
308  // Trying to set the client that is already admin => nothing to do
309  if (adminID == d->mAdminID)
310  return;
311 
312  if (adminID > 0 && findClient (adminID) == nullptr)
313  {
314  qCWarning(GAMES_PRIVATE_KGAME) << "Trying to set a new admin that doesn't exist!";
315  return;
316  }
317 
318  d->mAdminID = adminID;
319 
320  QByteArray msg;
321  QDataStream (&msg, QIODevice::WriteOnly) << quint32 (ANS_ADMIN_ID) << adminID;
322 
323  // Tell everyone about the new master
324  broadcastMessage (msg);
325 }
326 
327 
328 //------------------------------------------- ID stuff
329 
331 {
332  return d->mUniqueClientNumber++;
333 }
334 
335 // --------------------- Messages ---------------------------
336 
338 {
339  for (QList<KMessageIO*>::iterator iter (d->mClientList.begin()); iter!=d->mClientList.end(); ++iter)
340  (*iter)->send (msg);
341 }
342 
343 void KMessageServer::sendMessage (quint32 id, const QByteArray &msg)
344 {
345  KMessageIO *client = findClient (id);
346  if (client)
347  client->send (msg);
348 }
349 
351 {
352  for (QList<quint32>::ConstIterator iter = ids.begin(); iter != ids.end(); ++iter)
353  sendMessage (*iter, msg);
354 }
355 
357 {
358  KMessageIO *client = sender() ? qobject_cast<KMessageIO*>(sender()) : nullptr;
359  if (!client)
360  {
361  qCCritical(GAMES_PRIVATE_KGAME) << ": slot was not called from KMessageIO!";
362  return;
363  }
364  //qCDebug(GAMES_PRIVATE_KGAME) << ": size=" << msg.size();
365  quint32 clientID = client->id();
366 
367  //QByteArray *ta=new QByteArray;
368  //ta->duplicate(msg);
369  //d->mMessageQueue.enqueue (new MessageBuffer (clientID, *ta));
370 
371 
372  d->mMessageQueue.enqueue (new MessageBuffer (clientID, msg));
373  if (!d->mTimer.isActive())
374  d->mTimer.start(0); // AB: should be , TRUE i guess
375 }
376 
378 {
379  // This shouldn't happen, since the timer should be stopped before. But only to be sure!
380  if (d->mMessageQueue.isEmpty())
381  {
382  d->mTimer.stop();
383  return;
384  }
385  if (d->mIsRecursive)
386  {
387  return;
388  }
389  d->mIsRecursive = true;
390 
391  MessageBuffer *msg_buf = d->mMessageQueue.head();
392 
393  quint32 clientID = msg_buf->id;
394  QBuffer in_buffer (&msg_buf->data);
395  in_buffer.open (QIODevice::ReadOnly);
396  QDataStream in_stream (&in_buffer);
397 
398  QByteArray out_msg;
399  QBuffer out_buffer (&out_msg);
400  out_buffer.open (QIODevice::WriteOnly);
401  QDataStream out_stream (&out_buffer);
402 
403  bool unknown = false;
404 
405  quint32 messageID;
406  in_stream >> messageID;
407  //qCDebug(GAMES_PRIVATE_KGAME) << ": got message with messageID=" << messageID;
408  switch (messageID)
409  {
410  case REQ_BROADCAST:
411  out_stream << quint32 (MSG_BROADCAST) << clientID;
412  // FIXME, compiler bug?
413  // this should be okay, since QBuffer is subclass of QIODevice! :
414  // out_buffer.write (in_buffer.readAll());
415  out_buffer.QIODevice::write (in_buffer.readAll());
416  broadcastMessage (out_msg);
417  break;
418 
419  case REQ_FORWARD:
420  {
421  QList <quint32> clients;
422  in_stream >> clients;
423  out_stream << quint32 (MSG_FORWARD) << clientID << clients;
424  // see above!
425  out_buffer.QIODevice::write (in_buffer.readAll());
426  sendMessage (clients, out_msg);
427  }
428  break;
429 
430  case REQ_CLIENT_ID:
431  out_stream << quint32 (ANS_CLIENT_ID) << clientID;
432  sendMessage (clientID, out_msg);
433  break;
434 
435  case REQ_ADMIN_ID:
436  out_stream << quint32 (ANS_ADMIN_ID) << d->mAdminID;
437  sendMessage (clientID, out_msg);
438  break;
439 
440  case REQ_ADMIN_CHANGE:
441  if (clientID == d->mAdminID)
442  {
443  quint32 newAdmin;
444  in_stream >> newAdmin;
445  setAdmin (newAdmin);
446  }
447  break;
448 
449  case REQ_REMOVE_CLIENT:
450  if (clientID == d->mAdminID)
451  {
452  QList <quint32> client_list;
453  in_stream >> client_list;
454  for (QList<quint32>::Iterator iter = client_list.begin(); iter != client_list.end(); ++iter)
455  {
456  KMessageIO *client = findClient (*iter);
457  if (client)
458  removeClient (client, false);
459  else
460  qCWarning(GAMES_PRIVATE_KGAME) << ": removing non-existing clientID";
461  }
462  }
463  break;
464 
465  case REQ_MAX_NUM_CLIENTS:
466  if (clientID == d->mAdminID)
467  {
468  qint32 maximum_clients;
469  in_stream >> maximum_clients;
470  setMaxClients (maximum_clients);
471  }
472  break;
473 
474  case REQ_CLIENT_LIST:
475  {
476  out_stream << quint32 (ANS_CLIENT_LIST) << clientIDs();
477  sendMessage (clientID, out_msg);
478  }
479  break;
480 
481  default:
482  unknown = true;
483  }
484 
485  // check if all the data has been used
486  if (!unknown && !in_buffer.atEnd())
487  qCWarning(GAMES_PRIVATE_KGAME) << ": Extra data received for message ID" << messageID;
488 
489  Q_EMIT messageReceived (msg_buf->data, clientID, unknown);
490 
491  if (unknown)
492  qCWarning(GAMES_PRIVATE_KGAME) << ": received unknown message ID" << messageID;
493 
494  // remove the message, since we are ready with it
495  d->mMessageQueue.dequeue();
496  if (d->mMessageQueue.isEmpty())
497  d->mTimer.stop();
498  d->mIsRecursive = false;
499 }
500 
502 {
503  qCDebug(GAMES_PRIVATE_KGAME) << "------------------ KMESSAGESERVER -----------------------";
504  qCDebug(GAMES_PRIVATE_KGAME) << "MaxClients : " << maxClients();
505  qCDebug(GAMES_PRIVATE_KGAME) << "NoOfClients : " << clientCount();
506  qCDebug(GAMES_PRIVATE_KGAME) << "---------------------------------------------------";
507 }
508 
509 #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:164
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:30
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.
quint32 id()
Queries the ID of this object.
Definition: kmessageio.cpp:35
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:51
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-2021 The KDE developers.
Generated on Tue Dec 7 2021 22:34:15 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.