File indexing completed on 2024-06-23 04:04:20

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 __KMESSAGESERVER_H__
0009 #define __KMESSAGESERVER_H__
0010 
0011 // own
0012 #include "kdegamesprivate_export.h"
0013 // Qt
0014 #include <QObject>
0015 // Std
0016 #include <memory>
0017 
0018 class KMessageIO;
0019 class KMessageServerPrivate;
0020 
0021 /**
0022   \class KMessageServer kmessageserver.h <KGame/KMessageServer>
0023 
0024   @short A server for message sending and broadcasting, using TCP/IP connections.
0025 
0026   An object of this class listens for incoming connections via TCP/IP sockets and
0027   creates KMessageSocket objects for every established connection. It receives
0028   messages from the "clients", analyzes them and processes an appropriate
0029   reaction.
0030 
0031   You can also use other KMessageIO objects with KMessageServer, not only
0032   TCP/IP socket based ones. Use addClient to connect via an object of any
0033   KMessageIO subclass. (For clients within the same process, you can e.g. use
0034   KMessageDirect.) This object already has to be connected.
0035 
0036   The messages are always packages of an arbitrary length. The format of the messages
0037   is given below. All the data is stored and received with QDataStream, to be
0038   platform independent.
0039 
0040   Setting up a KMessageServer can be done like this:
0041 
0042   \code
0043     KMessageServer *server = new KMessageServer ();
0044     server->initNetwork (TCP/IP-Portnumber);
0045   \endcode
0046 
0047   Usually that is everything you will do. There are a lot of public methods to
0048   administrate the object (maximum number of clients, finding clients, removing
0049   clients, setting the admin client, ...), but this functionality can also
0050   be done by messages from the clients. So you can administrate the object completely
0051   on remote.
0052 
0053   If you want to extend the Server for your own needs (e.g. additional message types),
0054   you can either create a subclass and overwrite the method processOneMessage.
0055   (But don't forget to call the method of the superclass!) Or you can connect to
0056   the signal messageReceived, and analyze the messages there.
0057 
0058   Every client has a unique ID, so that messages can be sent to another dedicated
0059   client or a list of clients.
0060 
0061   One of the clients (the admin) has a special administration right. Some of the
0062   administration messages can only be used with him. The admin can give the admin
0063   status to another client. You can send a message to the admin by using clientID 0.
0064   This is always interpreted as the admin client,  of its real clientID.
0065 
0066   Here is a list of the messages the KMessageServer understands:
0067   &lt;&lt; means, the value is inserted into the QByteArray using QDataStream. The
0068   messageIDs (REQ_BROADCAST, ...) are of type quint32.
0069 
0070   - QByteArray << static_cast&lt;quint32>( REQ_BROADCAST ) << raw_data
0071 
0072     When the server receives this message, it sends the following message to
0073     ALL connected clients (a broadcast), where the raw_data is left unchanged:
0074        QByteArray << static_cast &lt;quint32>( MSG_BROADCAST ) << clientID << raw_data
0075        quint32 clientID; // the ID of the client that sent the broadcast request
0076 
0077   - QByteArray << static_cast&lt;quint32>( REQ_FORWARD ) << client_list << raw_data
0078     QValueList &lt;quint32> client_list; // list of receivers
0079 
0080     When the server receives this message, it sends the following message to
0081     the clients in client_list:
0082         QByteArray << static_cast&lt;quint32>( MSG_FORWARD ) << senderID << client_list << raw_data
0083         quint32 senderID;  // the sender of the forward request
0084         QValueList &lt;quint32> client_list; // a copy of the receiver list
0085 
0086     Note: Every client receives the message as many times as he is in the client_list.
0087     Note: Since the client_list is sent to all the clients, every client can see who else
0088           got the message. If you want to prevent this, send a single REQ_FORWARD
0089           message for every receiver.
0090 
0091   - QByteArray << static_cast&lt;quint32>( REQ_CLIENT_ID )
0092 
0093     When the server receives this message, it sends the following message to
0094     the asking client:
0095         QByteArray << static_cast&lt;quint32>( ANS_CLIENT_ID ) << clientID
0096         quint32 clientID;  // The ID of the client who asked for it
0097 
0098     Note: This answer is also automatically sent to a new connected client, so that he
0099           can store his ID. The ID of a client doesn't change during his lifetime, and is
0100           unique for this KMessageServer.
0101 
0102   - QByteArray << static_cast&lt;quint32>( REQ_ADMIN_ID )
0103 
0104     When the server receives this message, it sends the following message to
0105     the asking client:
0106         QByteArray << ANS_ADMIN_ID << adminID
0107         quint32 adminID;  // The ID of the admin
0108 
0109     Note: This answer is also automatically sent to a new connected client, so that he
0110           can see if he is the admin or not. It will also be sent to all connected clients
0111           when a new admin is set (see REQ_ADMIN_CHANGE).
0112 
0113   - QByteArray << static_cast&lt;quint32>( REQ_ADMIN_CHANGE ) << new_admin
0114     quint32 new_admin;  // the ID of the new admin, or 0 for no admin
0115 
0116     When the server receives this message, it sets the admin to the new ID. If no client
0117     with that ID exists, nothing happens. With new_admin == 0 no client is a admin.
0118     ONLY THE ADMIN ITSELF CAN USE THIS MESSAGE!
0119 
0120     Note: The server sends a ANS_ADMIN_ID message to every connected client.
0121 
0122   - QByteArray << static_cast&lt;quint32>( REQ_REMOVE_CLIENT ) << client_list
0123     QValueList &lt;quint32> client_list; // The list of clients to be removed
0124 
0125     When the server receives this message, it removes the clients with the ids stored in
0126     client_list, disconnecting the connection to them.
0127     ONLY THE ADMIN CAN USE THIS MESSAGE!
0128 
0129     Note: If one of the clients is the admin himself, he will also be deleted.
0130           Another client (if any left) will become the new admin.
0131 
0132   - QByteArray << static_cast&lt;quint32>( REQ_MAX_NUM_CLIENTS ) << maximum_clients
0133     qint32 maximum_clients; // The maximum of clients connected, or infinite if -1
0134 
0135     When the server receives this message, it limits the number of clients to the number given,
0136     or sets it unlimited for maximum_clients == -1.
0137     ONLY THE ADMIN CAN USE THIS MESSAGE!
0138 
0139     Note: If there are already more clients, they are not affected. It only prevents new Clients
0140           to be added. To assure this limit, remove clients afterwards (REQ_REMOVE_CLIENT)
0141 
0142   - QByteArray  << static_cast&lt;quint32>( REQ_CLIENT_LIST )
0143 
0144     When the server receives this message, it answers by sending a list of IDs of all the clients
0145     that are connected at the moment. So it sends the following message to the asking client:
0146         QByteArray << static_cast&lt;quint32>( ANS_CLIENT_LIST ) << clientList
0147         QValueList &lt;quint32> clientList;  // The IDs of the connected clients
0148 
0149     Note: This message is also sent to every new connected client, so that he knows the other
0150           clients.
0151 
0152   There are two more messages that are sent from the server to the every client automatically
0153   when a new client connects or a connection to a client is lost:
0154 
0155         QByteArray << static_cast&lt;quint32>( EVNT_CLIENT_CONNECTED ) << clientID;
0156         quint32 clientID;   // the ID of the new connected client
0157 
0158         QByteArray << static_cast&lt;quint32>( EVNT_CLIENT_DISCONNECTED ) << clientID;
0159         quint32 clientID;   // the ID of the client that lost the connection
0160         quint8 broken;      // 1 if the network connection was closed, 0 if it was disconnected
0161                              // on purpose
0162 
0163 
0164   @author Andreas Beckermann <b_mann@gmx.de>, Burkhard Lehner <Burkhard.Lehner@gmx.de>
0165 */
0166 class KDEGAMESPRIVATE_EXPORT KMessageServer : public QObject
0167 {
0168     Q_OBJECT
0169 
0170 public:
0171     /**
0172       MessageIDs for messages from a client to the message server.
0173     */
0174     enum {
0175         REQ_BROADCAST = 1,
0176         REQ_FORWARD,
0177         REQ_CLIENT_ID,
0178         REQ_ADMIN_ID,
0179         REQ_ADMIN_CHANGE,
0180         REQ_REMOVE_CLIENT,
0181         REQ_MAX_NUM_CLIENTS,
0182         REQ_CLIENT_LIST,
0183         REQ_MAX_REQ = 0xffff
0184     };
0185 
0186     /**
0187      * MessageIDs for messages from the message server to a client.
0188      */
0189     enum {
0190         MSG_BROADCAST = 101,
0191         MSG_FORWARD,
0192         ANS_CLIENT_ID,
0193         ANS_ADMIN_ID,
0194         ANS_CLIENT_LIST,
0195         EVNT_CLIENT_CONNECTED,
0196         EVNT_CLIENT_DISCONNECTED,
0197         EVNT_MAX_EVNT = 0xffff
0198     };
0199 
0200     /**
0201      * Create a KGameNetwork object
0202      */
0203     explicit KMessageServer(quint16 cookie = 42, QObject *parent = nullptr);
0204 
0205     ~KMessageServer() override;
0206 
0207     /**
0208      * Gives debug output of the game status
0209      */
0210     virtual void Debug();
0211 
0212     //---------------------------------- TCP/IP server stuff
0213 
0214     /**
0215      * Starts the Communication server to listen for incoming TCP/IP connections.
0216      *
0217      * @param port The port on which the service is offered, or 0 to let the
0218      * system pick a free port
0219      * @return true if it worked
0220      */
0221     bool initNetwork(quint16 port = 0);
0222 
0223     /**
0224      * Returns the TCP/IP port number we are listening to for incoming connections.
0225      * (This has to be known by other clients so that they can connect to us. It's
0226      * especially necessary if you used 0 as port number in initNetwork().
0227      * @return the port number
0228      */
0229     quint16 serverPort() const;
0230 
0231     /**
0232      * Stops listening for connections. The already running connections are
0233      * not affected.
0234      * To listen for connections again call initNetwork again.
0235      */
0236     void stopNetwork();
0237 
0238     /**
0239      * Are we still offer offering server connections?
0240      * @return true, if we are still listening to connections requests
0241      */
0242     bool isOfferingConnections() const;
0243 
0244     //---------------------------------- adding / removing clients
0245 
0246 public Q_SLOTS:
0247     /**
0248      * Adds a new @ref KMessageIO object to the communication server. This "client"
0249      * gets a unique ID.
0250      *
0251      * This slot method is automatically called for any incoming TCP/IP
0252      * connection. You can use it to add other types of connections, e.g.
0253      * local connections (KMessageDirect) to the server manually.
0254      *
0255      * NOTE: The @ref KMessageIO object gets owned by the KMessageServer,
0256      * so don't delete or manipulate it afterwards. It is automatically deleted
0257      * when the connection is broken or the communication server is deleted.
0258      * So, add a @ref KMessageIO object to just ONE KMessageServer.
0259      */
0260     void addClient(KMessageIO *);
0261 
0262     /**
0263      * Removes the KMessageIO object from the client list and deletes it.
0264      * This destroys the connection, if it already was up.
0265      * Does NOT emit connectionLost.
0266      * Sends an info message to the other clients, that contains the ID of
0267      * the removed client and the value of the parameter broken.
0268      *
0269      * @param io the object to delete and to remove from the client list
0270      * @param broken true if the client has lost connection
0271      * Mostly used internally. You will probably not need this.
0272      */
0273     void removeClient(KMessageIO *io, bool broken);
0274 
0275     /**
0276       Deletes all connections to the clients.
0277     */
0278     void deleteClients();
0279 
0280 private Q_SLOTS:
0281     /**
0282      * Removes the sender object of the signal that called this slot. It is
0283      * automatically connected to @ref KMessageIO::connectionBroken.
0284      * Emits @ref connectionLost (KMessageIO*), and deletes the @ref KMessageIO object.
0285      * Don't call it directly!
0286      */
0287     void removeBrokenClient();
0288 
0289 public:
0290     /**
0291      * sets the maximum number of clients which can connect.
0292      * If this number is reached, no more clients can be added.
0293      * Setting this number to -1 means unlimited number of clients.
0294      *
0295      * NOTE: Existing connections are not affected.
0296      * So, clientCount > maxClients is possible, if there were already
0297      * more clients than allowed before reducing this value.
0298      *
0299      * @param maxnumber the number of clients
0300      */
0301     void setMaxClients(int maxnumber);
0302 
0303     /**
0304      * returns the maximum number of clients
0305      *
0306      * @return the number of clients
0307      */
0308     int maxClients() const;
0309 
0310     /**
0311      * returns the current number of connected clients.
0312      *
0313      * @return the number of clients
0314      */
0315     int clientCount() const;
0316 
0317     /**
0318      * returns a list of the unique IDs of all clients.
0319      */
0320     QList<quint32> clientIDs() const;
0321 
0322     /**
0323      * Find the @ref KMessageIO object to the given client number.
0324      * @param no the client number to look for, or 0 to look for the admin
0325      * @return address of the client, or 0 if no client with that number exists
0326      */
0327     KMessageIO *findClient(quint32 no) const;
0328 
0329     /**
0330      * Returns the clientID of the admin, if there is a admin, 0 otherwise.
0331      *
0332      * NOTE: Most often you don't need to know that id, since you can
0333      * use clientID 0 to specify the admin.
0334      */
0335     quint32 adminID() const;
0336 
0337     /**
0338      * Sets the admin to a new client with the given ID.
0339      * The old admin (if existed) and the new admin will get the ANS_ADMIN message.
0340      * If you use 0 as new adminID, no client will be admin.
0341      */
0342     void setAdmin(quint32 adminID);
0343 
0344     //------------------------------ ID stuff
0345 
0346     /*
0347      * The unique ID of this game
0348      *
0349      * @return int id
0350      */
0351     //    int gameId() const;
0352 
0353     /*
0354      * Application cookie. this identifies the game application. It
0355      * help to distinguish between e.g. KPoker and KWin4
0356      *
0357      * @return the application cookie
0358      */
0359     //    int cookie() const;
0360 
0361     //------------------------------ Message stuff
0362 
0363 public:
0364     /**
0365      * Sends a message to all connected clients.
0366      * The message is NOT translated in any way. This method calls
0367      * @ref KMessageIO::send for every client added.
0368      */
0369     virtual void broadcastMessage(const QByteArray &msg);
0370 
0371     /**
0372      * Sends a message to a single client with the given ID.
0373      * The message is NOT translated in any way.
0374      * If no client with the given id exists, nothing is done.
0375      * This is just a convenience method. You could also call
0376      * @ref findClient (id)->send(msg) manually, but this method checks for
0377      * errors.
0378      */
0379     virtual void sendMessage(quint32 id, const QByteArray &msg);
0380 
0381     /**
0382      * Sends a message to a list of clients. Their ID is given in ids. If
0383      * a client id is given more than once in the list, the message is also
0384      * sent several times to that client.
0385      * This is just a convenience method. You could also iterate over the
0386      * list of IDs.
0387      */
0388     virtual void sendMessage(const QList<quint32> &ids, const QByteArray &msg);
0389 
0390 protected Q_SLOTS:
0391     /**
0392      * This slot receives all the messages from the @ref KMessageIO::received signals.
0393      * It stores the messages in a queue. The messages are later taken out of the queue
0394      * by @ref getReceivedMessage.
0395      *
0396      * NOTE: It is important that this slot may only be called from the signal
0397      * @ref KMessageIO::received, since the sender() object is used to find out
0398      * the client that sent the message!
0399      */
0400     virtual void getReceivedMessage(const QByteArray &msg);
0401 
0402     /**
0403      * This slot is called whenever there are elements in the message queue. This queue
0404      * is filled by @ref getReceivedMessage.
0405      * This slot takes one message out of the queue and analyzes processes it,
0406      * if it recognizes it. (See message types in the description of the class.)
0407      * After that, the signal @ref messageReceived is emitted. Connect to that signal if
0408      * you want to process other types of messages.
0409      */
0410     virtual void processOneMessage();
0411 
0412     //---------------------------- Signals
0413 
0414 Q_SIGNALS:
0415     /**
0416      * A new client connected to the game
0417      * @param client the client object that connected
0418      */
0419     void clientConnected(KMessageIO *client);
0420 
0421     /**
0422      * A network connection got broken. Note that the client will automatically get deleted
0423      * after this signal is emitted. The signal is not emitted when the client was removed
0424      * regularly.
0425      *
0426      * @param client the client which left the game
0427      */
0428     void connectionLost(KMessageIO *client);
0429 
0430     /**
0431      * This signal is always emitted when a message from a client is received.
0432      *
0433      * You can use this signal to extend the communication server without subclassing.
0434      * Just connect to this signal and analyze the message, if unknown is true.
0435      * If you recognize a message and process it, set unknown to false, otherwise
0436      * a warning message is printed.
0437      *
0438      * @param data the message data
0439      * @param clientID the ID of the KMessageIO object that received the message
0440      * @param unknown true, if the message type is not known by the KMessageServer
0441      */
0442     void messageReceived(const QByteArray &data, quint32 clientID, bool &unknown);
0443 
0444 protected:
0445     /**
0446      * @return A unique number which can be used as the id of a @ref KMessageIO. It is
0447      * incremented after every call so if you need the id twice you have to save
0448      * it anywhere. It's currently used to initialize newly connected clients only.
0449      */
0450     quint32 uniqueClientNumber() const;
0451 
0452 private:
0453     std::unique_ptr<KMessageServerPrivate> const d;
0454 };
0455 
0456 #endif