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 <QDataStream>
16 #include <QList>
17 #include <QTimer>
18 // Std
19 #include <cstdio>
20 
21 class KMessageClientPrivate
22 {
23 public:
24  KMessageClientPrivate()
25  : adminID(0)
26  , connection(nullptr)
27  {
28  }
29 
30  ~KMessageClientPrivate()
31  {
32  delete connection;
33  }
34 
35  quint32 adminID;
36  QList<quint32> clientList;
37  KMessageIO *connection;
38 
39  bool isLocked;
40  QList<QByteArray> delayedMessages;
41 };
42 
44  : QObject(parent)
45  , d(new KMessageClientPrivate)
46 {
47  d->isLocked = false;
48 }
49 
51 {
52  d->delayedMessages.clear();
53 }
54 
55 // -- setServer stuff
56 
57 void KMessageClient::setServer(const QString &host, quint16 port)
58 {
59  setServer(new KMessageSocket(host, port));
60 }
61 
63 {
64  KMessageDirect *serverIO = new KMessageDirect();
65  setServer(new KMessageDirect(serverIO));
66  server->addClient(serverIO);
67 }
68 
70 {
71  if (d->connection) {
72  delete d->connection;
73  qCDebug(GAMES_PRIVATE_KGAME) << ": We are changing the server!";
74  }
75 
76  d->connection = connection;
77  if (connection) {
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 
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  qCWarning(GAMES_PRIVATE_KGAME) << ": We have no connection yet!";
131  return;
132  }
133  d->connection->send(msg);
134 }
135 
137 {
138  QByteArray sendBuffer;
139  QBuffer buffer(&sendBuffer);
140  buffer.open(QIODevice::WriteOnly);
141  QDataStream stream(&buffer);
142 
143  stream << static_cast<quint32>(KMessageServer::REQ_BROADCAST);
144  buffer.QIODevice::write(msg);
145  sendServerMessage(sendBuffer);
146 }
147 
148 void KMessageClient::sendForward(const QByteArray &msg, const QList<quint32> &clients)
149 {
150  QByteArray sendBuffer;
151  QBuffer buffer(&sendBuffer);
152  buffer.open(QIODevice::WriteOnly);
153  QDataStream stream(&buffer);
154 
155  stream << static_cast<quint32>(KMessageServer::REQ_FORWARD) << clients;
156  buffer.QIODevice::write(msg);
157  sendServerMessage(sendBuffer);
158 }
159 
160 void KMessageClient::sendForward(const QByteArray &msg, quint32 client)
161 {
162  sendForward(msg, QList<quint32>() << client);
163 }
164 
165 // --------------------- Receiving and processing messages
166 
168 {
169  if (d->isLocked) {
170  d->delayedMessages.append(msg);
171  return;
172  }
173  if (!d->delayedMessages.isEmpty()) {
174  d->delayedMessages.append(msg);
175  QByteArray first = d->delayedMessages.front();
176  d->delayedMessages.pop_front();
177  processMessage(first);
178  } else {
179  processMessage(msg);
180  }
181 }
182 
184 {
185  if (d->isLocked) { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage
186  d->delayedMessages.append(msg);
187  return;
188  }
189  QBuffer in_buffer;
190  in_buffer.setData(msg);
191  in_buffer.open(QIODevice::ReadOnly);
192  QDataStream in_stream(&in_buffer);
193 
194  bool unknown = false;
195 
196  quint32 messageID;
197  in_stream >> messageID;
198  switch (messageID) {
199  case KMessageServer::MSG_BROADCAST: {
200  quint32 clientID;
201  in_stream >> clientID;
202  Q_EMIT broadcastReceived(in_buffer.readAll(), clientID);
203  } break;
204 
205  case KMessageServer::MSG_FORWARD: {
206  quint32 clientID;
208  in_stream >> clientID >> receivers;
209  Q_EMIT forwardReceived(in_buffer.readAll(), clientID, receivers);
210  } break;
211 
212  case KMessageServer::ANS_CLIENT_ID: {
213  bool old_admin = isAdmin();
214  quint32 clientID;
215  in_stream >> clientID;
216  d->connection->setId(clientID);
217  if (old_admin != isAdmin())
219  } break;
220 
221  case KMessageServer::ANS_ADMIN_ID: {
222  bool old_admin = isAdmin();
223  in_stream >> d->adminID;
224  if (old_admin != isAdmin())
226  } break;
227 
228  case KMessageServer::ANS_CLIENT_LIST: {
229  in_stream >> d->clientList;
230  } break;
231 
232  case KMessageServer::EVNT_CLIENT_CONNECTED: {
233  quint32 id;
234  in_stream >> id;
235 
236  if (d->clientList.contains(id))
237  qCWarning(GAMES_PRIVATE_KGAME) << ": Adding a client that already existed!";
238  else
239  d->clientList.append(id);
240 
242  } break;
243 
244  case KMessageServer::EVNT_CLIENT_DISCONNECTED: {
245  quint32 id;
246  qint8 broken;
247  in_stream >> id >> broken;
248 
249  if (!d->clientList.contains(id))
250  qCWarning(GAMES_PRIVATE_KGAME) << ": Removing a client that doesn't exist!";
251  else
252  d->clientList.removeAll(id);
253 
254  Q_EMIT eventClientDisconnected(id, bool(broken));
255  } break;
256 
257  default:
258  unknown = true;
259  }
260 
261  if (!unknown && !in_buffer.atEnd())
262  qCWarning(GAMES_PRIVATE_KGAME) << ": Extra data received for message ID" << messageID;
263 
264  Q_EMIT serverMessageReceived(msg, unknown);
265 
266  if (unknown)
267  qCWarning(GAMES_PRIVATE_KGAME) << ": received unknown message ID" << messageID;
268 }
269 
271 {
272  if (d->isLocked) {
273  return;
274  }
275  if (d->delayedMessages.count() == 0) {
276  qCDebug(GAMES_PRIVATE_KGAME) << ": no messages delayed";
277  return;
278  }
279  QByteArray first = d->delayedMessages.front();
280  d->delayedMessages.pop_front();
281  processMessage(first);
282 }
283 
285 {
286  qCDebug(GAMES_PRIVATE_KGAME) << ": timer single shot for removeBrokenConnection" << this;
287  // MH We cannot directly delete the socket. otherwise QSocket crashes
288  QTimer::singleShot(0, this, &KMessageClient::removeBrokenConnection2);
289  return;
290 }
291 
292 void KMessageClient::removeBrokenConnection2()
293 {
294  qCDebug(GAMES_PRIVATE_KGAME) << ": Broken:Deleting the connection object" << this;
295 
297  delete d->connection;
298  d->connection = nullptr;
299  d->adminID = 0;
301  qCDebug(GAMES_PRIVATE_KGAME) << ": Broken:Deleting the connection object DONE";
302 }
303 
305 {
306  qCDebug(GAMES_PRIVATE_KGAME) << ": Disconnect:Deleting the connection object";
307 
309  delete d->connection;
310  d->connection = nullptr;
311  d->adminID = 0;
313  qCDebug(GAMES_PRIVATE_KGAME) << ": Disconnect:Deleting the connection object DONE";
314 }
315 
317 {
318  d->isLocked = true;
319 }
320 
322 {
323  d->isLocked = false;
324  for (int i = 0; i < d->delayedMessages.count(); i++) {
326  }
327 }
328 
330 {
331  return d->delayedMessages.count();
332 }
void setServer(const QString &host, quint16 port)
Connects the client to (another) server.
virtual bool open(QIODevice::OpenMode flags) override
void connectionBroken()
This signal is emitted when the connection is closed.
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,...
Q_EMITQ_EMIT
void connectionBroken()
This signal is emitted when the connection to the KMessageServer is broken.
unsigned int delayedMessageCount() const
quint16 peerPort() const
void serverMessageReceived(const QByteArray &msg, bool &unknown)
This signal is emitted on every message that came from the server.
void processFirstMessage()
Called from unlock() (using QTimer::singleShot) until all delayed messages are delivered.
QString peerName() const
quint32 id() const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool isNetwork() const
void sendBroadcast(const QByteArray &msg)
Sends a message to all the clients connected to the server, including ourself.
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 unlock()
Deliver every message that was delayed by lock() and actually deliver all messages that get received ...
void sendServerMessage(const QByteArray &msg)
Sends a message to the KMessageServer.
void addClient(KMessageIO *)
Adds a new KMessageIO object to the communication server.
void sendForward(const QByteArray &msg, const QList< quint32 > &clients)
Sends a message to all the clients in a list.
~KMessageClient() override
Destructor.
virtual void processIncomingMessage(const QByteArray &msg)
This slot is called from the signal KMessageIO::received whenever a message from the KMessageServer a...
int receivers(const char *signal) const const
void disconnect()
Corresponds to setServer(0); but also emits the connectionBroken signal.
void adminStatusChanged(bool isAdmin)
This signal is emitted when this client becomes the admin client or when it loses the admin client st...
void setData(const QByteArray &data)
void received(const QByteArray &msg)
This signal is emitted when /e send() on the connected KMessageIO object is called.
QList< quint32 > clientList() const
ScriptableExtension * host() const
void lock()
Once this function is called no message will be received anymore.
bool isConnected() const
bool isAdmin() const
virtual void processMessage(const QByteArray &msg)
This slot is called from processIncomingMessage or processFirstMessage, depending on whether the clie...
QByteArray readAll()
void eventClientConnected(quint32 clientID)
This signal is emitted when another client has connected to the server.
A server for message sending and broadcasting, using TCP/IP connections.
virtual bool atEnd() const const override
KMessageClient(QObject *parent=nullptr)
Constructor.
virtual void removeBrokenConnection()
This slot is called from the signal KMessageIO::connectionBroken.
void aboutToDisconnect(quint32 id)
This signal is emitted right before the client disconnects.
void broadcastReceived(const QByteArray &msg, quint32 senderID)
This signal is emitted when the client receives a broadcast message from the KMessageServer,...
quint32 adminId() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon May 8 2023 03:49:44 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.