File indexing completed on 2024-06-16 03:57:56

0001 /*
0002     This file is part of the KDE games library
0003     SPDX-FileCopyrightText: 2001 Burkhard Lehner <Burkhard.Lehner@gmx.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "kmessageclient.h"
0009 
0010 // own
0011 #include "kmessageio.h"
0012 #include "kmessageserver.h"
0013 #include <kdegamesprivate_kgame_logging.h>
0014 // Qt
0015 #include <QBuffer>
0016 #include <QDataStream>
0017 #include <QList>
0018 #include <QTimer>
0019 // Std
0020 #include <cstdio>
0021 
0022 class KMessageClientPrivate
0023 {
0024 public:
0025     KMessageClientPrivate() = default;
0026 
0027     ~KMessageClientPrivate()
0028     {
0029         delete connection;
0030     }
0031 
0032 public:
0033     quint32 adminID = 0;
0034     QList<quint32> clientList;
0035     KMessageIO *connection = nullptr;
0036 
0037     bool isLocked = false;
0038     QList<QByteArray> delayedMessages;
0039 };
0040 
0041 KMessageClient::KMessageClient(QObject *parent)
0042     : QObject(parent)
0043     , d(new KMessageClientPrivate)
0044 {
0045 }
0046 
0047 KMessageClient::~KMessageClient()
0048 {
0049     d->delayedMessages.clear();
0050 }
0051 
0052 // -- setServer stuff
0053 
0054 void KMessageClient::setServer(const QString &host, quint16 port)
0055 {
0056     setServer(new KMessageSocket(host, port));
0057 }
0058 
0059 void KMessageClient::setServer(KMessageServer *server)
0060 {
0061     KMessageDirect *serverIO = new KMessageDirect();
0062     setServer(new KMessageDirect(serverIO));
0063     server->addClient(serverIO);
0064 }
0065 
0066 void KMessageClient::setServer(KMessageIO *connection)
0067 {
0068     if (d->connection) {
0069         delete d->connection;
0070         qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": We are changing the server!";
0071     }
0072 
0073     d->connection = connection;
0074     if (connection) {
0075         connect(connection, &KMessageIO::received, this, &KMessageClient::processIncomingMessage);
0076         connect(connection, &KMessageIO::connectionBroken, this, &KMessageClient::removeBrokenConnection);
0077     }
0078 }
0079 
0080 // -- id stuff
0081 
0082 quint32 KMessageClient::id() const
0083 {
0084     return (d->connection) ? d->connection->id() : 0;
0085 }
0086 
0087 bool KMessageClient::isAdmin() const
0088 {
0089     return id() != 0 && id() == adminId();
0090 }
0091 
0092 quint32 KMessageClient::adminId() const
0093 {
0094     return d->adminID;
0095 }
0096 
0097 QList<quint32> KMessageClient::clientList() const
0098 {
0099     return d->clientList;
0100 }
0101 
0102 bool KMessageClient::isConnected() const
0103 {
0104     return d->connection && d->connection->isConnected();
0105 }
0106 
0107 bool KMessageClient::isNetwork() const
0108 {
0109     return isConnected() ? d->connection->isNetwork() : false;
0110 }
0111 
0112 quint16 KMessageClient::peerPort() const
0113 {
0114     return d->connection ? d->connection->peerPort() : 0;
0115 }
0116 
0117 QString KMessageClient::peerName() const
0118 {
0119     return d->connection ? d->connection->peerName() : QStringLiteral("localhost");
0120 }
0121 
0122 // --------------------- Sending messages
0123 
0124 void KMessageClient::sendServerMessage(const QByteArray &msg)
0125 {
0126     if (!d->connection) {
0127         qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": We have no connection yet!";
0128         return;
0129     }
0130     d->connection->send(msg);
0131 }
0132 
0133 void KMessageClient::sendBroadcast(const QByteArray &msg)
0134 {
0135     QByteArray sendBuffer;
0136     QBuffer buffer(&sendBuffer);
0137     buffer.open(QIODevice::WriteOnly);
0138     QDataStream stream(&buffer);
0139 
0140     stream << static_cast<quint32>(KMessageServer::REQ_BROADCAST);
0141     buffer.QIODevice::write(msg);
0142     sendServerMessage(sendBuffer);
0143 }
0144 
0145 void KMessageClient::sendForward(const QByteArray &msg, const QList<quint32> &clients)
0146 {
0147     QByteArray sendBuffer;
0148     QBuffer buffer(&sendBuffer);
0149     buffer.open(QIODevice::WriteOnly);
0150     QDataStream stream(&buffer);
0151 
0152     stream << static_cast<quint32>(KMessageServer::REQ_FORWARD) << clients;
0153     buffer.QIODevice::write(msg);
0154     sendServerMessage(sendBuffer);
0155 }
0156 
0157 void KMessageClient::sendForward(const QByteArray &msg, quint32 client)
0158 {
0159     sendForward(msg, QList<quint32>{client});
0160 }
0161 
0162 // --------------------- Receiving and processing messages
0163 
0164 void KMessageClient::processIncomingMessage(const QByteArray &msg)
0165 {
0166     if (d->isLocked) {
0167         d->delayedMessages.append(msg);
0168         return;
0169     }
0170     if (!d->delayedMessages.isEmpty()) {
0171         d->delayedMessages.append(msg);
0172         QByteArray first = d->delayedMessages.front();
0173         d->delayedMessages.pop_front();
0174         processMessage(first);
0175     } else {
0176         processMessage(msg);
0177     }
0178 }
0179 
0180 void KMessageClient::processMessage(const QByteArray &msg)
0181 {
0182     if (d->isLocked) { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage
0183         d->delayedMessages.append(msg);
0184         return;
0185     }
0186     QBuffer in_buffer;
0187     in_buffer.setData(msg);
0188     in_buffer.open(QIODevice::ReadOnly);
0189     QDataStream in_stream(&in_buffer);
0190 
0191     bool unknown = false;
0192 
0193     quint32 messageID;
0194     in_stream >> messageID;
0195     switch (messageID) {
0196     case KMessageServer::MSG_BROADCAST: {
0197         quint32 clientID;
0198         in_stream >> clientID;
0199         Q_EMIT broadcastReceived(in_buffer.readAll(), clientID);
0200     } break;
0201 
0202     case KMessageServer::MSG_FORWARD: {
0203         quint32 clientID;
0204         QList<quint32> receivers;
0205         in_stream >> clientID >> receivers;
0206         Q_EMIT forwardReceived(in_buffer.readAll(), clientID, receivers);
0207     } break;
0208 
0209     case KMessageServer::ANS_CLIENT_ID: {
0210         bool old_admin = isAdmin();
0211         quint32 clientID;
0212         in_stream >> clientID;
0213         d->connection->setId(clientID);
0214         if (old_admin != isAdmin())
0215             Q_EMIT adminStatusChanged(isAdmin());
0216     } break;
0217 
0218     case KMessageServer::ANS_ADMIN_ID: {
0219         bool old_admin = isAdmin();
0220         in_stream >> d->adminID;
0221         if (old_admin != isAdmin())
0222             Q_EMIT adminStatusChanged(isAdmin());
0223     } break;
0224 
0225     case KMessageServer::ANS_CLIENT_LIST: {
0226         in_stream >> d->clientList;
0227     } break;
0228 
0229     case KMessageServer::EVNT_CLIENT_CONNECTED: {
0230         quint32 id;
0231         in_stream >> id;
0232 
0233         if (d->clientList.contains(id))
0234             qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Adding a client that already existed!";
0235         else
0236             d->clientList.append(id);
0237 
0238         Q_EMIT eventClientConnected(id);
0239     } break;
0240 
0241     case KMessageServer::EVNT_CLIENT_DISCONNECTED: {
0242         quint32 id;
0243         qint8 broken;
0244         in_stream >> id >> broken;
0245 
0246         if (!d->clientList.contains(id))
0247             qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Removing a client that doesn't exist!";
0248         else
0249             d->clientList.removeAll(id);
0250 
0251         Q_EMIT eventClientDisconnected(id, bool(broken));
0252     } break;
0253 
0254     default:
0255         unknown = true;
0256     }
0257 
0258     if (!unknown && !in_buffer.atEnd())
0259         qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Extra data received for message ID" << messageID;
0260 
0261     Q_EMIT serverMessageReceived(msg, unknown);
0262 
0263     if (unknown)
0264         qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": received unknown message ID" << messageID;
0265 }
0266 
0267 void KMessageClient::processFirstMessage()
0268 {
0269     if (d->isLocked) {
0270         return;
0271     }
0272     if (d->delayedMessages.count() == 0) {
0273         qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": no messages delayed";
0274         return;
0275     }
0276     QByteArray first = d->delayedMessages.front();
0277     d->delayedMessages.pop_front();
0278     processMessage(first);
0279 }
0280 
0281 void KMessageClient::removeBrokenConnection()
0282 {
0283     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": timer single shot for removeBrokenConnection" << this;
0284     // MH We cannot directly delete the socket. otherwise QSocket crashes
0285     QTimer::singleShot(0, this, &KMessageClient::removeBrokenConnection2);
0286     return;
0287 }
0288 
0289 void KMessageClient::removeBrokenConnection2()
0290 {
0291     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Broken:Deleting the connection object" << this;
0292 
0293     Q_EMIT aboutToDisconnect(id());
0294     delete d->connection;
0295     d->connection = nullptr;
0296     d->adminID = 0;
0297     Q_EMIT connectionBroken();
0298     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Broken:Deleting the connection object DONE";
0299 }
0300 
0301 void KMessageClient::disconnect()
0302 {
0303     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Disconnect:Deleting the connection object";
0304 
0305     Q_EMIT aboutToDisconnect(id());
0306     delete d->connection;
0307     d->connection = nullptr;
0308     d->adminID = 0;
0309     Q_EMIT connectionBroken();
0310     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Disconnect:Deleting the connection object DONE";
0311 }
0312 
0313 void KMessageClient::lock()
0314 {
0315     d->isLocked = true;
0316 }
0317 
0318 void KMessageClient::unlock()
0319 {
0320     d->isLocked = false;
0321     for (int i = 0; i < d->delayedMessages.count(); i++) {
0322         QTimer::singleShot(0, this, &KMessageClient::processFirstMessage);
0323     }
0324 }
0325 
0326 unsigned int KMessageClient::delayedMessageCount() const
0327 {
0328     return d->delayedMessages.count();
0329 }
0330 
0331 #include "moc_kmessageclient.cpp"