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