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