KDEGames

kmessageclient.cpp
1/*
2 This file is part of the KDE games library
3 SPDX-FileCopyrightText: 2001 Burkhard Lehner <Burkhard.Lehner@gmx.de>
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
22class KMessageClientPrivate
23{
24public:
25 KMessageClientPrivate() = default;
26
27 ~KMessageClientPrivate()
28 {
29 delete connection;
30 }
31
32public:
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
54void KMessageClient::setServer(const QString &host, quint16 port)
55{
56 setServer(new KMessageSocket(host, port));
57}
58
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
82quint32 KMessageClient::id() const
83{
84 return (d->connection) ? d->connection->id() : 0;
85}
86
88{
89 return id() != 0 && id() == adminId();
90}
91
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{
136 QBuffer buffer(&sendBuffer);
138 QDataStream stream(&buffer);
139
140 stream << static_cast<quint32>(KMessageServer::REQ_BROADCAST);
141 buffer.QIODevice::write(msg);
143}
144
146{
148 QBuffer buffer(&sendBuffer);
150 QDataStream stream(&buffer);
151
152 stream << static_cast<quint32>(KMessageServer::REQ_FORWARD) << clients;
153 buffer.QIODevice::write(msg);
155}
156
157void 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 }
187 in_buffer.setData(msg);
190
191 bool unknown = false;
192
193 quint32 messageID;
194 in_stream >> messageID;
195 switch (messageID) {
196 case KMessageServer::MSG_BROADCAST: {
197 quint32 clientID;
200 } break;
201
202 case KMessageServer::MSG_FORWARD: {
203 quint32 clientID;
207 } break;
208
209 case KMessageServer::ANS_CLIENT_ID: {
210 bool old_admin = isAdmin();
211 quint32 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
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
289void 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"
~KMessageClient() override
Destructor.
void processFirstMessage()
Called from unlock() (using QTimer::singleShot) until all delayed messages are delivered.
bool isConnected() const
virtual void processMessage(const QByteArray &msg)
This slot is called from processIncomingMessage or processFirstMessage, depending on whether the clie...
bool isNetwork() const
void lock()
Once this function is called no message will be received anymore.
quint32 adminId() const
void eventClientConnected(quint32 clientID)
This signal is emitted when another client has connected to the server.
quint16 peerPort() const
void sendBroadcast(const QByteArray &msg)
Sends a message to all the clients connected to the server, including ourself.
void broadcastReceived(const QByteArray &msg, quint32 senderID)
This signal is emitted when the client receives a broadcast message from the KMessageServer,...
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...
virtual void processIncomingMessage(const QByteArray &msg)
This slot is called from the signal KMessageIO::received whenever a message from the KMessageServer a...
QString peerName() const
void disconnect()
Corresponds to setServer(0); but also emits the connectionBroken signal.
void connectionBroken()
This signal is emitted when the connection to the KMessageServer is broken.
void sendForward(const QByteArray &msg, const QList< quint32 > &clients)
Sends a message to all the clients in a list.
void serverMessageReceived(const QByteArray &msg, bool &unknown)
This signal is emitted on every message that came from the server.
void adminStatusChanged(bool isAdmin)
This signal is emitted when this client becomes the admin client or when it loses the admin client st...
bool isAdmin() const
unsigned int delayedMessageCount() const
void sendServerMessage(const QByteArray &msg)
Sends a message to the KMessageServer.
void setServer(const QString &host, quint16 port)
Connects the client to (another) server.
void unlock()
Deliver every message that was delayed by lock() and actually deliver all messages that get received ...
quint32 id() const
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.
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,...
QList< quint32 > clientList() const
This class implements the message communication using function calls directly.
Definition kmessageio.h:279
This abstract base class represents one end of a message connections between two clients.
Definition kmessageio.h:44
void connectionBroken()
This signal is emitted when the connection is closed.
void received(const QByteArray &msg)
This signal is emitted when /e send() on the connected KMessageIO object is called.
A server for message sending and broadcasting, using TCP/IP connections.
void addClient(KMessageIO *)
Adds a new KMessageIO object to the communication server.
This class implements the message communication using a TCP/IP socket.
Definition kmessageio.h:150
virtual bool open(OpenMode flags) override
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
int receivers(const char *signal) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:50 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.