src/Network/Object.hh
author Tero Marttila <terom@fixme.fi>
Fri, 16 Jan 2009 22:03:49 +0200
changeset 400 d64bf28c4340
parent 337 ecde18d07879
child 431 c6d7272a164b
permissions -rw-r--r--
more documentation tweaking, all Network/ files now have a @file comment. Fix Platform.h -> Platform.hh, and Buffer.hh + Packet.cc
#ifndef NETWORK_OBJECT_HH
#define NETWORK_OBJECT_HH

/**
 * @file
 *
 * Object-based network protocol for use with NetworkSession
 */

#include "Session.hh"
#include "Node.hh"
#include "../Logger.hh"

#include <map>

/**
 * A NetworkObject's ID is a 32-bit integer
 */
typedef uint32_t NetworkObjectID;

/**
 * A NetworkObject's message type is a 16-bit integer
 */
typedef uint16_t NetworkMessageID;

// forward-declare
class NetworkObject;
class NetworkObject_Client;
class NetworkObject_Server;

/**
 * A NetworkObjectController contains a mapping of NetworkObjectID -> NetworkObject, and handles the
 * messages received on our channel from the NetworkSession.
 *
 * Additionally, this provides methods to read/write NetworkObject references from/to a NetworkPacket
 */
class NetworkObjectController {
    friend class NetworkObject;
    friend class NetworkObject_Server;
    friend class NetworkObject_Client;

    private:
        /**
         * The NetworkSession that we use
         */
        NetworkSession &session;

        /**
         * The NetworkChannelID that we use to communicate
         */
        NetworkChannelID channel_id;
        
        /**
         * Our map of NetworkObjectID -> NetworkObject
         */
        std::map<NetworkObjectID, NetworkObject*> objects;
        
        CL_Slot slot_message;
    
    protected:
        /**
         * Construct a NetworkObjectController to use the given NetworkSession and NetworkChannelID
         */
        NetworkObjectController (NetworkSession &session, NetworkChannelID channel_id);
    
    private:
        /**
         * Our NetworkSession::sig_chan_message handler
         *
         * Reads the NetworkObjectID and NetworkMessageID from the packet, and then either calls handle_create or
         * NetworkObject::handle_packet.
         */
        void on_message (NetworkPacketInput &pkt, NetworkNode *node);

    protected:
        /**
         * Abstract method called by on_message if we recieve a message for an unknown NetworkObjectID
         *
         * @param obj_id the unknown NetworkObjectID
         * @param msg_id the packet's NetworkMessageID
         * @param pkt the packet itself, with the header read
         * @param node the node that we got this packet from
         */
        virtual void handle_create (NetworkObjectID obj_id, NetworkMessageID msg_id, NetworkPacketInput &pkt, NetworkNode *node) = 0; 

    public:
        /**
         * Read an NetworkObjectID from the given packet, and return the corresponding NetworkObject, or NULL if:
         *  * it was zero
         *  * we don't know the object (should this thrown an exception instead?)
         *
         * @return the NetworkObject* corresponding to the NetworkObjectID in the packet, or NULL if zero/not found
         */
        NetworkObject* read_object (NetworkPacketInput &pkt);

        /**
         * Write the given Object's NetworkObjectID (or 0, if the obj is NULL) to the given packet
         *
         * @param the NetworkObject* whose NetworkObjectID to write to the packet, or NULL to write zero
         */
        void write_object (NetworkPacketOutput &pkt, NetworkObject *obj);
};

/**
 * A NetworkObjectController intended for use with a server.
 *
 * This has an id_pool that new NetworkObjectIDs can be generated from, and this doesn't accept unknown
 * NetworkObjectIDs from clients.
 */
class NetworkObject_ServerController : public NetworkObjectController {
    friend class NetworkObject_Server;

    private:
        /**
         * The id_pool that we use to generate NetworkObjectIDs for new NetworkObject's
         */
        NetworkObjectID id_pool;
    
    public:
        /**
         * @see NetworkObjectController
         */
        NetworkObject_ServerController (NetworkSession &session, NetworkChannelID channel_id);

    protected:
        /**
         * Get a new NetworkObjectID
         */
        NetworkObjectID getObjectID (void);
        
        /**
         * Throw an error, as we don't accept unknown NetworkObjects from clients
         */
        virtual void handle_create (NetworkObjectID obj_id, NetworkMessageID msg_id, NetworkPacketInput &pkt, NetworkNode *node);
};

/**
 * A NetworkObjectController intended for use with a client.
 *
 * This provides two ways to handle unknown objects: override handle_create, or use sig_create
 */
class NetworkObject_ClientController : public NetworkObjectController {
    friend class NetworkObject_Client;

    protected:
        /**
         * The server node, as returned by NetworkSession::connect
         */
        NetworkNode *server;
        
        /**
         * A mapping of NetworkMessageID -> sig_create, used by the default handle_create
         */
        std::map<NetworkMessageID, CL_Signal_v2<NetworkObject_Client*, NetworkPacketInput&> > _map_sig_create;
    
    public:
        /**
         * Construct a NetworkObjectController using the given NetworkNode (as returned by NetworkSession::connect) as
         * a server.
         *
         * @see NetworkObjectController
         */
        NetworkObject_ClientController (NetworkSession &session, NetworkChannelID channel_id, NetworkNode *server);

    protected:
        /**
         * Handle unknown NetworkObjectIDs by constructing a new NetworkObject_Client and calling sig_create.
         * 
         * This can be overriden to handle this differently, e.g. to create an instance of a NetworkObject_Client
         * subclass instead.
         */
        virtual void handle_create (NetworkObjectID obj_id, NetworkMessageID msg_id, NetworkPacketInput &pkt, NetworkNode *node);
    
    public:  
        /**
         * Signal triggered by default handle_create when we get a message for an unknown NetworkObject.
         * The signal gets the new NetworkObject_Client, and the packet itself.
         *
         * @param msg_id the NetworkMessageID to handle creates for
         */
        CL_Signal_v2<NetworkObject_Client*, NetworkPacketInput&>& sig_create (NetworkMessageID msg_id) { return _map_sig_create[msg_id]; }
};

/**
 * Base class of NetworkObject_(Client/Server)
 *
 * A NetworkObject has an NetworkObjectID, and can handle packets to/from this object
 */
class NetworkObject {
    friend class NetworkObjectController;
    friend std::ostream& operator<< (std::ostream &s, const NetworkObject &obj);

    protected:
        /**
         * Generic controller
         */
        NetworkObjectController &controller;

        /**
         * This object's object id
         */
        NetworkObjectID obj_id;

    protected:
        /**
         * Construct a NetworkObject using the given controller and object id
         *
         * @param controller the NetworkObjectController (subclass)
         * @param obj_id our NetworkObjectID
         */
        NetworkObject (NetworkObjectController &controller, NetworkObjectID obj_id);
        
        /**
         * Removes this object from the controller's objects list
         */
        ~NetworkObject (void);
        
        /**
         * Abstract method to handle packets sent to this object
         *
         * @param node the NetworkNode that sent this packet to us
         * @param msg_id the packet's NetworkMessageID
         * @param pkt the packet itself, with the headers read
         */
        virtual void handle_packet (NetworkNode *node, NetworkMessageID msg_id, NetworkPacketInput &pkt) = 0;
        
        /**
         * Prepare an outgoing packet, writing the header and the payload
         *
         * @param pkt the packet to prepare
         * @param msg_id the packet's message id
         * @param payload the packet's payload
         */
        void buildPacket (NetworkPacketOutput &pkt, NetworkMessageID msg_id, const NetworkPacketBuffer &payload);
};

/**
 * Formats the object as "<NetworkObject #" <obj_id> ">"
 */
std::ostream& operator<< (std::ostream &s, const NetworkObject &obj);

/**
 * A server-side NetworkObject
 */
class NetworkObject_Server : public NetworkObject {
    friend class NetworkObject_ServerController;

    protected:
        /**
         * The NetworkObject_ServerController
         */
        NetworkObject_ServerController &controller;
        
        /**
         * Mapping of NetworkMessageID -> sig_message used for received messages
         */
        std::map<NetworkMessageID, CL_Signal_v2<NetworkNode*, NetworkPacketInput&> > _map_sig_message;

    public:
        /**
         * @see NetworkObject
         */
        NetworkObject_Server (NetworkObject_ServerController &controller);
    
    protected:
        /**
         * Handle an incoming packet by invoking the appopriate sig_message
         *
         * @see NetworkObject::handlePacket
         */
        virtual void handle_packet (NetworkNode *node, NetworkMessageID msg_id, NetworkPacketInput &pkt);

    public:
        /**
         * Send a message on this object to the given node
         *
         * @param dst the NetworkNode to send the message to
         * @param msg_id the type of message to send
         * @param pkt the packet payload
         * @param reliable Whether to use TCP or UDP
         */
        void send_to (NetworkNode *dst, NetworkMessageID msg_id, const NetworkPacketBuffer &pkt, bool reliable = true);

        /**
         * Send a message on this object to all nodes on our controller's NetworkSession
         *
         * @param msg_id the type of message to send
         * @param pkt the packet payload
         * @param reliable Whether to use TCP or UDP
         */
        void send_all (NetworkMessageID msg_id, const NetworkPacketBuffer &pkt, bool reliable = true);

        /**
         * Send a message on this object to all nodes on our controller's NetworkSession, except the given node
         *
         * @param msg_id the type of message to send
         * @param pkt the packet payload
         * @param black_sheep the node to *NOT* send the message to
         * @param reliable Whether to use TCP or UDP
         */
        void send_all_except (NetworkMessageID msg_id, const NetworkPacketBuffer &pkt, NetworkNode *black_sheep, bool reliable = true);
        
        /**
         * Triggered whenever we receive a message of the given type on this object, giving the node that sent it and the
         * packet itself
         *
         * @param msg_id the NetworkMessageID to handle
         */
        CL_Signal_v2<NetworkNode*, NetworkPacketInput&>& sig_message (NetworkMessageID msg_id) { return _map_sig_message[msg_id]; }
};

/**
 * A client-side NetworkObject
 */
class NetworkObject_Client : public NetworkObject {
    friend class NetworkObject_ClientController;

    protected:
        /**
         * Our NetworkObject_ClientController
         */
        NetworkObject_ClientController &controller;
        
        /**
         * Our mapping of NetworkMessageID -> sig_message, used to handle received packets
         */
        std::map<NetworkMessageID, CL_Signal_v1<NetworkPacketInput&> > _map_sig_message;

    protected:
        /**
         * @see NetworkObject
         */
        NetworkObject_Client (NetworkObject_ClientController &controller, NetworkObjectID id);
        
        /**
         * Handle an incoming packet by invoking the appropriate sig_message.
         *
         * This assumes that packets only come from the server
         *
         * @see NetworkObject::handlePacket
         */
        virtual void handle_packet (NetworkNode *node, NetworkMessageID msg_id, NetworkPacketInput &pkt);

    public:
        /**
         * Send a message on this object to the server
         *
         * @param msg_id the type of message to send
         * @param pkt the packet payload
         * @param reliable Whether to use TCP or UDP
         */
        void send (NetworkMessageID msg_id, const NetworkPacketBuffer &pkt, bool reliable = true);
        
        /**
         * Triggered whenever we receive a message of the given type on this object from the server.
         *
         * @param msg_id the NetworkMessageID to handle
         */
        CL_Signal_v1<NetworkPacketInput&>& sig_message (NetworkMessageID msg_id) { return _map_sig_message[msg_id]; }
};

#endif /* NETWORK_OBJECT_HH */