diff options
Diffstat (limited to 'libtdegames/kgame/kmessageserver.h')
-rw-r--r-- | libtdegames/kgame/kmessageserver.h | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/libtdegames/kgame/kmessageserver.h b/libtdegames/kgame/kmessageserver.h new file mode 100644 index 00000000..9042fca2 --- /dev/null +++ b/libtdegames/kgame/kmessageserver.h @@ -0,0 +1,494 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KMESSAGESERVER_H__ +#define __KMESSAGESERVER_H__ + +#include <tqobject.h> +#include <tqserversocket.h> +#include <tqstring.h> +#include <tqvaluelist.h> + +class KMessageIO; +class KMessageServerPrivate; + +/** + @short A server for message sending and broadcasting, using TCP/IP connections. + + An object of this class listens for incoming connections via TCP/IP sockets and + creates KMessageSocket objects for every established connection. It receives + messages from the "clients", analyses them and processes an appropriate + reaction. + + You can also use other KMessageIO objects with KMessageServer, not only + TCP/IP socket based ones. Use addClient to connect via an object of any + KMessageIO subclass. (For clients within the same process, you can e.g. use + KMessageDirect.) This object already has to be connected. + + The messages are always packages of an arbitrary length. The format of the messages + is given below. All the data is stored and received with TQDataStream, to be + platform independant. + + Setting up a KMessageServer can be done like this: + + \code + KMessageServer *server = new KMessageServer (); + server->initNetwork (TCP/IP-Portnumber); + \endcode + + Usually that is everything you will do. There are a lot of public methods to + administrate the object (maximum number of clients, finding clients, removing + clients, setting the admin client, ...), but this functionality can also + be done by messages from the clients. So you can administrate the object completely + on remote. + + If you want to extend the Server for your own needs (e.g. additional message types), + you can either create a subclass and overwrite the method processOneMessage. + (But don't forget to call the method of the superclass!) Or you can connect to + the signal messageReceived, and analyse the messages there. + + Every client has a unique ID, so that messages can be sent to another dedicated + client or a list of clients. + + One of the clients (the admin) has a special administration right. Some of the + administration messages can only be used with him. The admin can give the admin + status to another client. You can send a message to the admin by using clientID 0. + This is always interpreted as the admin client, independant of its real clientID. + + Here is a list of the messages the KMessageServer understands: + << means, the value is inserted into the TQByteArray using TQDataStream. The + messageIDs (RETQ_BROADCAST, ...) are of type TQ_UINT32. + + - TQByteArray << static_cast<TQ_UINT32>( RETQ_BROADCAST ) << raw_data + + When the server receives this message, it sends the following message to + ALL connected clients (a broadcast), where the raw_data is left unchanged: + TQByteArray << static_cast <TQ_UINT32>( MSG_BROADCAST ) << clientID << raw_data + TQ_UINT32 clientID; // the ID of the client that sent the broadcast request + + - TQByteArray << static_cast<TQ_UINT32>( RETQ_FORWARD ) << client_list << raw_data + TQValueList <TQ_UINT32> client_list; // list of tqreceivers + + When the server receives this message, it sends the following message to + the clients in client_list: + TQByteArray << static_cast<TQ_UINT32>( MSG_FORWARD ) << senderID << client_list << raw_data + TQ_UINT32 senderID; // the sender of the forward request + TQValueList <TQ_UINT32> client_list; // a copy of the receiver list + + Note: Every client receives the message as many times as he is in the client_list. + Note: Since the client_list is sent to all the clients, every client can see who else + got the message. If you want to prevent this, send a single RETQ_FORWARD + message for every receiver. + + - TQByteArray << static_cast<TQ_UINT32>( RETQ_CLIENT_ID ) + + When the server receives this message, it sends the following message to + the asking client: + TQByteArray << static_cast<TQ_UINT32>( ANS_CLIENT_ID ) << clientID + TQ_UINT32 clientID; // The ID of the client who asked for it + + Note: This answer is also automatically sent to a new connected client, so that he + can store his ID. The ID of a client doesn't change during his lifetime, and is + unique for this KMessageServer. + + - TQByteArray << static_cast<TQ_UINT32>( RETQ_ADMIN_ID ) + + When the server receives this message, it sends the following message to + the asking client: + TQByteArray << ANS_ADMIN_ID << adminID + TQ_UINT32 adminID; // The ID of the admin + + Note: This answer is also automatically sent to a new connected client, so that he + can see if he is the admin or not. It will also be sent to all connected clients + when a new admin is set (see RETQ_ADMIN_CHANGE). + + - TQByteArray << static_cast<TQ_UINT32>( RETQ_ADMIN_CHANGE ) << new_admin + TQ_UINT32 new_admin; // the ID of the new admin, or 0 for no admin + + When the server receives this message, it sets the admin to the new ID. If no client + with that ID exists, nothing happens. With new_admin == 0 no client is a admin. + ONLY THE ADMIN ITSELF CAN USE THIS MESSAGE! + + Note: The server sends a ANS_ADMIN_ID message to every connected client. + + - TQByteArray << static_cast<TQ_UINT32>( RETQ_REMOVE_CLIENT ) << client_list + TQValueList <TQ_UINT32> client_list; // The list of clients to be removed + + When the server receives this message, it removes the clients with the ids stored in + client_list, disconnecting the connection to them. + ONLY THE ADMIN CAN USE THIS MESSAGE! + + Note: If one of the clients is the admin himself, he will also be deleted. + Another client (if any left) will become the new admin. + + - TQByteArray << static_cast<TQ_UINT32>( RETQ_MAX_NUM_CLIENTS ) << maximum_clients + TQ_INT32 maximum_clients; // The maximum of clients connected, or infinite if -1 + + When the server receives this message, it limits the number of clients to the number given, + or sets it unlimited for maximum_clients == -1. + ONLY THE ADMIN CAN USE THIS MESSAGE! + + Note: If there are already more clients, they are not affected. It only prevents new Clients + to be added. To assure this limit, remove clients afterwards (RETQ_REMOVE_CLIENT) + + - TQByteArray << static_cast<TQ_UINT32>( RETQ_CLIENT_LIST ) + + When the server receives this message, it answers by sending a list of IDs of all the clients + that are connected at the moment. So it sends the following message to the asking client: + TQByteArray << static_cast<TQ_UINT32>( ANS_CLIENT_LIST ) << clientList + TQValueList <TQ_UINT32> clientList; // The IDs of the connected clients + + Note: This message is also sent to every new connected client, so that he knows the other + clients. + + There are two more messages that are sent from the server to the every client automatically + when a new client connects or a connection to a client is lost: + + TQByteArray << static_cast<TQ_UINT32>( EVNT_CLIENT_CONNECTED ) << clientID; + TQ_UINT32 clientID; // the ID of the new connected client + + TQByteArray << static_cast<TQ_UINT32>( EVNT_CLIENT_DISCONNECTED ) << clientID; + TQ_UINT32 clientID; // the ID of the client that lost the connection + TQ_UINT8 broken; // 1 if the network connection was closed, 0 if it was disconnected + // on purpose + + + @author Andreas Beckermann <b_mann@gmx.de>, Burkhard Lehner <Burkhard.Lehner@gmx.de> + @version $Id$ +*/ +class KMessageServer : public TQObject +{ + Q_OBJECT + TQ_OBJECT + +public: + /** + MessageIDs for messages from a client to the message server. + */ + enum { + RETQ_BROADCAST = 1, + RETQ_FORWARD, + RETQ_CLIENT_ID, + RETQ_ADMIN_ID, + RETQ_ADMIN_CHANGE, + RETQ_REMOVE_CLIENT, + RETQ_MAX_NUM_CLIENTS, + RETQ_CLIENT_LIST, + RETQ_MAX_REQ = 0xffff }; + + /** + * MessageIDs for messages from the message server to a client. + **/ + enum { + MSG_BROADCAST = 101, + MSG_FORWARD, + ANS_CLIENT_ID, + ANS_ADMIN_ID, + ANS_CLIENT_LIST, + EVNT_CLIENT_CONNECTED, + EVNT_CLIENT_DISCONNECTED, + EVNT_MAX_EVNT = 0xffff + }; + + /** + * Create a KGameNetwork object + **/ + KMessageServer(TQ_UINT16 cookie = 42, TQObject* parent = 0); + + ~KMessageServer(); + + /** + * Gives debug output of the game status + **/ + virtual void Debug(); + +//---------------------------------- TCP/IP server stuff + + /** + * Starts the Communication server to listen for incoming TCP/IP connections. + * + * @param port The port on which the service is offered, or 0 to let the + * system pick a free port + * @return true if it worked + */ + bool initNetwork (TQ_UINT16 port = 0); + + /** + * Returns the TCP/IP port number we are listening to for incoming connections. + * (This has to be known by other clients so that they can connect to us. It's + * especially necessary if you used 0 as port number in initNetwork(). + * @return the port number + **/ + TQ_UINT16 serverPort () const; + + /** + * Stops listening for connections. The already running connections are + * not affected. + * To listen for connections again call initNetwork again. + **/ + void stopNetwork(); + + /** + * Are we still offer offering server connections? + * @return true, if we are still listening to connections requests + **/ + bool isOfferingConnections() const; + +//---------------------------------- adding / removing clients + +public slots: + /** + * Adds a new @ref KMessageIO object to the communication server. This "client" + * gets a unique ID. + * + * This slot method is automatically called for any incoming TCP/IP + * connection. You can use it to add other types of connections, e.g. + * local connections (KMessageDirect) to the server manually. + * + * NOTE: The @ref KMessageIO object gets owned by the KMessageServer, + * so don't delete or manipulate it afterwards. It is automatically deleted + * when the connection is broken or the communication server is deleted. + * So, add a @ref KMessageIO object to just ONE KMessageServer. + **/ + void addClient (KMessageIO *); + + /** + * Removes the KMessageIO object from the client list and deletes it. + * This destroys the connection, if it already was up. + * Does NOT emit connectionLost. + * Sends an info message to the other clients, that contains the ID of + * the removed client and the value of the parameter broken. + * + * @param io the object to delete and to remove from the client list + * @param broken true if the client has lost connection + * Mostly used internally. You will probably not need this. + **/ + void removeClient (KMessageIO *io, bool broken); + + /** + Deletes all connections to the clients. + */ + void deleteClients(); + +private slots: + /** + * Removes the sender object of the signal that called this slot. It is + * automatically connected to @ref KMessageIO::connectionBroken. + * Emits @ref connectionLost (KMessageIO*), and deletes the @ref KMessageIO object. + * Don't call it directly! + **/ + void removeBrokenClient (); + +public: + /** + * sets the maximum number of clients which can connect. + * If this number is reached, no more clients can be added. + * Setting this number to -1 means unlimited number of clients. + * + * NOTE: Existing connections are not affected. + * So, clientCount > maxClients is possible, if there were already + * more clients than allowed before reducing this value. + * + * @param maxnumber the number of clients + **/ + void setMaxClients(int maxnumber); + + /** + * returns the maximum number of clients + * + * @return the number of clients + **/ + int maxClients() const; + + /** + * returns the current number of connected clients. + * + * @return the number of clients + **/ + int clientCount() const; + + /** + * returns a list of the unique IDs of all clients. + **/ + TQValueList <TQ_UINT32> clientIDs() const; + + /** + * Find the @ref KMessageIO object to the given client number. + * @param no the client number to look for, or 0 to look for the admin + * @return address of the client, or 0 if no client with that number exists + **/ + KMessageIO *findClient (TQ_UINT32 no) const; + + /** + * Returns the clientID of the admin, if there is a admin, 0 otherwise. + * + * NOTE: Most often you don't need to know that id, since you can + * use clientID 0 to specify the admin. + **/ + TQ_UINT32 adminID() const; + + /** + * Sets the admin to a new client with the given ID. + * The old admin (if existed) and the new admin will get the ANS_ADMIN message. + * If you use 0 as new adminID, no client will be admin. + **/ + void setAdmin (TQ_UINT32 adminID); + + +//------------------------------ ID stuff + + /* + * The unique ID of this game + * + * @return int id + **/ +// int gameId() const; + + /* + * Application cookie. this idendifies the game application. It + * help to distinguish between e.g. KPoker and KWin4 + * + * @return the application cookie + **/ +// int cookie() const; + +//------------------------------ Message stuff + +public: + /** + * Sends a message to all connected clients. + * The message is NOT translated in any way. This method calls + * @ref KMessageIO::send for every client added. + **/ + virtual void broadcastMessage (const TQByteArray &msg); + + /** + * Sends a message to a single client with the given ID. + * The message is NOT translated in any way. + * If no client with the given id exists, nothing is done. + * This is just a convenience method. You could also call + * @ref findClient (id)->send(msg) manually, but this method checks for + * errors. + **/ + virtual void sendMessage (TQ_UINT32 id, const TQByteArray &msg); + + /** + * Sends a message to a list of clients. Their ID is given in ids. If + * a client id is given more than once in the list, the message is also + * sent several times to that client. + * This is just a convenience method. You could also iterate over the + * list of IDs. + **/ + virtual void sendMessage (const TQValueList <TQ_UINT32> &ids, const TQByteArray &msg); + +protected slots: + /** + * This slot receives all the messages from the @ref KMessageIO::received signals. + * It stores the messages in a queue. The messages are later taken out of the queue + * by @ref getReceivedMessage. + * + * NOTE: It is important that this slot may only be called from the signal + * @ref KMessageIO::received, since the sender() object is used to find out + * the client that sent the message! + **/ + virtual void getReceivedMessage (const TQByteArray &msg); + + /** + * This slot is called whenever there are elements in the message queue. This queue + * is filled by @ref getReceivedMessage. + * This slot takes one message out of the queue and analyses processes it, + * if it recognizes it. (See message types in the description of the class.) + * After that, the signal @ref messageReceived is emitted. Connect to that signal if + * you want to process other types of messages. + **/ + virtual void processOneMessage (); + +//---------------------------- Signals + +signals: + /** + * A new client connected to the game + * @param client the client object that connected + **/ + void clientConnected (KMessageIO *client); + + /** + * A network connection got broken. Note that the client will automatically get deleted + * after this signal is emitted. The signal is not emitted when the client was removed + * regularly. + * + * @param client the client which left the game + **/ + void connectionLost (KMessageIO *client); + + /** + * This signal is always emitted when a message from a client is received. + * + * You can use this signal to extend the communication server without subclassing. + * Just connect to this signal and analyse the message, if unknown is true. + * If you recognize a message and process it, set unknown to false, otherwise + * a warning message is printed. + * + * @param data the message data + * @param clientID the ID of the KMessageIO object that received the message + * @param unknown true, if the message type is not known by the KMessageServer + **/ + void messageReceived (const TQByteArray &data, TQ_UINT32 clientID, bool &unknown); + +protected: + /** + * @return A unique number which can be used as the id of a @ref KMessageIO. It is + * incremented after every call so if you need the id twice you have to save + * it anywhere. It's currently used to initialize newly connected clints only. + **/ + TQ_UINT32 uniqueClientNumber() const; + +private: + KMessageServerPrivate* d; +}; + + +/** + Internal class of KMessageServer. Creates a server socket and waits for + connections. + + NOTE: This has to be here in the header file, because it is a subclass from + TQObject and has to go through the tqmoc. + + @short An internal class for KServerSocket + @author Burkhard Lehner <Burkhard.Lehner@gmx.de> +*/ +class KMessageServerSocket : public TQServerSocket +{ + Q_OBJECT + TQ_OBJECT + +public: + KMessageServerSocket (TQ_UINT16 port, TQObject *parent = 0); + ~KMessageServerSocket (); + + void newConnection (int socket); + +signals: + void newClientConnected (KMessageIO *client); +}; + + + +#endif |