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@431: // XXX: move terom@431: #include "Message.hh" terom@431: 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@431: send_terrain_data(*node); terom@408: terom@185: // create the player object (it logs it) terom@431: NetworkServerPlayer *player = new NetworkServerPlayer(*this, *node); terom@185: terom@185: // add to players terom@185: players.push_back(player); terom@185: } terom@408: terom@431: 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@431: netsession.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@431: 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@431: 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@431: 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@431: NetworkMessage hello_msg(*this, NETMSG_SERVER_HELLO); terom@431: hello_msg.write_vector(getPosition()); terom@432: node.send(hello_msg); terom@185: terom@431: // sync other players terom@185: for (std::list::iterator it = server.players.begin(); it != server.players.end(); it++) { terom@185: NetworkServerPlayer *player = *it; terom@434: terom@185: // player is not in players list yet terom@185: assert(player != this); terom@185: terom@435: // send info terom@435: player->send_info(node); terom@185: } terom@185: terom@431: // sync projectiles terom@431: for (std::list::iterator it = server.state.projectiles.begin(); it != server.state.projectiles.end(); it++) { terom@431: NetworkServerProjectile *proj = dynamic_cast(*it); terom@434: terom@431: // all projectiles should be under the control of the server terom@431: assert(proj); terom@434: terom@434: // send info terom@436: proj->send_info(node); terom@431: } terom@355: terom@185: // broadcast NETMSG_PLAYER_JOIN to all clients except current terom@431: NetworkMessage join_msg(*this, NETMSG_PLAYER_JOIN); terom@431: join_msg.write_vector(getPosition()); terom@432: server.netsession.all_nodes_except(node).send(join_msg); terom@185: terom@431: Engine::log(INFO, "net.server") << "Player joined: " << this << " from " << node.getRemoteAddress(); terom@431: } terom@431: terom@200: void NetworkServerPlayer::handleDig (Vector position, float radius) { terom@431: NetworkMessage msg(*this, NETMSG_PLAYER_DIG); terom@200: terom@420: Engine::log(DEBUG, "server_player.handle_dig") << "position=" << position << ", radius=" << radius; terom@431: terom@431: // write packet terom@431: msg.write_vector(position); terom@431: msg.write_float32(radius); terom@200: terom@431: // broadcast packet terom@432: server.netsession.all_nodes().send(msg); 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@431: NetworkMessage msg(*this, NETMSG_PLAYER_WEAPON_CHANGE); terom@239: terom@420: Engine::log(DEBUG, "server_player.change_weapon") << "weaponIndex=" << weaponIndex; terom@239: terom@239: // write packet terom@431: msg.write_uint8(weaponIndex); terom@239: terom@239: // XXX: only tell the client itself? terom@432: server.netsession.all_nodes().send(msg); terom@239: terom@239: // pass through terom@239: Player::handleChangeWeapon(weaponIndex); terom@223: } terom@241: terom@241: void NetworkServerPlayer::handleRopeState (RopeState state) { 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@435: // the NetworkGroup to send to terom@435: NetworkGroup all = server.netsession.all_nodes(); terom@435: terom@435: // send new rope state terom@435: send_rope_state(all, state); terom@241: } terom@241: terom@241: void NetworkServerPlayer::handleRopeLength (float length) { terom@431: NetworkMessage msg(*this, NETMSG_PLAYER_ROPE_LENGTH); terom@431: terom@431: // write packet terom@431: msg.write_float32(length); terom@431: terom@431: // send packet terom@432: server.netsession.all_nodes().send(msg); terom@241: } terom@223: terom@302: void NetworkServerPlayer::spawn (Vector position) { terom@431: NetworkMessage msg(*this, NETMSG_PLAYER_SPAWN); terom@302: terom@302: // write packet terom@431: msg.write_vector(position); terom@302: terom@420: Engine::log(DEBUG, "server_player.spawn") << this << ": position=" << position; terom@302: terom@431: // send packet terom@432: server.netsession.all_nodes().send(msg); terom@302: terom@302: // super terom@302: Player::spawn(position); terom@302: } terom@302: terom@302: void NetworkServerPlayer::die (bool start_timer) { terom@431: NetworkMessage msg(*this, NETMSG_PLAYER_DIE); terom@302: terom@420: Engine::log(DEBUG, "server_player.die") << this; terom@302: terom@431: // send packet terom@432: server.netsession.all_nodes().send(msg); terom@302: terom@302: // super terom@302: Player::die(start_timer); terom@302: } terom@302: terom@435: void NetworkServerPlayer::send_info (NetworkNode &node) { terom@435: // we send a NETMSG_PLAYER_INFO message for the player object terom@435: NetworkMessage player_msg(*this, NETMSG_PLAYER_INFO); terom@435: terom@435: // write packet terom@435: player_msg.write_vector(getPosition()); terom@435: terom@435: // send message to client terom@435: node.send(player_msg); terom@435: terom@435: // then send rope state, unless folded terom@435: if (rope.getState() != ROPE_FOLDED) terom@435: send_rope_state(node, rope.getState()); terom@435: } terom@435: terom@435: void NetworkServerPlayer::send_rope_state (NetworkTarget &target, RopeState state) { terom@435: switch (state) { terom@435: case ROPE_FLYING: { terom@435: NetworkMessage msg(*this, NETMSG_PLAYER_ROPE_THROW); terom@435: terom@435: msg.write_vector(rope.getPosition()); terom@435: msg.write_vector(rope.getVelocity()); terom@435: msg.write_float32(rope.getLength()); terom@435: terom@435: target.send(msg); terom@435: terom@435: } break; terom@435: terom@435: case ROPE_FIXED: { terom@435: NetworkMessage msg(*this, NETMSG_PLAYER_ROPE_FIXED); terom@435: terom@435: Player *player_base = rope.getPivotPlayer(); terom@435: NetworkServerPlayer *player = NULL; terom@435: terom@435: if (player_base != NULL && (player = dynamic_cast(player_base)) == NULL) terom@435: throw Error("NetworkServerPlayer::handleRopeState: rope's pivotPlayer is not a NetworkServerPlayer"); terom@435: terom@435: msg.write_vector(rope.getPosition()); terom@435: msg.write_float32(rope.getLength()); terom@435: msg.write_object(player); // may be NULL terom@435: terom@435: target.send(msg); terom@435: terom@435: } break; terom@435: terom@435: case ROPE_FOLDED: { terom@435: NetworkMessage msg(*this, NETMSG_PLAYER_ROPE_RELEASED); terom@435: terom@435: target.send(msg); terom@435: terom@435: } break; terom@435: } terom@435: } terom@435: terom@185: void NetworkServerPlayer::on_disconnected (void) { terom@431: NetworkMessage msg(*this, NETMSG_PLAYER_QUIT); 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@432: server.netsession.all_nodes().send(msg); terom@185: terom@431: // XXX: 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@431: if (src != &node) { terom@431: 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@431: NetworkMessage msg(*this, NETMSG_PLAYER_POSITION); 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@431: msg.write_vector(getPosition()); terom@431: msg.write_vector(getVelocity()); terom@431: msg.write_uint8(flags); terom@431: msg.write_float32(aim); terom@185: terom@255: // Engine::log(INFO, "server_player.send_position_update") << "obj=" << obj << " -> " << position << "+" << velocity << " [" << flags << "]"; terom@185: terom@432: server.netsession.all_nodes().send(msg); 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@431: NetworkMessage msg(*this, NETMSG_PROJECTILE_PLAYER_FIRED); terom@431: terom@431: // write out packet terom@434: msg.write_object(player); terom@431: msg.write_vector(getPosition()); terom@431: msg.write_vector(getVelocity()); terom@431: msg.write_uint8(weapon->getID()); terom@276: terom@431: // send to given target terom@432: server.netsession.all_nodes().send(msg); terom@223: } terom@223: terom@224: void NetworkServerProjectile::onDestroy (Vector position, bool removeGround) { terom@431: NetworkMessage msg(*this, NETMSG_PROJECTILE_DESTROY); terom@224: terom@420: Engine::log(DEBUG, "server_projectile.destroy") << this << "position=" << position << ", removeGround=" << removeGround; terom@276: terom@431: // write packet terom@431: msg.write_vector(position); terom@431: msg.write_uint8(removeGround ? NETWORK_PROJECTILE_REMOVE_GROUND : 0); terom@431: terom@431: // broadcast packet terom@432: server.netsession.all_nodes().send(msg); 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@431: NetworkMessage msg(*this, NETMSG_PROJECTILE_HIT_PLAYER); 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@434: msg.write_object(player); terom@296: terom@431: // send packet terom@432: server.netsession.all_nodes().send(msg); terom@304: terom@304: // super saiam@308: Projectile::onHitPlayer(player_ptr); terom@296: } terom@296: terom@436: void NetworkServerProjectile::send_info (NetworkNode &node) { terom@436: // get our NetworkServerPlayer via getOwner() terom@436: const NetworkServerPlayer *player = dynamic_cast(getOwner()); terom@436: terom@436: // as should players terom@436: assert(player); terom@436: terom@436: // send info terom@436: NetworkMessage msg(*this, NETMSG_PROJECTILE_INFO); terom@436: terom@436: // write out packet terom@436: msg.write_object(player); terom@436: msg.write_vector(getPosition()); terom@436: msg.write_vector(getVelocity()); terom@436: msg.write_uint8(getWeapon()->getID()); terom@436: terom@436: // send to given target terom@436: node.send(msg); terom@436: terom@436: }