File indexing completed on 2024-12-22 03:52:57

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 "kmessageserver.h"
0009 #include "kmessageserver_p.h"
0010 
0011 // own
0012 #include "kmessageio.h"
0013 #include <kdegamesprivate_kgame_logging.h>
0014 // Qt
0015 #include <QBuffer>
0016 #include <QDataStream>
0017 #include <QIODevice>
0018 #include <QList>
0019 #include <QQueue>
0020 #include <QTimer>
0021 
0022 // --------------- internal class KMessageServerSocket
0023 
0024 KMessageServerSocket::KMessageServerSocket(quint16 port, QObject *parent)
0025     : QTcpServer(parent)
0026 {
0027     listen(QHostAddress::Any, port);
0028     connect(this, &KMessageServerSocket::newConnection, this, &KMessageServerSocket::slotNewConnection);
0029 }
0030 
0031 KMessageServerSocket::~KMessageServerSocket()
0032 {
0033 }
0034 
0035 void KMessageServerSocket::slotNewConnection()
0036 {
0037     if (hasPendingConnections()) {
0038         Q_EMIT newClientConnected(new KMessageSocket(nextPendingConnection()));
0039     }
0040 }
0041 
0042 // ---------------- class for storing an incoming message
0043 
0044 class MessageBuffer
0045 {
0046 public:
0047     MessageBuffer(quint32 clientID, const QByteArray &messageData)
0048         : id(clientID)
0049         , data(messageData)
0050     {
0051     }
0052     ~MessageBuffer()
0053     {
0054     }
0055     quint32 id;
0056     QByteArray data;
0057 };
0058 
0059 // ---------------- KMessageServer's private class
0060 
0061 class KMessageServerPrivate
0062 {
0063 public:
0064     KMessageServerPrivate() = default;
0065 
0066     ~KMessageServerPrivate()
0067     {
0068         qDeleteAll(mClientList);
0069         qDeleteAll(mMessageQueue);
0070     }
0071 
0072 public:
0073     int mMaxClients = -1;
0074     int mGameId = 1;
0075     quint16 mCookie;
0076     quint32 mUniqueClientNumber = 1;
0077     quint32 mAdminID = 0;
0078 
0079     KMessageServerSocket *mServerSocket = nullptr;
0080 
0081     QList<KMessageIO *> mClientList;
0082     QQueue<MessageBuffer *> mMessageQueue;
0083     QTimer mTimer;
0084     bool mIsRecursive = false;
0085 };
0086 
0087 // ------------------ KMessageServer
0088 
0089 KMessageServer::KMessageServer(quint16 cookie, QObject *parent)
0090     : QObject(parent)
0091     , d(new KMessageServerPrivate)
0092 {
0093     d->mCookie = cookie;
0094     connect(&(d->mTimer), &QTimer::timeout, this, &KMessageServer::processOneMessage);
0095     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "CREATE(KMessageServer=" << this << ") cookie=" << d->mCookie << "sizeof(this)=" << sizeof(KMessageServer);
0096 }
0097 
0098 KMessageServer::~KMessageServer()
0099 {
0100     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "this=" << this;
0101     Debug();
0102     stopNetwork();
0103     deleteClients();
0104     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "done";
0105 }
0106 
0107 //------------------------------------- TCP/IP server stuff
0108 
0109 bool KMessageServer::initNetwork(quint16 port)
0110 {
0111     qCDebug(KDEGAMESPRIVATE_KGAME_LOG);
0112 
0113     if (d->mServerSocket) {
0114         qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": We were already offering connections!";
0115         delete d->mServerSocket;
0116     }
0117 
0118     d->mServerSocket = new KMessageServerSocket(port);
0119     d->mIsRecursive = false;
0120 
0121     if (!d->mServerSocket || !d->mServerSocket->isListening()) {
0122         qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << ": Serversocket::ok() == false";
0123         delete d->mServerSocket;
0124         d->mServerSocket = nullptr;
0125         return false;
0126     }
0127 
0128     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Now listening to port " << d->mServerSocket->serverPort();
0129     connect(d->mServerSocket, &KMessageServerSocket::newClientConnected, this, &KMessageServer::addClient);
0130     return true;
0131 }
0132 
0133 quint16 KMessageServer::serverPort() const
0134 {
0135     if (d->mServerSocket)
0136         return d->mServerSocket->serverPort();
0137     else
0138         return 0;
0139 }
0140 
0141 void KMessageServer::stopNetwork()
0142 {
0143     if (d->mServerSocket) {
0144         delete d->mServerSocket;
0145         d->mServerSocket = nullptr;
0146     }
0147 }
0148 
0149 bool KMessageServer::isOfferingConnections() const
0150 {
0151     return d->mServerSocket != nullptr;
0152 }
0153 
0154 //----------------------------------------------- adding / removing clients
0155 
0156 void KMessageServer::addClient(KMessageIO *client)
0157 {
0158     QByteArray msg;
0159 
0160     // maximum number of clients reached?
0161     if (d->mMaxClients >= 0 && d->mMaxClients <= clientCount()) {
0162         qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << ": Maximum number of clients reached!";
0163         return;
0164     }
0165 
0166     // give it a unique ID
0167     client->setId(uniqueClientNumber());
0168     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ":" << client->id();
0169 
0170     // connect its signals
0171     connect(client, &KMessageIO::connectionBroken, this, &KMessageServer::removeBrokenClient);
0172     connect(client, &KMessageIO::received, this, &KMessageServer::getReceivedMessage);
0173 
0174     // Tell everyone about the new guest
0175     // Note: The new client doesn't get this message!
0176     QDataStream(&msg, QIODevice::WriteOnly) << quint32(EVNT_CLIENT_CONNECTED) << client->id();
0177     broadcastMessage(msg);
0178 
0179     // add to our list
0180     d->mClientList.push_back(client);
0181 
0182     // tell it its ID
0183     QDataStream(&msg, QIODevice::WriteOnly) << quint32(ANS_CLIENT_ID) << client->id();
0184     client->send(msg);
0185 
0186     // Give it the complete list of client IDs
0187     QDataStream(&msg, QIODevice::WriteOnly) << quint32(ANS_CLIENT_LIST) << clientIDs();
0188     client->send(msg);
0189 
0190     if (clientCount() == 1) {
0191         // if it is the first client, it becomes the admin
0192         setAdmin(client->id());
0193     } else {
0194         // otherwise tell it who is the admin
0195         QDataStream(&msg, QIODevice::WriteOnly) << quint32(ANS_ADMIN_ID) << adminID();
0196         client->send(msg);
0197     }
0198 
0199     Q_EMIT clientConnected(client);
0200 }
0201 
0202 void KMessageServer::removeClient(KMessageIO *client, bool broken)
0203 {
0204     quint32 clientID = client->id();
0205     if (!d->mClientList.removeAll(client)) {
0206         qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << ": Deleting client that wasn't added before!";
0207         return;
0208     }
0209 
0210     // tell everyone about the removed client
0211     QByteArray msg;
0212     QDataStream(&msg, QIODevice::WriteOnly) << quint32(EVNT_CLIENT_DISCONNECTED) << client->id() << (qint8)broken;
0213     broadcastMessage(msg);
0214 
0215     // If it was the admin, select a new admin.
0216     if (clientID == adminID()) {
0217         if (!d->mClientList.isEmpty())
0218             setAdmin(d->mClientList.front()->id());
0219         else
0220             setAdmin(0);
0221     }
0222 }
0223 
0224 void KMessageServer::deleteClients()
0225 {
0226     qDeleteAll(d->mClientList);
0227     d->mClientList.clear();
0228     d->mAdminID = 0;
0229 }
0230 
0231 void KMessageServer::removeBrokenClient()
0232 {
0233     KMessageIO *client = sender() ? qobject_cast<KMessageIO *>(sender()) : nullptr;
0234     if (!client) {
0235         qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << ": sender of the signal was not a KMessageIO object!";
0236         return;
0237     }
0238 
0239     Q_EMIT connectionLost(client);
0240     removeClient(client, true);
0241 }
0242 
0243 void KMessageServer::setMaxClients(int c)
0244 {
0245     d->mMaxClients = c;
0246 }
0247 
0248 int KMessageServer::maxClients() const
0249 {
0250     return d->mMaxClients;
0251 }
0252 
0253 int KMessageServer::clientCount() const
0254 {
0255     return d->mClientList.count();
0256 }
0257 
0258 QList<quint32> KMessageServer::clientIDs() const
0259 {
0260     QList<quint32> list;
0261     list.reserve(d->mClientList.size());
0262     for (QList<KMessageIO *>::iterator iter(d->mClientList.begin()); iter != d->mClientList.end(); ++iter)
0263         list.append((*iter)->id());
0264     return list;
0265 }
0266 
0267 KMessageIO *KMessageServer::findClient(quint32 no) const
0268 {
0269     if (no == 0)
0270         no = d->mAdminID;
0271 
0272     QList<KMessageIO *>::iterator iter = d->mClientList.begin();
0273     while (iter != d->mClientList.end()) {
0274         if ((*iter)->id() == no)
0275             return (*iter);
0276         ++iter;
0277     }
0278     return nullptr;
0279 }
0280 
0281 quint32 KMessageServer::adminID() const
0282 {
0283     return d->mAdminID;
0284 }
0285 
0286 void KMessageServer::setAdmin(quint32 adminID)
0287 {
0288     // Trying to set the client that is already admin => nothing to do
0289     if (adminID == d->mAdminID)
0290         return;
0291 
0292     if (adminID > 0 && findClient(adminID) == nullptr) {
0293         qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << "Trying to set a new admin that doesn't exist!";
0294         return;
0295     }
0296 
0297     d->mAdminID = adminID;
0298 
0299     QByteArray msg;
0300     QDataStream(&msg, QIODevice::WriteOnly) << quint32(ANS_ADMIN_ID) << adminID;
0301 
0302     // Tell everyone about the new master
0303     broadcastMessage(msg);
0304 }
0305 
0306 //------------------------------------------- ID stuff
0307 
0308 quint32 KMessageServer::uniqueClientNumber() const
0309 {
0310     return d->mUniqueClientNumber++;
0311 }
0312 
0313 // --------------------- Messages ---------------------------
0314 
0315 void KMessageServer::broadcastMessage(const QByteArray &msg)
0316 {
0317     for (QList<KMessageIO *>::iterator iter(d->mClientList.begin()); iter != d->mClientList.end(); ++iter)
0318         (*iter)->send(msg);
0319 }
0320 
0321 void KMessageServer::sendMessage(quint32 id, const QByteArray &msg)
0322 {
0323     KMessageIO *client = findClient(id);
0324     if (client)
0325         client->send(msg);
0326 }
0327 
0328 void KMessageServer::sendMessage(const QList<quint32> &ids, const QByteArray &msg)
0329 {
0330     for (quint32 id : ids) {
0331         sendMessage(id, msg);
0332     }
0333 }
0334 
0335 void KMessageServer::getReceivedMessage(const QByteArray &msg)
0336 {
0337     KMessageIO *client = sender() ? qobject_cast<KMessageIO *>(sender()) : nullptr;
0338     if (!client) {
0339         qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << ": slot was not called from KMessageIO!";
0340         return;
0341     }
0342     // qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": size=" << msg.size();
0343     quint32 clientID = client->id();
0344 
0345     // QByteArray *ta=new QByteArray;
0346     // ta->duplicate(msg);
0347     // d->mMessageQueue.enqueue (new MessageBuffer (clientID, *ta));
0348 
0349     d->mMessageQueue.enqueue(new MessageBuffer(clientID, msg));
0350     if (!d->mTimer.isActive())
0351         d->mTimer.start(0); // AB: should be , TRUE i guess
0352 }
0353 
0354 void KMessageServer::processOneMessage()
0355 {
0356     // This shouldn't happen, since the timer should be stopped before. But only to be sure!
0357     if (d->mMessageQueue.isEmpty()) {
0358         d->mTimer.stop();
0359         return;
0360     }
0361     if (d->mIsRecursive) {
0362         return;
0363     }
0364     d->mIsRecursive = true;
0365 
0366     MessageBuffer *msg_buf = d->mMessageQueue.head();
0367 
0368     quint32 clientID = msg_buf->id;
0369     QBuffer in_buffer(&msg_buf->data);
0370     in_buffer.open(QIODevice::ReadOnly);
0371     QDataStream in_stream(&in_buffer);
0372 
0373     QByteArray out_msg;
0374     QBuffer out_buffer(&out_msg);
0375     out_buffer.open(QIODevice::WriteOnly);
0376     QDataStream out_stream(&out_buffer);
0377 
0378     bool unknown = false;
0379 
0380     quint32 messageID;
0381     in_stream >> messageID;
0382     // qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": got message with messageID=" << messageID;
0383     switch (messageID) {
0384     case REQ_BROADCAST:
0385         out_stream << quint32(MSG_BROADCAST) << clientID;
0386         // FIXME, compiler bug?
0387         // this should be okay, since QBuffer is subclass of QIODevice! :
0388         // out_buffer.write (in_buffer.readAll());
0389         out_buffer.QIODevice::write(in_buffer.readAll());
0390         broadcastMessage(out_msg);
0391         break;
0392 
0393     case REQ_FORWARD: {
0394         QList<quint32> clients;
0395         in_stream >> clients;
0396         out_stream << quint32(MSG_FORWARD) << clientID << clients;
0397         // see above!
0398         out_buffer.QIODevice::write(in_buffer.readAll());
0399         sendMessage(clients, out_msg);
0400     } break;
0401 
0402     case REQ_CLIENT_ID:
0403         out_stream << quint32(ANS_CLIENT_ID) << clientID;
0404         sendMessage(clientID, out_msg);
0405         break;
0406 
0407     case REQ_ADMIN_ID:
0408         out_stream << quint32(ANS_ADMIN_ID) << d->mAdminID;
0409         sendMessage(clientID, out_msg);
0410         break;
0411 
0412     case REQ_ADMIN_CHANGE:
0413         if (clientID == d->mAdminID) {
0414             quint32 newAdmin;
0415             in_stream >> newAdmin;
0416             setAdmin(newAdmin);
0417         }
0418         break;
0419 
0420     case REQ_REMOVE_CLIENT:
0421         if (clientID == d->mAdminID) {
0422             QList<quint32> client_list;
0423             in_stream >> client_list;
0424             for (quint32 id : std::as_const(client_list)) {
0425                 KMessageIO *client = findClient(id);
0426                 if (client)
0427                     removeClient(client, false);
0428                 else
0429                     qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": removing non-existing clientID";
0430             }
0431         }
0432         break;
0433 
0434     case REQ_MAX_NUM_CLIENTS:
0435         if (clientID == d->mAdminID) {
0436             qint32 maximum_clients;
0437             in_stream >> maximum_clients;
0438             setMaxClients(maximum_clients);
0439         }
0440         break;
0441 
0442     case REQ_CLIENT_LIST: {
0443         out_stream << quint32(ANS_CLIENT_LIST) << clientIDs();
0444         sendMessage(clientID, out_msg);
0445     } break;
0446 
0447     default:
0448         unknown = true;
0449     }
0450 
0451     // check if all the data has been used
0452     if (!unknown && !in_buffer.atEnd())
0453         qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Extra data received for message ID" << messageID;
0454 
0455     Q_EMIT messageReceived(msg_buf->data, clientID, unknown);
0456 
0457     if (unknown)
0458         qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": received unknown message ID" << messageID;
0459 
0460     // remove the message, since we are ready with it
0461     delete d->mMessageQueue.dequeue();
0462     if (d->mMessageQueue.isEmpty())
0463         d->mTimer.stop();
0464     d->mIsRecursive = false;
0465 }
0466 
0467 void KMessageServer::Debug()
0468 {
0469     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "------------------ KMESSAGESERVER -----------------------";
0470     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "MaxClients :   " << maxClients();
0471     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "NoOfClients :  " << clientCount();
0472     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "---------------------------------------------------";
0473 }
0474 
0475 #include "moc_kmessageserver.cpp"
0476 #include "moc_kmessageserver_p.cpp"