#include "Client.hh"
#include "Protocol.hh"
#include "../Config.hh"
#include "../Engine.hh"
#include "../Logger.hh"
#include <cassert>
/*
* NetworkClientConnect
*/
NetworkClientConnect::NetworkClientConnect (Engine &engine, const NetworkEndpoint &connect_to) :
engine(engine), netsession(NETWORK_MAGIC_ID)
{
Engine::log(INFO, "net.client") << "Connecting to server: " << connect_to;
// connect NetworkSession to get server node (this is still blocking)
server = netsession.connect(connect_to);
Engine::log(INFO, "net.client") << "Connected, receiving game data";
// connect slots
slots.connect(netsession.sig_chan_message(NETCHAN_TERRAIN_ARRAY), this, &NetworkClientConnect::on_terrain_array);
slots.connect(server->sig_disconnected(), this, &NetworkClientConnect::on_disconnected);
// then we must wait for the terrain data
}
void NetworkClientConnect::on_disconnected (void) {
Engine::log(ERROR, "client.on_disconnect") << "Disconnected from server";
engine.stop();
}
void NetworkClientConnect::on_terrain_array (NetworkPacketInput &pkt, NetworkNode *node) {
// ignore if not from server
if (node != server)
return;
// read map width/height
PixelDimension map_w = pkt.read_uint32();
PixelDimension map_h = pkt.read_uint32();
// the terrain byte array
size_t terrain_size = map_w * map_h;
TerrainPixel *terrain_buf = new TerrainPixel[map_w * map_h];
// read uncompressed terrain data
size_t inflate_size = pkt.read_uncompressed(terrain_buf, terrain_size);
// invalid data?
if (inflate_size != terrain_size)
throw Error("Corrupt terrain data");
// create the terrain object that we then use, and hand over terrain_buf to it
Terrain *terrain = new Terrain(map_w, map_h, terrain_buf);
// execute connectDone
connectDone(terrain);
}
void NetworkClientConnect::connectDone (Terrain *terrain) {
// pass Terrain to engine to create game
GameState &gs = engine.onNetworkClientConnected(terrain);
Engine::log(INFO, "net.client") << "Got game data, creating player";
// create our new NetworkClient object
client = new NetworkClient(engine, gs, netsession, server);
}
/*
* NetworkClient
*/
NetworkClient::NetworkClient (Engine &engine, GameState &state, NetworkSession &netsession, NetworkNode *server) :
engine(engine), state(state), netsession(netsession), server(server),
controller(*this)
{
}
void NetworkClient::player_quit (NetworkClientRemotePlayer *player) {
(void) player;
}
/*
* NetworkClientController
*/
NetworkClientController::NetworkClientController (NetworkClient &client) :
NetworkObject_ClientController(client.netsession, NETCHAN_CORE, client.server), client(client)
{
}
void NetworkClientController::handle_create (NetworkObjectID obj_id, NetworkMessageID msg_id, NetworkPacketInput &pkt, NetworkNode *node) {
// XXX: should be server
(void) node;
switch (msg_id) {
case NETMSG_SERVER_HELLO:
on_server_hello(obj_id, pkt);
break;
case NETMSG_PLAYER_INFO:
on_player_info(obj_id, pkt);
break;
case NETMSG_PLAYER_JOIN:
on_player_join(obj_id, pkt);
break;
case NETMSG_PROJECTILE_PLAYER_FIRED:
on_projectile_player_fired(obj_id, pkt);
break;
default:
Engine::log(WARN, "client.handle_create") << "Unknown object create message: obj_id=" << obj_id << ", msg_id=" << msg_id;
}
}
void NetworkClientController::on_server_hello (NetworkObjectID obj_id, NetworkPacketInput &pkt) {
// read the packet
Vector position = pkt.read_vector();
Engine::log(DEBUG, "client.on_server_hello") << this << ": pos=" << position;
// create the LocalPlayer object
NetworkClientLocalPlayer *player = new NetworkClientLocalPlayer(client, obj_id, position);
// pass it on to engine
client.engine.onNetworkClientPlayer(player);
Engine::log(INFO, "net.client") << "Joined server: " << player;
}
void NetworkClientController::on_player_info (NetworkObjectID obj_id, NetworkPacketInput &pkt) {
// read the packet
Vector position = pkt.read_vector();
Engine::log(DEBUG, "client.on_player_info") << this << ": pos=" << position;
// create the LocalPlayer object
new NetworkClientRemotePlayer(client, obj_id, position);
}
void NetworkClientController::on_player_join (NetworkObjectID obj_id, NetworkPacketInput &pkt) {
// read the packet
Vector position = pkt.read_vector();
Engine::log(DEBUG, "client.on_player_join") << this << ": pos=" << position;
// create the RemotePlayer object
NetworkClientRemotePlayer *player = new NetworkClientRemotePlayer(client, obj_id, position);
Engine::log(INFO, "net.client") << "Player joined: " << player;
}
void NetworkClientController::on_projectile_player_fired (NetworkObjectID obj_id, NetworkPacketInput &pkt) {
// read the packet
NetworkObject *player_obj = client.controller.read_object(pkt);
Vector position = pkt.read_vector();
Vector velocity = pkt.read_vector();
WeaponID weapon_id = pkt.read_uint8();
NetworkClientPlayerBase *player;
// ignore for invalid players
if ((player = dynamic_cast<NetworkClientPlayerBase*>(player_obj)) == NULL) {
Engine::log(ERROR, "client.on_projectile_player_fired") << this << ": Unknown player object";
return;
}
Weapon *weapon;
// try and get the weapon
if ((weapon = player->getWeapon(weapon_id)) == NULL) {
Engine::log(ERROR, "client.on_projectile_player_fired") << this << ": Unknown weapon id: player=" << player << ", weapon_id=" << weapon_id;
}
Engine::log(DEBUG, "client.on_projectile_create") << this << ": player=" << player << ", pos=" << position << ", velocity=" << velocity << ", weapon=" << weapon;
// create the NetworkClientPorjectile object
new NetworkClientProjectile(client, obj_id, player, position, velocity, weapon);
}
/*
* NetworkClientObject
*/
NetworkClientObject::NetworkClientObject (NetworkClient &client, NetworkObjectID obj_id) :
NetworkObject_Client(client.controller, obj_id), client(client)
{
}
/*
* NetworkClientPlayerBase
*/
NetworkClientPlayerBase::NetworkClientPlayerBase (NetworkClient &client, NetworkObjectID obj_id, Vector position) :
Player(client.state, position, true),
NetworkClientObject(client, obj_id)
{
slots.connect(sig_message(NETMSG_PLAYER_POSITION), this, &NetworkClientPlayerBase::on_position );
slots.connect(sig_message(NETMSG_PLAYER_DIG), this, &NetworkClientPlayerBase::on_dig );
slots.connect(sig_message(NETMSG_PLAYER_WEAPON_CHANGE), this, &NetworkClientPlayerBase::on_weapon_change );
slots.connect(sig_message(NETMSG_PLAYER_ROPE_THROW), this, &NetworkClientPlayerBase::on_rope_throw );
slots.connect(sig_message(NETMSG_PLAYER_ROPE_FIXED), this, &NetworkClientPlayerBase::on_rope_fixed );
slots.connect(sig_message(NETMSG_PLAYER_ROPE_RELEASED), this, &NetworkClientPlayerBase::on_rope_released );
slots.connect(sig_message(NETMSG_PLAYER_ROPE_LENGTH), this, &NetworkClientPlayerBase::on_rope_length );
slots.connect(sig_message(NETMSG_PLAYER_SPAWN), this, &NetworkClientPlayerBase::on_spawn );
slots.connect(sig_message(NETMSG_PLAYER_DIE), this, &NetworkClientPlayerBase::on_die );
}
void NetworkClientPlayerBase::spawn (Vector position) {
(void) position;
throw Error("NetworkClientPlayerBase::spawn called");
}
void NetworkClientPlayerBase::respawn (TimeMS dt) {
(void) dt;
throw Error("NetworkClientPlayerBase::respawn called");
}
void NetworkClientPlayerBase::die (bool start_timer) {
(void) start_timer;
// ignore :>
}
void NetworkClientPlayerBase::on_position (NetworkPacketInput &pkt) {
Vector position = pkt.read_vector();
Vector velocity = pkt.read_vector();
int flags = pkt.read_uint8();
float aim = pkt.read_float32();
// Engine::log(DEBUG, "client_player.on_position") << "obj=" << obj << ", position=" << position << ", velocity=" << velocity << ", aim=" << aim << ", [" << flags << "]";
// just update...
updatePhysics(position, velocity,
flags & NETWORK_PHYSICS_INAIR,
flags & NETWORK_PHYSICS_FACE_RIGHT ? FACING_RIGHT : FACING_LEFT,
aim
);
}
void NetworkClientPlayerBase::on_dig (NetworkPacketInput &pkt) {
Vector position = pkt.read_vector();
float radius = pkt.read_float32();
Engine::log(DEBUG, "client_player.on_dig") << this << ": position=" << position << ", radius=" << radius;
// just update...
handleDig(position, radius);
}
void NetworkClientPlayerBase::on_weapon_change (NetworkPacketInput &pkt) {
uint8_t weapon_index = pkt.read_uint8();
Engine::log(DEBUG, "client_player.on_weapon_change") << this << ": weapon_index=" << weapon_index;
handleChangeWeapon(weapon_index);
}
void NetworkClientPlayerBase::on_rope_throw (NetworkPacketInput &pkt) {
Vector position = pkt.read_vector();
Vector velocity = pkt.read_vector();
float length = pkt.read_float32();
Engine::log(DEBUG, "client_player.on_rope_throw") << this << ": position=" << position << ", velocity=" << velocity << ", length=" << length;
rope.updateState(ROPE_FLYING, position, velocity, length, NULL);
}
void NetworkClientPlayerBase::on_rope_fixed (NetworkPacketInput &pkt) {
Vector position = pkt.read_vector();
float length = pkt.read_float32();
NetworkObject *player_obj = controller.read_object(pkt);
NetworkClientPlayerBase *player = NULL;
if (player_obj != NULL && (player = dynamic_cast<NetworkClientPlayerBase*>(player_obj)) == NULL) {
Engine::log(ERROR, "client.on_rope_fixed") << this << ": Unknown player object";
return;
}
Engine::log(DEBUG, "client_player.on_rope_fixed") << this << ": position=" << position << ", length=" << length
<< ", player=" << player;
rope.updateState(ROPE_FIXED, position, Vector(0, 0), length, player);
}
void NetworkClientPlayerBase::on_rope_released (NetworkPacketInput &pkt) {
(void) pkt;
Engine::log(DEBUG, "client_player.on_rope_released") << this;
// use rope.getPosition() instead of e.g. Vector(0, 0) because it will collide there...
rope.updateState(ROPE_FOLDED, rope.getPosition(), Vector(0, 0), 0, NULL);
}
void NetworkClientPlayerBase::on_rope_length (NetworkPacketInput &pkt) {
float length = pkt.read_float32();
Engine::log(DEBUG, "client_player.on_rope_length") << this << ": length=" << length;
rope.updateLength(length);
}
void NetworkClientPlayerBase::on_spawn (NetworkPacketInput &pkt) {
// read packet
Vector position = pkt.read_vector();
Engine::log(DEBUG, "client_player.on_spawn") << this << ": position=" << position;
// super
Player::spawn(position);
}
void NetworkClientPlayerBase::on_die (NetworkPacketInput &pkt) {
(void) pkt;
Engine::log(DEBUG, "client_player.on_die") << this;
// super, but don't start the respawn_timer
Player::die(false);
}
/*
* NetworkClientLocalPlayer
*/
NetworkClientLocalPlayer::NetworkClientLocalPlayer (NetworkClient &client, NetworkObjectID obj_id, Vector position) :
Player(client.state, position, true), NetworkClientPlayerBase(client, obj_id, position)
{
// set ourselves as the local player
state.setLocalPlayer(this);
}
void NetworkClientLocalPlayer::handleInput (PlayerInput input, TimeMS dt) {
NetworkPacket pkt;
// write packet
pkt.write_uint16(input);
pkt.write_uint32(dt);
// send
send(NETMSG_CLIENT_INPUT, pkt, false);
// do not handle locally
}
/*
* NetworkClientRemotePlayer
*/
NetworkClientRemotePlayer::NetworkClientRemotePlayer (NetworkClient &client, NetworkObjectID obj_id, Vector position) :
Player(client.state, position, true), NetworkClientPlayerBase(client, obj_id, position) {
// receive messages
slots.connect(sig_message(NETMSG_PLAYER_QUIT), this, &NetworkClientRemotePlayer::on_quit);
}
void NetworkClientRemotePlayer::on_quit (NetworkPacketInput &pkt) {
// pkt is empty
(void) pkt;
Engine::log(INFO, "net.client") << "Player quit: " << this;
client.player_quit(this);
// delete
// XXX: leak because deleting the slot while it's being called breaks ClanLib
// delete this;
}
/*
* NetworkClientProjectile
*/
NetworkClientProjectile::NetworkClientProjectile (NetworkClient &client, NetworkObjectID obj_id, Player *player,
Vector position, Vector velocity, Weapon *weapon) :
NetworkClientObject(client, obj_id), Projectile(player, position, velocity, weapon)
{
// hook up signals
slots.connect(sig_message(NETMSG_PROJECTILE_DESTROY), this, &NetworkClientProjectile::on_destroy);
slots.connect(sig_message(NETMSG_PROJECTILE_HIT_PLAYER), this, &NetworkClientProjectile::on_hit_player);
// tell Player
player->weaponFired(weapon);
}
void NetworkClientProjectile::onDestroy (Vector position, bool removeGround) {
(void) position;
(void) removeGround;
// ignore :>
}
void NetworkClientProjectile::onHitPlayer (Player *player) {
(void) player;
// ignore :>
}
void NetworkClientProjectile::on_destroy (NetworkPacketInput &pkt) {
Vector position = pkt.read_vector();
uint8_t flags = pkt.read_uint8();
Engine::log(DEBUG, "client_projectile.on_destroy") << this << ": position=" << position << ", flags=" << flags;
// pass on to super
Projectile::onDestroy(position, flags & NETWORK_PROJECTILE_REMOVE_GROUND);
}
void NetworkClientProjectile::on_hit_player (NetworkPacketInput &pkt) {
// read packet
NetworkObject *player_obj = controller.read_object(pkt);
NetworkClientPlayerBase *player;
// ignore for invalid players
if ((player = dynamic_cast<NetworkClientPlayerBase*>(player_obj)) == NULL) {
Engine::log(ERROR, "client.on_hit_player") << this << ": Unknown player object";
return;
}
Engine::log(DEBUG, "client_projectile.hit_player") << this << ": player=" << player;
// pass on to super
Projectile::onHitPlayer(player);
}