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 #include <kdegamesprivate_kgame_logging.h>
14 // Qt
15 #include <QBuffer>
16 #include <QDataStream>
17 #include <QList>
18 #include <QTimer>
19 // Std
20 #include <cstdio>
21 
22 class KMessageClientPrivate
23 {
24 public:
25  KMessageClientPrivate() = default;
26 
27  ~KMessageClientPrivate()
28  {
29  delete connection;
30  }
31 
32 public:
33  quint32 adminID = 0;
34  QList<quint32> clientList;
35  KMessageIO *connection = nullptr;
36 
37  bool isLocked = false;
38  QList<QByteArray> delayedMessages;
39 };
40 
42  : QObject(parent)
43  , d(new KMessageClientPrivate)
44 {
45 }
46 
48 {
49  d->delayedMessages.clear();
50 }
51 
52 // -- setServer stuff
53 
54 void KMessageClient::setServer(const QString &host, quint16 port)
55 {
56  setServer(new KMessageSocket(host, port));
57 }
58 
60 {
61  KMessageDirect *serverIO = new KMessageDirect();
62  setServer(new KMessageDirect(serverIO));
63  server->addClient(serverIO);
64 }
65 
67 {
68  if (d->connection) {
69  delete d->connection;
70  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": We are changing the server!";
71  }
72 
73  d->connection = connection;
74  if (connection) {
77  }
78 }
79 
80 // -- id stuff
81 
82 quint32 KMessageClient::id() const
83 {
84  return (d->connection) ? d->connection->id() : 0;
85 }
86 
88 {
89  return id() != 0 && id() == adminId();
90 }
91 
92 quint32 KMessageClient::adminId() const
93 {
94  return d->adminID;
95 }
96 
98 {
99  return d->clientList;
100 }
101 
103 {
104  return d->connection && d->connection->isConnected();
105 }
106 
108 {
109  return isConnected() ? d->connection->isNetwork() : false;
110 }
111 
113 {
114  return d->connection ? d->connection->peerPort() : 0;
115 }
116 
118 {
119  return d->connection ? d->connection->peerName() : QStringLiteral("localhost");
120 }
121 
122 // --------------------- Sending messages
123 
125 {
126  if (!d->connection) {
127  qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": We have no connection yet!";
128  return;
129  }
130  d->connection->send(msg);
131 }
132 
134 {
135  QByteArray sendBuffer;
136  QBuffer buffer(&sendBuffer);
137  buffer.open(QIODevice::WriteOnly);
138  QDataStream stream(&buffer);
139 
140  stream << static_cast<quint32>(KMessageServer::REQ_BROADCAST);
141  buffer.QIODevice::write(msg);
142  sendServerMessage(sendBuffer);
143 }
144 
145 void KMessageClient::sendForward(const QByteArray &msg, const QList<quint32> &clients)
146 {
147  QByteArray sendBuffer;
148  QBuffer buffer(&sendBuffer);
149  buffer.open(QIODevice::WriteOnly);
150  QDataStream stream(&buffer);
151 
152  stream << static_cast<quint32>(KMessageServer::REQ_FORWARD) << clients;
153  buffer.QIODevice::write(msg);
154  sendServerMessage(sendBuffer);
155 }
156 
157 void KMessageClient::sendForward(const QByteArray &msg, quint32 client)
158 {
159  sendForward(msg, QList<quint32>{client});
160 }
161 
162 // --------------------- Receiving and processing messages
163 
165 {
166  if (d->isLocked) {
167  d->delayedMessages.append(msg);
168  return;
169  }
170  if (!d->delayedMessages.isEmpty()) {
171  d->delayedMessages.append(msg);
172  QByteArray first = d->delayedMessages.front();
173  d->delayedMessages.pop_front();
174  processMessage(first);
175  } else {
176  processMessage(msg);
177  }
178 }
179 
181 {
182  if (d->isLocked) { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage
183  d->delayedMessages.append(msg);
184  return;
185  }
186  QBuffer in_buffer;
187  in_buffer.setData(msg);
188  in_buffer.open(QIODevice::ReadOnly);
189  QDataStream in_stream(&in_buffer);
190 
191  bool unknown = false;
192 
193  quint32 messageID;
194  in_stream >> messageID;
195  switch (messageID) {
196  case KMessageServer::MSG_BROADCAST: {
197  quint32 clientID;
198  in_stream >> clientID;
199  Q_EMIT broadcastReceived(in_buffer.readAll(), clientID);
200  } break;
201 
202  case KMessageServer::MSG_FORWARD: {
203  quint32 clientID;
205  in_stream >> clientID >> receivers;
206  Q_EMIT forwardReceived(in_buffer.readAll(), clientID, receivers);
207  } break;
208 
209  case KMessageServer::ANS_CLIENT_ID: {
210  bool old_admin = isAdmin();
211  quint32 clientID;
212  in_stream >> clientID;
213  d->connection->setId(clientID);
214  if (old_admin != isAdmin())
216  } break;
217 
218  case KMessageServer::ANS_ADMIN_ID: {
219  bool old_admin = isAdmin();
220  in_stream >> d->adminID;
221  if (old_admin != isAdmin())
223  } break;
224 
225  case KMessageServer::ANS_CLIENT_LIST: {
226  in_stream >> d->clientList;
227  } break;
228 
229  case KMessageServer::EVNT_CLIENT_CONNECTED: {
230  quint32 id;
231  in_stream >> id;
232 
233  if (d->clientList.contains(id))
234  qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Adding a client that already existed!";
235  else
236  d->clientList.append(id);
237 
239  } break;
240 
241  case KMessageServer::EVNT_CLIENT_DISCONNECTED: {
242  quint32 id;
243  qint8 broken;
244  in_stream >> id >> broken;
245 
246  if (!d->clientList.contains(id))
247  qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Removing a client that doesn't exist!";
248  else
249  d->clientList.removeAll(id);
250 
251  Q_EMIT eventClientDisconnected(id, bool(broken));
252  } break;
253 
254  default:
255  unknown = true;
256  }
257 
258  if (!unknown && !in_buffer.atEnd())
259  qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Extra data received for message ID" << messageID;
260 
261  Q_EMIT serverMessageReceived(msg, unknown);
262 
263  if (unknown)
264  qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": received unknown message ID" << messageID;
265 }
266 
268 {
269  if (d->isLocked) {
270  return;
271  }
272  if (d->delayedMessages.count() == 0) {
273  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": no messages delayed";
274  return;
275  }
276  QByteArray first = d->delayedMessages.front();
277  d->delayedMessages.pop_front();
278  processMessage(first);
279 }
280 
282 {
283  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": timer single shot for removeBrokenConnection" << this;
284  // MH We cannot directly delete the socket. otherwise QSocket crashes
285  QTimer::singleShot(0, this, &KMessageClient::removeBrokenConnection2);
286  return;
287 }
288 
289 void KMessageClient::removeBrokenConnection2()
290 {
291  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Broken:Deleting the connection object" << this;
292 
294  delete d->connection;
295  d->connection = nullptr;
296  d->adminID = 0;
298  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Broken:Deleting the connection object DONE";
299 }
300 
302 {
303  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Disconnect:Deleting the connection object";
304 
306  delete d->connection;
307  d->connection = nullptr;
308  d->adminID = 0;
310  qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Disconnect:Deleting the connection object DONE";
311 }
312 
314 {
315  d->isLocked = true;
316 }
317 
319 {
320  d->isLocked = false;
321  for (int i = 0; i < d->delayedMessages.count(); i++) {
323  }
324 }
325 
327 {
328  return d->delayedMessages.count();
329 }
330 
331 #include "moc_kmessageclient.cpp"
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 Thu Nov 30 2023 04:08:07 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.