File indexing completed on 2024-05-26 04:09: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 #ifndef __KMESSAGECLIENT_H__
0009 #define __KMESSAGECLIENT_H__
0010 
0011 // own
0012 #include "kdegamesprivate_export.h"
0013 // Qt
0014 #include <QObject>
0015 #include <QString>
0016 // Std
0017 #include <memory>
0018 
0019 class KMessageIO;
0020 class KMessageServer;
0021 class KMessageClientPrivate;
0022 
0023 /**
0024   \class KMessageClient kmessageclient.h <KGame/KMessageClient>
0025 
0026   @short A client to connect to a KMessageServer
0027 
0028   This class implements a client that can connect to a KMessageServer object.
0029   It can be used to exchange messages between clients.
0030 
0031   Usually you will connect the signals broadcastReceived and forwardReceived to
0032   some specific slots. In these slot methods you can analyze the messages that are
0033   sent to you from other clients.
0034 
0035   To send messages to other clients, use the methods sendBroadcast() (to send to all
0036   clients) or sendForward() (to send to a list of selected clients).
0037 
0038   If you want to communicate with the KMessageServer object directly (on a more low
0039   level base), use the method sendServerMessage to send a command to the server and
0040   connect to the signal serverMessageReceived to see all the incoming messages.
0041   In that case the messages must be of the format specified in KMessageServer.
0042   @author Burkhard Lehner <Burkhard.Lehner@gmx.de>
0043 */
0044 class KDEGAMESPRIVATE_EXPORT KMessageClient : public QObject
0045 {
0046     Q_OBJECT
0047 
0048 public:
0049     /**
0050       Constructor.
0051       Creates an unconnected KMessageClient object. Use setServer() later to connect to a
0052       KMessageServer object.
0053     */
0054     explicit KMessageClient(QObject *parent = nullptr);
0055 
0056     /**
0057       Destructor.
0058       Disconnects from the server, if any connection was established.
0059     */
0060     ~KMessageClient() override;
0061 
0062     /**
0063       @return The client ID of this client. Every client that is connected to a KMessageServer
0064       has a unique ID number.
0065 
0066       NOTE: As long as the object is not yet connected to the server, and as long as the server
0067       hasn't sent the client ID, this method returns 0.
0068     */
0069     quint32 id() const;
0070 
0071     /**
0072       @return Whether or not this client is the server admin.
0073       One of the clients connected to the server is the admin and can administrate the server
0074       (set maximum number of clients, remove clients, ...).
0075 
0076       If you use admin commands without being the admin, these commands are simply ignored by
0077       the server.
0078 
0079       NOTE: As long as you are not connected to a server, this method returns false.
0080     */
0081     bool isAdmin() const;
0082 
0083     /**
0084       @return The ID of the admin client on the message server.
0085     */
0086     quint32 adminId() const;
0087 
0088     /**
0089       @return The list of the IDs of all the message clients connected to the message server.
0090     */
0091     QList<quint32> clientList() const;
0092 
0093     /**
0094       Connects the client to (another) server.
0095 
0096       Tries to connect via a TCP/IP socket to a KMessageServer object
0097       on the given host, listening on the specified port.
0098 
0099       If we were already connected, the old connection is closed.
0100       @param host The name of the host to connect to. Must be either a hostname which can
0101       be resolved to an IP or just an IP
0102       @param port The port to connect to
0103     */
0104     void setServer(const QString &host, quint16 port);
0105 
0106     /**
0107       Connects the client to (another) server.
0108 
0109       Connects to the given server, using KMessageDirect.
0110       (The server object has to be in the same process.)
0111 
0112       If we were already connected, the old connection is closed.
0113       @param server The KMessageServer to connect to
0114     */
0115     void setServer(KMessageServer *server);
0116 
0117     /**
0118      * Corresponds to setServer(0); but also emits the connectionBroken signal
0119      */
0120     void disconnect();
0121 
0122     /**
0123       Connects the client to (another) server.
0124 
0125       To use this method, you have to create a KMessageIO object with new (indeed you must
0126       create an instance of a subclass of KMessageIO, e.g. KMessageSocket or KMessageDirect).
0127       This object must already be connected to the new server.
0128 
0129       Calling this method disconnects any earlier connection, and uses the new KMessageIO
0130       object instead. This object gets owned by the KMessageClient object, so don't delete
0131       or manipulate it afterwards.
0132 
0133       With this method it is possible to change the server on the fly. But be careful that
0134       there are no important messages from the old server not yet delivered.
0135 
0136       NOTE: It is very likely that we will have another client ID on the new server. The
0137       value returned by clientID may be a little outdated until the new server tells us
0138       our new ID.
0139 
0140       NOTE: The two other setServer methods are for convenience. If you use them, you don't
0141       have to create a KMessageIO object yourself.
0142     */
0143     virtual void setServer(KMessageIO *connection);
0144 
0145     /**
0146       @return True, if a connection to a KMessageServer has been started, and if the
0147       connection is ready for transferring data. (It will return false e.g. as long as
0148       a socket connection hasn't been established, and it will also return false after
0149       a socket connection is broken.)
0150     */
0151     bool isConnected() const;
0152 
0153     /**
0154       @return TRUE if isConnected() is true AND this is not a local (like
0155       KMessageDirect) connection.
0156     */
0157     bool isNetwork() const;
0158 
0159     /**
0160       @return 0 if isConnected() is FALSE, otherwise the port number this client is
0161       connected to. See also KMessageIO::peerPort and QSocket::peerPort.
0162     */
0163     quint16 peerPort() const;
0164 
0165     /**
0166       @return "localhost" if isConnected() is FALSE, otherwise the hostname this client is
0167       connected to. See also KMessageIO::peerName() and QSocket::peerName().
0168     */
0169     QString peerName() const;
0170 
0171     /**
0172       Sends a message to the KMessageServer. If we are not yet connected to one, nothing
0173       happens.
0174 
0175       Use this method to send a low level command to the server. It has to be in the
0176       format specified in KMessageServer.
0177 
0178       If you want to send messages to other clients, you should use sendBroadcast()
0179       and sendForward().
0180       @param msg The message to be sent to the server. Must be in the format specified in KMessageServer.
0181     */
0182     void sendServerMessage(const QByteArray &msg);
0183 
0184     /**
0185       Sends a message to all the clients connected to the server, including ourself.
0186       The message consists of an arbitrary block of data with arbitrary length.
0187 
0188       All the clients will receive an exact copy of this block of data, which will be
0189       processed in their processBroadcast() method.
0190       @param msg The message to be sent to the clients
0191     */
0192     // AB: processBroadcast doesn't exist!! is processIncomingMessage meant?
0193     void sendBroadcast(const QByteArray &msg);
0194 
0195     /**
0196       Sends a message to all the clients in a list.
0197       The message consists of an arbitrary block of data with arbitrary length.
0198 
0199       All clients will receive an exact copy of this block of data, which will be
0200       processed in their processForward() method.
0201 
0202       If the list contains client IDs that are not defined, they are ignored. If
0203       it contains an ID several times, that client will receive the message several
0204       times.
0205 
0206       To send a message to the admin of the KMessageServer, you can use 0 as clientID,
0207       instead of using the real client ID.
0208       @param msg The message to be sent to the clients
0209       @param clients A list of clients the message should be sent to
0210     */
0211     // AB: processForward doesn't exist!! is processIncomingMessage meant?
0212     void sendForward(const QByteArray &msg, const QList<quint32> &clients);
0213 
0214     /**
0215       Sends a message to a single client. This is a convenience method. It calls
0216       sendForward (const QByteArray &msg, const QValueList &ltquint32> &clients)
0217       with a list containing only one client ID.
0218 
0219       To send a message to the admin of the  KMessageServer, you can use 0 as clientID,
0220       instead of using the real client ID.
0221       @param msg The message to be sent to the client
0222       @param client The id of the client the message shall be sent to
0223     */
0224     void sendForward(const QByteArray &msg, quint32 client);
0225 
0226     /**
0227       Once this function is called no message will be received anymore.
0228       processIncomingMessage() gets delayed until unlock() is called.
0229 
0230       Note that all messages are still received, but their delivery (like
0231       broadcastReceived()) get delayed only.
0232      */
0233     void lock();
0234 
0235     /**
0236       Deliver every message that was delayed by lock() and actually deliver
0237       all messages that get received from now on.
0238      */
0239     void unlock();
0240 
0241     /**
0242       @return The number of messages that got delayed since lock() was called
0243      */
0244     unsigned int delayedMessageCount() const;
0245 
0246 Q_SIGNALS:
0247     /**
0248       This signal is emitted when the client receives a broadcast message from the
0249       KMessageServer, sent by another client. Connect to this signal to analyze the
0250       received message and do the right reaction.
0251 
0252       senderID contains the ID of the client that sent the broadcast message. You can
0253       use this e.g. to send a reply message to only that client. Or you can use it
0254       to ignore broadcast messages that were sent by yourself:
0255 
0256       \code
0257         void myObject::myBroadcastSlot (const QByteArray &msg, quint32 senderID)
0258         {
0259           if (senderID == ((KMessageClient *)sender())->id())
0260             return;
0261           ...
0262         }
0263       \endcode
0264       @param msg The message that has been sent to us
0265       @param senderID The ID of the client which sent the message
0266     */
0267     void broadcastReceived(const QByteArray &msg, quint32 senderID);
0268 
0269     /**
0270       This signal is emitted when the client receives a forward message from the
0271       KMessageServer, sent by another client. Connect to this signal to analyze the
0272       received message and do the right reaction.
0273 
0274       senderID contains the ID of the client that sent the broadcast message. You can
0275       use this e.g. to send a reply message to only that client.
0276 
0277       receivers contains the list of the clients that got the message. (If this list
0278       only contains one number, this will be your client ID, and it was exclusively
0279       sent to you.)
0280 
0281       If you don't want to distinguish between broadcast and forward messages and
0282       treat them the same, you can connect forwardReceived signal to the
0283       broadcastReceived signal. (Yes, that's possible! You can connect a Qt signal to
0284       a Qt signal, and the second one can have less parameters.)
0285 
0286       \code
0287         KMessageClient *client = new KMessageClient ();
0288         connect (client, SIGNAL (forwardReceived (const QByteArray &, quint32, const QValueList <quint32>&)),
0289                  client, SIGNAL (broadcastReceived (const QByteArray &, quint32)));
0290       \endcode
0291 
0292       Then connect the broadcast signal to your slot that analyzes the message.
0293       @param msg The message that has been sent to us
0294       @param senderID The ID of the client which sent the message
0295       @param receivers All clients which receive this message
0296     */
0297     void forwardReceived(const QByteArray &msg, quint32 senderID, const QList<quint32> &receivers);
0298 
0299     /**
0300       This signal is emitted when the connection to the KMessageServer is broken.
0301       Reasons for this can be: a network error, a server breakdown, or you were just kicked
0302       from the server.
0303 
0304       When this signal is sent, the connection is already lost and the client is unconnected.
0305       You can connect to another server by calling setServer() afterwards. But keep in mind that
0306       some important messages might have vanished.
0307     */
0308     void connectionBroken();
0309 
0310     /**
0311       This signal is emitted right before the client disconnects. It can be used
0312       to this store the id of the client which is about to be lost.
0313     */
0314     void aboutToDisconnect(quint32 id);
0315 
0316     /**
0317       This signal is emitted when this client becomes the admin client or when it loses
0318       the admin client status. Connect to this signal if you have to do any initialization
0319       or cleanup.
0320       @param isAdmin Whether we are now admin or not
0321     */
0322     void adminStatusChanged(bool isAdmin);
0323 
0324     /**
0325       This signal is emitted when another client has connected
0326       to the server. Connect to this method if that clients needs initialization.
0327       This should usually only be done in one client, e.g. the admin client.
0328       @param clientID The ID of the client that has newly connected.
0329     */
0330     void eventClientConnected(quint32 clientID);
0331 
0332     /**
0333       This signal is emitted when the server has lost the
0334       connection to one of the clients (This could be because of a bad internet connection
0335       or because the client disconnected on purpose).
0336       @param clientID The ID of the client that has disconnected
0337       @param broken true if it was disconnected because of a network error
0338     */
0339     void eventClientDisconnected(quint32 clientID, bool broken);
0340 
0341     /**
0342       This signal is emitted on every message that came from the server. You can connect to this
0343       signal to see the messages directly. They are in the format specified in KMessageServer.
0344 
0345       @param msg The message that has been sent to us
0346       @param unknown True when KMessageClient didn't recognize the message, i.e. it contained an unknown
0347       message ID. If you want to add additional message types to the client, connect to this signal,
0348       and if unknown is true, analyze the message by yourself. If you recognized the message,
0349       set unknown to false (Otherwise a debug message will be printed).
0350     */
0351     // AB: maybe add a setNoEmit() so that the other signals can be deactivated?
0352     // Could be a performance benefit (note: KMessageClient is a time critical
0353     // class!!!)
0354     void serverMessageReceived(const QByteArray &msg, bool &unknown);
0355 
0356 protected:
0357     /**
0358       This slot is called from processIncomingMessage or
0359       processFirstMessage, depending on whether the client is locked or a delayed
0360       message is still here (see lock)
0361 
0362       It processes the message and analyzes it. If it is a broadcast or a forward message from
0363       another client, it emits the signal processBroadcast or processForward accordingly.
0364 
0365       If you want to treat additional server messages, you can overwrite this method. Don't
0366       forget to call processIncomingMessage of your superclass!
0367 
0368       At the moment, the following server messages are interpreted:
0369 
0370       MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST
0371       @param msg The incoming message
0372     */
0373 
0374     virtual void processMessage(const QByteArray &msg);
0375 
0376 protected Q_SLOTS:
0377     /**
0378       This slot is called from the signal KMessageIO::received whenever a message from the
0379       KMessageServer arrives.
0380 
0381       It processes the message and analyzes it. If it is a broadcast or a forward message from
0382       another client, it emits the signal processBroadcast or processForward accordingly.
0383 
0384       If you want to treat additional server messages, you can overwrite this method. Don't
0385       forget to call processIncomingMessage() of your superclass!
0386 
0387       At the moment, the following server messages are interpreted:
0388 
0389       MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST
0390       @param msg The incoming message
0391     */
0392     virtual void processIncomingMessage(const QByteArray &msg);
0393 
0394     /**
0395       Called from unlock() (using QTimer::singleShot) until all delayed
0396       messages are delivered.
0397     */
0398     void processFirstMessage();
0399 
0400     /**
0401       This slot is called from the signal KMessageIO::connectionBroken.
0402 
0403       It deletes the internal KMessageIO object, and resets the client to default
0404       values. To connect again to another server, use setServer.
0405     */
0406     virtual void removeBrokenConnection();
0407     void removeBrokenConnection2();
0408 
0409 private:
0410     std::unique_ptr<KMessageClientPrivate> const d;
0411 };
0412 
0413 #endif