src/Network/Client.cc
author terom
Wed, 17 Dec 2008 00:40:22 +0000
changeset 381 9b35bc329d23
parent 334 0cf3f2be51eb
child 387 294ce7ae8140
permissions -rw-r--r--
separate sockaddr stuff out of NetworkAddress... now called NetworkEndpoint

#include "Client.hh"
#include "Protocol.hh"
#include "Config.hh"
#include "../Engine.hh"
#include "../Logger.hh"

#include <cassert>

NetworkClient::NetworkClient (Engine &engine, GameState &state, const NetworkEndpoint &connect_to) : 
    engine(engine), state(state), netsession(NETWORK_MAGIC_ID), server(netsession.connect(connect_to)),
    controller(*this)
{
    // connect slots
    slots.connect(netsession.sig_chan_message(NETCHAN_TERRAIN_ARRAY), this, &NetworkClient::on_terrain_array);
    slots.connect(server->sig_disconnected(), this, &NetworkClient::on_disconnected);
}
        
void NetworkClient::on_disconnected (void) {
    Engine::log(ERROR, "client.on_disconnect") << "Disconnected from server";
    engine.stop();
}

void NetworkClient::on_terrain_array (NetworkPacketInput &pkt, NetworkNode *node) {
    // ignore if not from server
    if (node != server)
        return;

    Terrain &terrain = state.world;

    // read map width/height
    // XXX: over 2**31?
    PixelDimension map_w = pkt.read_uint32();
    PixelDimension map_h = pkt.read_uint32();

    // read map data
    for (PixelDimension x = 0; x < map_w; x++) {
        for (PixelDimension y = 0; y < map_h; y++) {
            terrain.terrain[x][y] = (TerrainType) pkt.read_uint8();
        }
    }

    // update the pixbuf
    terrain.generatePixelBuffer();
}
        
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(INFO, "client.on_server_hello") << this << ": pos=" << position;

    // create the LocalPlayer object
    new NetworkClientLocalPlayer(client, obj_id, position);
}
        
void NetworkClientController::on_player_info (NetworkObjectID obj_id, NetworkPacketInput &pkt) {
    // read the packet
    Vector position = pkt.read_vector();
    
    Engine::log(INFO, "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(INFO, "client.on_player_join") << this << ": pos=" << position;
    
    // create the RemotePlayer object
    new NetworkClientRemotePlayer(client, obj_id, position);
}
        
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(INFO, "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(INFO, "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(INFO, "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(INFO, "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(INFO, "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(INFO, "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(INFO, "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(INFO, "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(INFO, "client_player.on_spawn") << this << ": position=" << position;
    
    // super
    Player::spawn(position);
}
        
void NetworkClientPlayerBase::on_die (NetworkPacketInput &pkt) {
    (void) pkt;

    Engine::log(INFO, "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, "client_player.on_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(INFO, "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(INFO, "client_projectile.hit_player") << this << ": player=" << player;
    
    // pass on to super
    Projectile::onHitPlayer(player);
}