terom@187: terom@186: #include "Server.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@381: NetworkServer::NetworkServer (GameState &state, const NetworkEndpoint &listen_addr) : terom@276: state(state), netsession(NETWORK_MAGIC_ID), controller(netsession, NETCHAN_CORE) { terom@185: terom@185: // connect slots terom@185: slots.connect(netsession.sig_node_connected(), this, &NetworkServer::on_node_connected); terom@185: terom@185: // and then we listen terom@185: netsession.listen(listen_addr); terom@185: terom@420: Engine::log(INFO, "net.server") << "Listening on interface: " << listen_addr; terom@185: } terom@185: terom@185: void NetworkServer::on_node_connected (NetworkNode *node) { terom@420: Engine::log(INFO, "net.server") << "Client connected, sending terrain data: " << node->getRemoteAddress(); terom@420: terom@408: // send the terrain data terom@408: send_terrain_data(node); terom@408: terom@185: // create the player object (it logs it) terom@185: NetworkServerPlayer *player = new NetworkServerPlayer(*this, node); terom@185: terom@185: // add to players terom@185: players.push_back(player); terom@185: } terom@408: terom@408: void NetworkServer::send_terrain_data (NetworkNode *node) { terom@408: Terrain &terrain = state.world.terrain; terom@408: terom@408: // dimensions? terom@408: PixelCoordinate map = terrain.getDimensions(); terom@408: terom@408: // translate to a byte array terom@408: size_t terrain_size = map.x * map.y; terom@408: terom@408: // get terrain buffer terom@408: const uint8_t *terrain_buf = terrain.getTerrainBuffer(); terom@408: terom@408: // allocate our packet... terom@408: BigNetworkPacket pkt ( terom@408: // NetworkChannel header terom@408: NETWORK_SESSION_HEADER_SIZE terom@408: terom@408: // our own header terom@408: + 2 * sizeof(uint32_t) terom@408: terom@408: // compressed terrain buffer terom@408: + NetworkPacketOutput::write_compressed_size(terrain_size) terom@408: ); terom@408: terom@408: // write netsession header terom@408: node->write_packet_header(pkt, NETCHAN_TERRAIN_ARRAY); terom@408: terom@408: // write terrain dimensions terom@408: pkt.write_uint32(map.x); terom@408: pkt.write_uint32(map.y); terom@408: terom@408: // write compressed terrain data terom@408: pkt.write_compressed(terrain_buf, terrain_size); terom@408: terom@408: // send terom@408: node->send_raw(pkt, true); terom@408: } terom@408: terom@408: terom@185: void NetworkServer::handle_disconnect (NetworkServerPlayer *player) { terom@185: // remove from list terom@185: players.remove(player); terom@185: } terom@274: terom@274: /* terom@274: * NetworkServerObject terom@274: */ terom@274: NetworkServerObject::NetworkServerObject (NetworkServer &server) : terom@276: NetworkObject_Server(server.controller), server(server) terom@274: { terom@274: terom@274: } terom@274: terom@274: /* terom@274: * NetworkServerPlayer terom@274: */ terom@185: NetworkServerPlayer::NetworkServerPlayer (NetworkServer &server, NetworkNode *node) : terom@274: Player(server.state, Vector(PLAYER_INITIAL_X, PLAYER_INITIAL_Y), true), NetworkServerObject(server), node(node) terom@274: { terom@185: // messages terom@185: slots.connect(node->sig_disconnected(), this, &NetworkServerPlayer::on_disconnected); terom@274: slots.connect(this->sig_message(NETMSG_CLIENT_INPUT), this, &NetworkServerPlayer::on_input); terom@185: terom@185: // the initial NETMSG_PLAYER_HELLO terom@185: NetworkPacket hello_pkt; terom@428: hello_pkt.write_vector(getPosition()); terom@185: terom@274: this->send_to(node, NETMSG_SERVER_HELLO, hello_pkt, true); terom@185: terom@185: // send other player objects terom@185: for (std::list::iterator it = server.players.begin(); it != server.players.end(); it++) { terom@355: NetworkPacket player_pkt; terom@185: NetworkServerPlayer *player = *it; terom@185: terom@185: // player is not in players list yet terom@185: assert(player != this); terom@185: terom@355: // write packet terom@428: player_pkt.write_vector(player->getPosition()); terom@185: terom@274: player->send_to(node, NETMSG_PLAYER_INFO, player_pkt, true); terom@355: terom@355: // XXX: send rope info... terom@185: } terom@185: terom@355: // XXX: send projectiles? Or let the client handle the events that the unknown projectiles generate? terom@355: terom@185: // broadcast NETMSG_PLAYER_JOIN to all clients except current terom@274: this->send_all_except(NETMSG_PLAYER_JOIN, hello_pkt, node, true); terom@420: terom@420: Engine::log(INFO, "net.server") << "Player joined: " << this << " from " << node->getRemoteAddress(); terom@185: } terom@185: terom@200: void NetworkServerPlayer::handleDig (Vector position, float radius) { terom@200: NetworkPacket pkt; terom@200: terom@200: pkt.write_vector(position); terom@200: pkt.write_float32(radius); terom@200: terom@420: Engine::log(DEBUG, "server_player.handle_dig") << "position=" << position << ", radius=" << radius; terom@200: terom@200: // tell everyone... make this reliable... terom@274: this->send_all(NETMSG_PLAYER_DIG, pkt, true); terom@200: terom@200: // and carry out the actual dig on the server as well terom@200: Player::handleDig(position, radius); terom@200: } terom@200: terom@276: void NetworkServerPlayer::handleFireWeapon (Weapon *weapon, Vector position, Vector velocity) { terom@420: Engine::log(DEBUG, "server_player.fire_weapon") << "weapon='" << weapon->getName() << "', position=" << position << ", velocity=" << velocity; terom@223: terom@223: // create new NetworkServerProjectile object terom@274: new NetworkServerProjectile(server, this, position, velocity, weapon); terom@276: terom@276: // as handleFireWeapon does terom@276: weaponFired(weapon); terom@239: } terom@239: terom@239: void NetworkServerPlayer::handleChangeWeapon (unsigned int weaponIndex) { terom@239: NetworkPacket pkt; terom@239: terom@420: Engine::log(DEBUG, "server_player.change_weapon") << "weaponIndex=" << weaponIndex; terom@239: terom@239: // write packet terom@239: pkt.write_uint8(weaponIndex); terom@239: terom@239: // XXX: only tell the client itself? terom@274: send_all(NETMSG_PLAYER_WEAPON_CHANGE, pkt, true); terom@239: terom@239: // pass through terom@239: Player::handleChangeWeapon(weaponIndex); terom@223: } terom@241: terom@241: void NetworkServerPlayer::handleRopeState (RopeState state) { terom@241: NetworkPacket pkt; terom@241: terom@420: Engine::log(DEBUG, "server_player.rope_state") << "state=" << rope.getState() << ", position=" << rope.getPosition() << ", velocity=" << rope.getVelocity() << ", length=" << rope.getLength() << ", pivotPlayer=" << rope.getPivotPlayer(); terom@241: terom@241: switch (state) { terom@241: case ROPE_FLYING: terom@241: pkt.write_vector(rope.getPosition()); terom@241: pkt.write_vector(rope.getVelocity()); terom@241: pkt.write_float32(rope.getLength()); terom@241: terom@274: send_all(NETMSG_PLAYER_ROPE_THROW, pkt, true); terom@241: terom@241: break; terom@241: terom@328: case ROPE_FIXED: { terom@328: Player *player_base = rope.getPivotPlayer(); terom@328: NetworkServerPlayer *player = NULL; terom@328: terom@328: if (player_base != NULL && (player = dynamic_cast(player_base)) == NULL) terom@328: throw Error("NetworkServerPlayer::handleRopeState: rope's pivotPlayer is not a NetworkServerPlayer"); terom@328: terom@241: pkt.write_vector(rope.getPosition()); terom@241: pkt.write_float32(rope.getLength()); terom@328: controller.write_object(pkt, player); // may be NULL terom@241: terom@274: send_all(NETMSG_PLAYER_ROPE_FIXED, pkt, true); terom@241: terom@328: } break; terom@241: terom@241: case ROPE_FOLDED: terom@274: send_all(NETMSG_PLAYER_ROPE_RELEASED, pkt, true); terom@241: terom@241: break; terom@241: } terom@241: } terom@241: terom@241: void NetworkServerPlayer::handleRopeLength (float length) { terom@241: NetworkPacket pkt; terom@241: terom@241: pkt.write_float32(length); terom@241: terom@274: send_all(NETMSG_PLAYER_ROPE_LENGTH, pkt, true); terom@241: } terom@223: terom@302: void NetworkServerPlayer::spawn (Vector position) { terom@302: NetworkPacket pkt; terom@302: terom@302: // write packet terom@302: pkt.write_vector(position); terom@302: terom@420: Engine::log(DEBUG, "server_player.spawn") << this << ": position=" << position; terom@302: terom@302: // send terom@302: send_all(NETMSG_PLAYER_SPAWN, pkt, true); terom@302: terom@302: // super terom@302: Player::spawn(position); terom@302: } terom@302: terom@302: void NetworkServerPlayer::die (bool start_timer) { terom@302: NetworkPacket pkt; terom@302: terom@420: Engine::log(DEBUG, "server_player.die") << this; terom@302: terom@302: // send terom@302: send_all(NETMSG_PLAYER_DIE, pkt, true); terom@302: terom@302: // super terom@302: Player::die(start_timer); terom@302: } terom@302: terom@185: void NetworkServerPlayer::on_disconnected (void) { terom@185: NetworkPacket pkt; terom@185: terom@420: Engine::log(INFO, "net.server") << "Player disconnected: " << this; terom@185: terom@185: // remove from server terom@185: server.handle_disconnect(this); terom@185: terom@185: // tell other clients terom@274: send_all(NETMSG_PLAYER_QUIT, pkt, true); terom@185: terom@185: // free terom@185: // delete this; terom@185: } terom@185: terom@223: void NetworkServerPlayer::on_input (NetworkNode *src, NetworkPacketInput &pkt) { terom@185: // sanity-check, other players shouldn't move terom@185: if (src != node) { terom@223: Engine::log(WARN, "server_player.on_input") << "packet from wrong src=" << src << ", node=" << node; terom@185: return; terom@185: } terom@185: terom@239: // read packet terom@221: PlayerInput input = pkt.read_uint16(); terom@334: TimeMS dt = pkt.read_uint32(); terom@185: terom@255: // Engine::log(INFO, "server_player.on_input") << "player=" << obj << ", old_pos=" << position << ", input=" << input; terom@185: terom@185: // apply input terom@334: handleInput(input, dt); terom@185: terom@185: // send position update terom@185: send_position_update(); terom@185: } terom@185: terom@185: void NetworkServerPlayer::send_position_update (void) { terom@185: NetworkPacket pkt; terom@200: terom@264: int flags = terom@264: (inAir ? NETWORK_PHYSICS_INAIR : 0) | terom@264: (facing == FACING_RIGHT ? NETWORK_PHYSICS_FACE_RIGHT : 0); terom@200: terom@428: pkt.write_vector(getPosition()); terom@428: pkt.write_vector(getVelocity()); terom@200: pkt.write_uint8(flags); terom@200: pkt.write_float32(aim); terom@185: terom@255: // Engine::log(INFO, "server_player.send_position_update") << "obj=" << obj << " -> " << position << "+" << velocity << " [" << flags << "]"; terom@185: terom@274: send_all(NETMSG_PLAYER_POSITION, pkt, false); terom@185: } terom@185: terom@223: /* terom@223: * NetworkServerProjectile terom@223: */ terom@274: NetworkServerProjectile::NetworkServerProjectile (NetworkServer &server, NetworkServerPlayer *player, Vector position, terom@274: Vector velocity, Weapon *weapon) : terom@276: Projectile(player, position, velocity, weapon, true), NetworkServerObject(server) terom@223: { terom@223: NetworkPacket pkt; terom@276: terom@276: server.controller.write_object(pkt, player); terom@223: pkt.write_vector(position); terom@223: pkt.write_vector(velocity); terom@276: pkt.write_uint8(weapon->getID()); terom@223: terom@276: send_all(NETMSG_PROJECTILE_PLAYER_FIRED, pkt, true); terom@223: } terom@223: terom@224: void NetworkServerProjectile::onDestroy (Vector position, bool removeGround) { terom@224: NetworkPacket pkt; terom@224: terom@420: Engine::log(DEBUG, "server_projectile.destroy") << this << "position=" << position << ", removeGround=" << removeGround; terom@276: terom@224: pkt.write_vector(position); terom@224: pkt.write_uint8(removeGround ? NETWORK_PROJECTILE_REMOVE_GROUND : 0); terom@224: terom@274: send_all(NETMSG_PROJECTILE_DESTROY, pkt, true); terom@224: terom@224: // XXX: leak obj, not yet implemented: obj.destory(); terom@224: terom@224: // pass on to super terom@224: Projectile::onDestroy(position, removeGround); terom@224: } terom@224: terom@330: void NetworkServerProjectile::onHitPlayer (Player *player_ptr) { terom@296: NetworkPacket pkt; terom@296: NetworkServerPlayer *player = dynamic_cast(player_ptr); terom@296: terom@296: if (player == NULL) terom@296: throw Error("NetworkServerProjectile::onHitPlayer called with non-NetworkServerPlayer player"); terom@296: terom@420: Engine::log(DEBUG, "server_projectile.hit_player") << this << ": player=" << player; terom@296: terom@296: // write packet terom@296: controller.write_object(pkt, player); terom@296: terom@296: // send terom@296: send_all(NETMSG_PROJECTILE_HIT_PLAYER, pkt, true); terom@304: terom@304: // super saiam@308: Projectile::onHitPlayer(player_ptr); terom@296: } terom@296: