KDEGames

kmessageclient.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 "kmessageclient.h"
9 
10 // own
11 #include "kmessageio.h"
12 #include "kmessageserver.h"
13 // Qt
14 #include <QBuffer>
15 #include <QTimer>
16 #include <QList>
17 #include <QDataStream>
18 // Std
19 #include <cstdio>
20 
21 class KMessageClientPrivate
22 {
23 public:
24  KMessageClientPrivate ()
25  : adminID (0), connection (nullptr)
26  {}
27 
28  ~KMessageClientPrivate ()
29  {
30  delete connection;
31  }
32 
33  quint32 adminID;
34  QList <quint32> clientList;
35  KMessageIO *connection;
36 
37  bool isLocked;
38  QList <QByteArray> delayedMessages;
39 };
40 
42  : QObject (parent),
43  d( new KMessageClientPrivate )
44 {
45  d->isLocked = false;
46 }
47 
49 {
50  d->delayedMessages.clear();
51 }
52 
53 // -- setServer stuff
54 
55 void KMessageClient::setServer (const QString &host, quint16 port)
56 {
57  setServer (new KMessageSocket (host, port));
58 }
59 
61 {
62  KMessageDirect *serverIO = new KMessageDirect ();
63  setServer (new KMessageDirect (serverIO));
64  server->addClient (serverIO);
65 }
66 
68 {
69  if (d->connection)
70  {
71  delete d->connection;
72  qCDebug(GAMES_PRIVATE_KGAME) << ": We are changing the server!";
73  }
74 
75  d->connection = connection;
76  if (connection )
77  {
80  }
81 }
82 
83 // -- id stuff
84 
85 quint32 KMessageClient::id () const
86 {
87  return (d->connection) ? d->connection->id () : 0;
88 }
89 
91 {
92  return id() != 0 && id() == adminId();
93 }
94 
95 quint32 KMessageClient::adminId () const
96 {
97  return d->adminID;
98 }
99 
101 {
102  return d->clientList;
103 }
104 
106 {
107  return d->connection && d->connection->isConnected();
108 }
109 
111 {
112  return isConnected() ? d->connection->isNetwork() : false;
113 }
114 
115 quint16 KMessageClient::peerPort () const
116 {
117  return d->connection ? d->connection->peerPort() : 0;
118 }
119 
121 {
122  return d->connection ? d->connection->peerName() : QStringLiteral("localhost");
123 }
124 
125 // --------------------- Sending messages
126 
128 {
129  if (!d->connection)
130  {
131  qCWarning(GAMES_PRIVATE_KGAME) << ": We have no connection yet!";
132  return;
133  }
134  d->connection->send (msg);
135 }
136 
138 {
139  QByteArray sendBuffer;
140  QBuffer buffer (&sendBuffer);
141  buffer.open (QIODevice::WriteOnly);
142  QDataStream stream (&buffer);
143 
144  stream << static_cast<quint32> ( KMessageServer::REQ_BROADCAST );
145  buffer.QIODevice::write (msg);
146  sendServerMessage (sendBuffer);
147 }
148 
149 void KMessageClient::sendForward (const QByteArray &msg, const QList <quint32> &clients)
150 {
151  QByteArray sendBuffer;
152  QBuffer buffer (&sendBuffer);
153  buffer.open (QIODevice::WriteOnly);
154  QDataStream stream (&buffer);
155 
156  stream << static_cast<quint32>( KMessageServer::REQ_FORWARD ) << clients;
157  buffer.QIODevice::write (msg);
158  sendServerMessage (sendBuffer);
159 }
160 
161 void KMessageClient::sendForward (const QByteArray &msg, quint32 client)
162 {
163  sendForward (msg, QList <quint32> () << client);
164 }
165 
166 
167 // --------------------- Receiving and processing messages
168 
170 {
171  if (d->isLocked)
172  {
173  d->delayedMessages.append(msg);
174  return;
175  }
176  if (!d->delayedMessages.isEmpty())
177  {
178  d->delayedMessages.append (msg);
179  QByteArray first = d->delayedMessages.front();
180  d->delayedMessages.pop_front();
181  processMessage (first);
182  }
183  else
184  {
185  processMessage(msg);
186  }
187 }
188 
190 {
191  if (d->isLocked)
192  { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage
193  d->delayedMessages.append(msg);
194  return;
195  }
196  QBuffer in_buffer;
197  in_buffer.setData(msg);
198  in_buffer.open (QIODevice::ReadOnly);
199  QDataStream in_stream (&in_buffer);
200 
201 
202  bool unknown = false;
203 
204  quint32 messageID;
205  in_stream >> messageID;
206  switch (messageID)
207  {
208  case KMessageServer::MSG_BROADCAST:
209  {
210  quint32 clientID;
211  in_stream >> clientID;
212  Q_EMIT broadcastReceived (in_buffer.readAll(), clientID);
213  }
214  break;
215 
216  case KMessageServer::MSG_FORWARD:
217  {
218  quint32 clientID;
220  in_stream >> clientID >> receivers;
221  Q_EMIT forwardReceived (in_buffer.readAll(), clientID, receivers);
222  }
223  break;
224 
225  case KMessageServer::ANS_CLIENT_ID:
226  {
227  bool old_admin = isAdmin();
228  quint32 clientID;
229  in_stream >> clientID;
230  d->connection->setId (clientID);
231  if (old_admin != isAdmin())
233  }
234  break;
235 
236  case KMessageServer::ANS_ADMIN_ID:
237  {
238  bool old_admin = isAdmin();
239  in_stream >> d->adminID;
240  if (old_admin != isAdmin())
242  }
243  break;
244 
245  case KMessageServer::ANS_CLIENT_LIST:
246  {
247  in_stream >> d->clientList;
248  }
249  break;
250 
251  case KMessageServer::EVNT_CLIENT_CONNECTED:
252  {
253  quint32 id;
254  in_stream >> id;
255 
256  if (d->clientList.contains (id))
257  qCWarning(GAMES_PRIVATE_KGAME) << ": Adding a client that already existed!";
258  else
259  d->clientList.append (id);
260 
262  }
263  break;
264 
265  case KMessageServer::EVNT_CLIENT_DISCONNECTED:
266  {
267  quint32 id;
268  qint8 broken;
269  in_stream >> id >> broken;
270 
271  if (!d->clientList.contains (id))
272  qCWarning(GAMES_PRIVATE_KGAME) << ": Removing a client that doesn't exist!";
273  else
274  d->clientList.removeAll (id);
275 
276  Q_EMIT eventClientDisconnected (id, bool (broken));
277  }
278  break;
279 
280  default:
281  unknown = true;
282  }
283 
284  if (!unknown && !in_buffer.atEnd())
285  qCWarning(GAMES_PRIVATE_KGAME) << ": Extra data received for message ID" << messageID;
286 
287  Q_EMIT serverMessageReceived (msg, unknown);
288 
289  if (unknown)
290  qCWarning(GAMES_PRIVATE_KGAME) << ": received unknown message ID" << messageID;
291 }
292 
294 {
295  if (d->isLocked)
296  {
297  return;
298  }
299  if (d->delayedMessages.count() == 0)
300  {
301  qCDebug(GAMES_PRIVATE_KGAME) << ": no messages delayed";
302  return;
303  }
304  QByteArray first = d->delayedMessages.front();
305  d->delayedMessages.pop_front();
306  processMessage (first);
307 }
308 
310 {
311  qCDebug(GAMES_PRIVATE_KGAME) << ": timer single shot for removeBrokenConnection"<<this;
312  // MH We cannot directly delete the socket. otherwise QSocket crashes
313  QTimer::singleShot( 0, this, &KMessageClient::removeBrokenConnection2 );
314  return;
315 }
316 
317 
318 void KMessageClient::removeBrokenConnection2 ()
319 {
320  qCDebug(GAMES_PRIVATE_KGAME) << ": Broken:Deleting the connection object"<<this;
321 
323  delete d->connection;
324  d->connection = nullptr;
325  d->adminID = 0;
327  qCDebug(GAMES_PRIVATE_KGAME) << ": Broken:Deleting the connection object DONE";
328 }
329 
331 {
332  qCDebug(GAMES_PRIVATE_KGAME) << ": Disconnect:Deleting the connection object";
333 
335  delete d->connection;
336  d->connection = nullptr;
337  d->adminID = 0;
339  qCDebug(GAMES_PRIVATE_KGAME) << ": Disconnect:Deleting the connection object DONE";
340 }
341 
343 {
344  d->isLocked = true;
345 }
346 
348 {
349  d->isLocked = false;
350  for (int i = 0; i < d->delayedMessages.count(); i++)
351  {
353  }
354 }
355 
357 {
358  return d->delayedMessages.count();
359 }
360 
361 
virtual void processIncomingMessage(const QByteArray &msg)
This slot is called from the signal KMessageIO::received whenever a message from the KMessageServer a...
bool isAdmin() const
quint16 peerPort() const
unsigned int delayedMessageCount() const
This class implements the message communication using a TCP/IP socket.
Definition: kmessageio.h:164
void sendForward(const QByteArray &msg, const QList< quint32 > &clients)
Sends a message to all the clients in a list.
void sendServerMessage(const QByteArray &msg)
Sends a message to the KMessageServer.
void received(const QByteArray &msg)
This signal is emitted when /e send() on the connected KMessageIO object is called.
void aboutToDisconnect(quint32 id)
This signal is emitted right before the client disconnects.
void setData(const QByteArray &data)
quint32 adminId() const
KMessageClient(QObject *parent=nullptr)
Constructor.
void forwardReceived(const QByteArray &msg, quint32 senderID, const QList< quint32 > &receivers)
This signal is emitted when the client receives a forward message from the KMessageServer, sent by another client.
bool isConnected() const
QList< quint32 > clientList() const
void serverMessageReceived(const QByteArray &msg, bool &unknown)
This signal is emitted on every message that came from the server.
This class implements the message communication using function calls directly.
Definition: kmessageio.h:288
void addClient(KMessageIO *)
Adds a new KMessageIO object to the communication server.
virtual bool atEnd() const const override
virtual void removeBrokenConnection()
This slot is called from the signal KMessageIO::connectionBroken.
void processFirstMessage()
Called from unlock() (using QTimer::singleShot) until all delayed messages are delivered.
void adminStatusChanged(bool isAdmin)
This signal is emitted when this client becomes the admin client or when it loses the admin client st...
void disconnect()
Corresponds to setServer(0); but also emits the connectionBroken signal.
void sendBroadcast(const QByteArray &msg)
Sends a message to all the clients connected to the server, including ourself.
This abstract base class represents one end of a message connections between two clients.
Definition: kmessageio.h:51
bool isNetwork() const
QByteArray readAll()
A server for message sending and broadcasting, using TCP/IP connections.
virtual bool open(QIODevice::OpenMode flags) override
void eventClientConnected(quint32 clientID)
This signal is emitted when another client has connected to the server.
void broadcastReceived(const QByteArray &msg, quint32 senderID)
This signal is emitted when the client receives a broadcast message from the KMessageServer, sent by another client.
void connectionBroken()
This signal is emitted when the connection is closed.
void eventClientDisconnected(quint32 clientID, bool broken)
This signal is emitted when the server has lost the connection to one of the clients (This could be b...
void lock()
Once this function is called no message will be received anymore.
quint32 id() const
virtual void processMessage(const QByteArray &msg)
This slot is called from processIncomingMessage or processFirstMessage, depending on whether the clie...
void connectionBroken()
This signal is emitted when the connection to the KMessageServer is broken.
QString peerName() const
void unlock()
Deliver every message that was delayed by lock() and actually deliver all messages that get received ...
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setServer(const QString &host, quint16 port)
Connects the client to (another) server.
~KMessageClient() override
Destructor.
Q_EMITQ_EMIT
int receivers(const char *signal) const const
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.