src/Network/Client.hh
author Tero Marttila <terom@fixme.fi>
Tue, 20 Jan 2009 23:30:18 +0200
changeset 408 e6cfc44266af
parent 400 d64bf28c4340
child 431 c6d7272a164b
permissions -rw-r--r--
reorganize Terrain/PhysicsWorld/GameState/Engine to use NetworkClientConnect, and hence handle the connection process asynchronously, and finally properly implement receiving the terrain data from the server
#ifndef NETWORKCLIENT_HH
#define NETWORKCLIENT_HH

/**
 * @file
 *
 * Game client implementation
 */

// forward-declare
class NetworkClientConnect;
class NetworkClient;
class NetworkClientLocalPlayer;
class NetworkClientRemotePlayer;

#include "../GameState.hh"
#include "../Engine.hh"
#include "Session.hh"
#include "Object.hh"

/**
 * Our specialized NetworkObject_ClientController that overrides handle_create to create the right kind of
 * object (a subclass of NetowrkClientObject).
 */
class NetworkClientController : public NetworkObject_ClientController {
    protected:
        /**
         * The NetworkClient
         */
        NetworkClient &client;
        
    public:
        /**
         * Control objects on the given client using the client.netsession's NETCHAN_CORE channel
         */
        NetworkClientController (NetworkClient &client);

    protected:
        /**
         * We override handle_create from NetworkObject_ClientController to call one of the on_* methods, which creates
         * a NetworkClientObject subclass
         *
         * @see NetworkObject_ClientController::handle_create
         */
        virtual void handle_create (NetworkObjectID obj_id, NetworkMessageID msg_id, NetworkPacketInput &pkt, NetworkNode *node);
        
        /**
         * Handle NETMSG_SERVER_HELLO -> NetworkClientLocalPlayer
         */
        void on_server_hello (NetworkObjectID obj_id, NetworkPacketInput &pkt);

        /**
         * Handle NETMSG_PLAYER_INFO -> NetworkClientRemotePlayer
         */
        void on_player_info (NetworkObjectID obj_id, NetworkPacketInput &pkt);

        /**
         * Handle NETMSG_PLAYER_JOIN -> NetworkClientRemotePlayer
         */
        void on_player_join (NetworkObjectID obj_id, NetworkPacketInput &pkt);

        /**
         * Handle NETMSG_PROJECTILE_PLAYER_FIRED -> NetworkClientProjectile
         */
        void on_projectile_player_fired (NetworkObjectID obj_id, NetworkPacketInput &pkt);
};

/**
 * Our NetworkClient, that connects to a NetworkServer. This has the GameState, NetworkSession, NetworkClientController, etc.
 */
class NetworkClient {
    friend class NetworkClientController;
    friend class NetworkClientObject;
    friend class NetworkClientPlayerBase;
    friend class NetworkClientLocalPlayer;
    friend class NetworkClientRemotePlayer;
    friend class NetworkClientProjectile;

    protected:
        /**
         * The Engine
         */
        Engine &engine;

        /**
         * The GameState
         */
        GameState &state;

        /**
         * The connect()-mode NetworkSession
         */
        NetworkSession &netsession;

        /**
         * The server NetworkNode from Netsession::connect
         */
        NetworkNode *server;

        /**
         * Our specialized NetworkObject_ClientController
         */
        NetworkClientController controller;

        CL_SlotContainer slots;

    public:
        /**
         * Create a NetworkClient with the given GameState, connecting a server on the given NetworkEndpoint
         *
         * @param engine the Engine we're running as
         * @param state the GameState to use
         * @param connect_to the address to connect to
         */
        NetworkClient (Engine &engine, GameState &state, NetworkSession &netsession, NetworkNode *server);
    
    public:
        /**
         * Called by NetworkClientRemotePlayer when they get disconnected. Doesn't do anything currently
         */
        void player_quit (NetworkClientRemotePlayer *player);
};

/**
 * This handles the actual connection process to the server, and handles the initial data from the server. Once the
 * Engine has a game running, we can create the actual NetworkClient object.
 */
class NetworkClientConnect {
    protected:
        /**
         * The Engine we are running under
         */
        Engine &engine;

        /**
         * The NetworkSession we are using
         */
        NetworkSession netsession;

        /**
         * The server we've connected to
         */
        NetworkNode *server;

        /**
         * The NetworkClient that we eventually create
         */
        NetworkClient *client;
        
        CL_SlotContainer slots;

    public:
        /**
         * Begin the connection process. Once it is complete, we will callback to Engine::networkClientConnected
         */
        NetworkClientConnect (Engine &engine, const NetworkEndpoint &connect_to);
    
    protected:
        /**
         * We have disconnected from the server
         */
        void on_disconnected (void);

        /**
         * Receive the NETCHAN_TERRAIN_ARRAY message from the server and apply it to our GameState::world terrain
         */
        void on_terrain_array (NetworkPacketInput &pkt, NetworkNode *node);

        /**
         * Finished connecting 
         */
        void connectDone (Terrain *terrain);
};

/**
 * Our base NetworkObject_Client object, containing the NetworkClient and a CL_SlotContainer for conveniance
 */
class NetworkClientObject : public NetworkObject_Client {
    protected:
        /**
         * The NetworkClient
         */
        NetworkClient &client;

        CL_SlotContainer slots;
        
        /**
         * Construct this using the given client and obj_id, passing the client.controller and obj_id to the
         * NetworkObject_Client's constructor
         */
        NetworkClientObject (NetworkClient &client, NetworkObjectID obj_id);
};

/**
 * Our base class for NetworkClient Players, this implements most of the server -> client messages
 *
 * This inherits from NetworkClientObject and virtually from Player, as classes should inherit from both this and
 * LocalPlayer/RemotePlayer
 */
class NetworkClientPlayerBase : public NetworkClientObject, public virtual Player {
    protected:
        /**
         * Dummy-initialize Player, initialize NetworkClientObject, and hook up our signals
         */
        NetworkClientPlayerBase (NetworkClient &client, NetworkObjectID obj_id, Vector position);
    
    protected:
        // @{
        /**
         * These should never be called directly, always via the network
         */
        virtual void spawn (Vector position);
        virtual void respawn (TimeMS dt);
        // @}
        
        /**
         * Ignore, the servers tells us this
         */
        virtual void die (bool start_timer = true);

    private:
        /**
         * NETMSG_PLAYER_POSITION -> PhysicsObject::updatePhysics
         */
        void on_position (NetworkPacketInput &pkt);

        /**
         * NETMSG_PLAYER_DIG -> Player::handleDig
         */
        void on_dig (NetworkPacketInput &pkt);

        /**
         * NETMSG_PLAYER_WEAPON_CHANGE -> Player::rope.handleChangeWeapon
         */
        void on_weapon_change (NetworkPacketInput &pkt);

        /**
         * NETMSG_PLAYER_ROPE_THROW -> Player::rope.updateState(ROPE_FLYING)
         */
        void on_rope_throw (NetworkPacketInput &pkt);

        /**
         * NETMSG_PLAYER_ROPE_FIXED -> Player::rope.updateState(ROPE_FIXED)
         */
        void on_rope_fixed (NetworkPacketInput &pkt);

        /**
         * NETMSG_PLAYER_ROPE_RELEASED -> Player::rope.updateState(ROPE_FOLDED)
         */
        void on_rope_released (NetworkPacketInput &pkt);

        /**
         * NETMSG_PLAYER_ROPE_LENGTH -> Player::Rope.updateLength
         */
        void on_rope_length (NetworkPacketInput &pkt);

        /**
         * NETMSG_PLAYER_SPAWN -> Player::spawn
         */
        void on_spawn (NetworkPacketInput &pkt);

        /**
         * NETMSG_PLAYER_Die -> Player::die
         */
        void on_die (NetworkPacketInput &pkt);
};

/**
 * Our NetworkClientPlayerBase + LocalPlayer specialization, this lets us handle local input
 */
class NetworkClientLocalPlayer : public NetworkClientPlayerBase, public LocalPlayer {
    public:
        /**
         * Calls NetworkClientPlayerBase/Player constructors, calls GameState::setLocalPlayer
         */
        NetworkClientLocalPlayer (NetworkClient &client, NetworkObjectID obj_id, Vector position);
        
        /**
         * Overriden from LocalPlayer to send a NETMSG_CLIENT_INPUT message without executing LocalPlayer::handleInput
         * locally
         */
        virtual void handleInput (PlayerInput input, TimeMS dt);
};

/**
 * Our NetworkClientPlayerBase + RemotePlayer specialization, this lets us handle players quitting
 */
class NetworkClientRemotePlayer : public NetworkClientPlayerBase, public RemotePlayer {
    public:
        /**
         * Calls NetworkClientPlayerBase/Player constructors and hooks up signals
         */
        NetworkClientRemotePlayer (NetworkClient &client, NetworkObjectID obj_id, Vector position);
    
    private:
        /**
         * Calls NetworkClient::player_quit, and then destroys ourselves
         */
        void on_quit (NetworkPacketInput &pkt);
};

/**
 * A Projectile that was created on the server
 */
class NetworkClientProjectile : public NetworkClientObject, public Projectile {
    public:
        /**
         * Call Projectile's constructor, hook up signals and call player->weaponFired
         */
        NetworkClientProjectile (NetworkClient &client, NetworkObjectID obj_id, Player *player, Vector position, 
                Vector velocity, Weapon *weapon);
    
    protected:
        /**
         * Overrides Projectile::onDestroy to ignore this, as we must wait for the server to tell us where it impacted
         * so that we can remove the ground reliably
         */
        virtual void onDestroy (Vector position, bool removeGround);
        
        /**
         * Overrides Projectile::onHitPlayer to ignore this, as we must wait for the server to tell us if it really did
         * happen.
         */
        virtual void onHitPlayer (Player *player);

    private:
        /**
         * NETMSG_PROJECTILE_DESTROY -> Projectile::onDestory
         */
        void on_destroy (NetworkPacketInput &pkt);

        /**
         * NETMSG_PROJECTILE_HIT_PLAYER -> Projectile::onHitPlayer
         */
        void on_hit_player (NetworkPacketInput &pkt);
};

#endif