server/room_manager.cpp

Go to the documentation of this file.
00001 /* $Id: room_manager.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2009 - 2012 by Tomasz Sniatowski <kailoran@gmail.com>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 #include "game.hpp"
00016 #include "player_network.hpp"
00017 #include "room_manager.hpp"
00018 
00019 #include "../serialization/parser.hpp"
00020 #include "../serialization/binary_or_text.hpp"
00021 #include "../serialization/string_utils.hpp"
00022 #include "../util.hpp"
00023 #include "../filesystem.hpp"
00024 #include "../foreach.hpp"
00025 #include "../log.hpp"
00026 
00027 static lg::log_domain log_server_lobby("server/lobby");
00028 #define ERR_LOBBY LOG_STREAM(err, log_server_lobby)
00029 #define WRN_LOBBY LOG_STREAM(warn, log_server_lobby)
00030 #define LOG_LOBBY LOG_STREAM(info, log_server_lobby)
00031 #define DBG_LOBBY LOG_STREAM(debug, log_server_lobby)
00032 
00033 static lg::log_domain log_server("server");
00034 #define ERR_SERVER LOG_STREAM(err, log_server)
00035 #define WRN_SERVER LOG_STREAM(warn, log_server)
00036 #define LOG_SERVER LOG_STREAM(info, log_server)
00037 #define DBG_SERVER LOG_STREAM(debug, log_server)
00038 
00039 namespace wesnothd {
00040 
00041 const char* const room_manager::lobby_name_ = "lobby";
00042 
00043 room_manager::room_manager(player_map &all_players)
00044     : all_players_(all_players)
00045     , lobby_(NULL)
00046     , rooms_by_name_()
00047     , rooms_by_player_()
00048     , player_stored_rooms_()
00049     , filename_()
00050     , compress_stored_rooms_(true)
00051     , new_room_policy_(PP_EVERYONE)
00052     , dirty_(false)
00053 {
00054 }
00055 
00056 room_manager::~room_manager()
00057 {
00058     // this assumes the server is shutting down, so there's no need to
00059     // send the actual room-quit messages to clients
00060     write_rooms();
00061     foreach (t_rooms_by_name_::value_type i, rooms_by_name_) {
00062         delete i.second;
00063     }
00064 }
00065 
00066 room_manager::PRIVILEGE_POLICY room_manager::pp_from_string(const std::string& str)
00067 {
00068     if (str == "everyone") {
00069         return PP_EVERYONE;
00070     } else if (str == "registered") {
00071         return PP_REGISTERED;
00072     } else if (str == "admins") {
00073         return PP_ADMINS;
00074     } else if (str == "nobody") {
00075         return PP_NOBODY;
00076     }
00077     return PP_COUNT;
00078 }
00079 
00080 void room_manager::load_config(const config& cfg)
00081 {
00082     filename_ = cfg["room_save_file"].str();
00083     compress_stored_rooms_ = cfg["compress_stored_rooms"].to_bool(true);
00084     PRIVILEGE_POLICY pp = pp_from_string(cfg["new_room_policy"]);
00085     if (pp != PP_COUNT) new_room_policy_ = pp;
00086 }
00087 
00088 void room_manager::read_rooms()
00089 {
00090     if (!filename_.empty() && file_exists(filename_)) {
00091         LOG_LOBBY << "Reading rooms from " <<  filename_ << "\n";
00092         config cfg;
00093         scoped_istream file = istream_file(filename_);
00094         if (compress_stored_rooms_) {
00095             read_gz(cfg, *file);
00096         } else {
00097             read(cfg, *file);
00098         }
00099 
00100         foreach (const config &c, cfg.child_range("room")) {
00101             room* r(new room(c));
00102             if (room_exists(r->name())) {
00103                 ERR_LOBBY << "Duplicate room ignored in stored rooms: "
00104                     << r->name() << "\n";
00105                 delete r;
00106             } else {
00107                 rooms_by_name_.insert(std::make_pair(r->name(), r));
00108             }
00109         }
00110     }
00111     lobby_ = get_room(lobby_name_);
00112     if (lobby_ == NULL) {
00113         lobby_ = create_room(lobby_name_);
00114         lobby_->set_persistent(true);
00115         lobby_->set_logged(true);
00116         dirty_ = true;
00117     }
00118 }
00119 
00120 void room_manager::write_rooms()
00121 {
00122     if (filename_.empty()) return;
00123     LOG_LOBBY << "Writing rooms to " << filename_ << "\n";
00124     config cfg;
00125     foreach (const t_rooms_by_name_::value_type& v, rooms_by_name_) {
00126         const room& r = *v.second;
00127         if (r.persistent()) {
00128             config& c = cfg.add_child("room");
00129             r.write(c);
00130         }
00131     }
00132 
00133     scoped_ostream file = ostream_file(filename_);
00134     config_writer writer(*file, compress_stored_rooms_);
00135     writer.write(cfg);
00136     dirty_ = false;
00137 }
00138 
00139 
00140 
00141 room* room_manager::get_room(const std::string &name)
00142 {
00143     t_rooms_by_name_::iterator i = rooms_by_name_.find(name);
00144     if (i != rooms_by_name_.end()) {
00145         return i->second;
00146     } else {
00147         return NULL;
00148     }
00149 }
00150 
00151 bool room_manager::room_exists(const std::string &name) const
00152 {
00153     return rooms_by_name_.find(name) != rooms_by_name_.end();
00154 }
00155 
00156 room* room_manager::create_room(const std::string &name)
00157 {
00158     if (room_exists(name)) {
00159         DBG_LOBBY << "Requested creation of already existing room '" << name << "'\n";
00160         return NULL;
00161     }
00162     room* r = new room(name);
00163     rooms_by_name_.insert(std::make_pair(name, r));
00164     return r;
00165 }
00166 
00167 room* room_manager::get_create_room(const std::string &name, network::connection player)
00168 {
00169     room* r = get_room(name);
00170     if (r == NULL) {
00171         bool can_create = false;
00172         switch (new_room_policy_) {
00173             case PP_EVERYONE:
00174                 can_create = true;
00175                 break;
00176             case PP_REGISTERED:
00177                 {
00178                     player_map::iterator i = all_players_.find(player);
00179                     if (i != all_players_.end()) {
00180                         can_create = i->second.registered();
00181                     }
00182                 }
00183                 break;
00184             case PP_ADMINS:
00185                 {
00186                     player_map::iterator i = all_players_.find(player);
00187                     if (i != all_players_.end()) {
00188                         can_create = i->second.is_moderator();
00189                     }
00190                 }
00191                 break;
00192             default:
00193                 break;
00194         }
00195         if (can_create) { //TODO: check if player can create room
00196             //TODO: filter room names for abuse?
00197             r = create_room(name);
00198         } else {
00199             lobby_->send_server_message("The room does not exist", player);
00200             return NULL;
00201         }
00202     }
00203     return r;
00204 }
00205 
00206 void room_manager::enter_lobby(network::connection player)
00207 {
00208     lobby_->add_player(player);
00209     unstore_player_rooms(player);
00210 }
00211 
00212 void room_manager::enter_lobby(const wesnothd::game &game)
00213 {
00214     foreach (network::connection player, game.all_game_users()) {
00215         enter_lobby(player);
00216     }
00217 }
00218 
00219 void room_manager::exit_lobby(network::connection player)
00220 {
00221     // No messages are sent to the rooms the player is in because other members
00222     // will receive the "player-entered-game" message (or similar) anyway, and
00223     // will be able to deduce that he or she is no longer in any rooms
00224     lobby_->remove_player(player);
00225     store_player_rooms(player);
00226     t_rooms_by_player_::iterator i = rooms_by_player_.find(player);
00227     if (i != rooms_by_player_.end()) {
00228         foreach (room* r, i->second) {
00229             r->remove_player(player);
00230         }
00231     }
00232     rooms_by_player_.erase(player);
00233 }
00234 
00235 bool room_manager::in_lobby(network::connection player) const
00236 {
00237     return lobby_->is_member(player);
00238 }
00239 
00240 void room_manager::remove_player(network::connection player)
00241 {
00242     // No messages are sent since a player-quit message is sent to everyone
00243     // anyway.
00244     lobby_->remove_player(player);
00245     t_rooms_by_player_::iterator i = rooms_by_player_.find(player);
00246     if (i != rooms_by_player_.end()) {
00247         foreach (room* r, i->second) {
00248             r->remove_player(player);
00249         }
00250     }
00251     rooms_by_player_.erase(player);
00252     player_stored_rooms_.erase(player);
00253 }
00254 
00255 room* room_manager::require_room(const std::string& room_name,
00256                                    const player_map::iterator user,
00257                                    const char *log_string)
00258 {
00259     room* r = get_room(room_name);
00260     if (r == NULL) {
00261         lobby_->send_server_message("The room does not exist", user->first);
00262         WRN_LOBBY << "Player " << user->second.name()
00263             << " (conn " << user->first << ")"
00264             << " attempted to " << log_string
00265             << "a nonexistent room '" << room_name << "'\n";
00266         return NULL;
00267     }
00268     return r;
00269 }
00270 
00271 room* room_manager::require_member(const std::string& room_name,
00272                                    const player_map::iterator user,
00273                                    const char *log_string)
00274 {
00275     room* r = require_room(room_name, user, log_string);
00276     if (r == NULL) return NULL;
00277     if (!r->is_member(user->first)) {
00278         lobby_->send_server_message("You are not a member of this room", user->first);
00279         WRN_LOBBY << "Player " << user->second.name()
00280             << " (conn " << user->first << ")"
00281             << " attempted to " << log_string
00282             << "room '" << room_name << "', but is not a member of that room\n";
00283         return NULL;
00284     }
00285     return r;
00286 }
00287 
00288 bool room_manager::player_enters_room(network::connection player, wesnothd::room *room)
00289 {
00290     if (room->is_member(player)) {
00291         room->send_server_message("You are already in this room", player);
00292         return false;
00293     }
00294     //TODO: implement per-room bans, check ban status here
00295     room->add_player(player);
00296     rooms_by_player_[player].insert(room);
00297     return true;
00298 }
00299 
00300 void room_manager::player_exits_room(network::connection player, wesnothd::room *room)
00301 {
00302     room->remove_player(player);
00303     rooms_by_player_[player].erase(room);
00304 }
00305 
00306 void room_manager::store_player_rooms(network::connection player)
00307 {
00308     t_rooms_by_player_::iterator i = rooms_by_player_.find(player);
00309     if (i == rooms_by_player_.end()) {
00310         return;
00311     }
00312     if (i->second.size() < 1) {
00313         return;
00314     }
00315     t_player_stored_rooms_::iterator it =
00316         player_stored_rooms_.insert(std::make_pair(player, std::set<std::string>())).first;
00317     std::set<std::string>& store = it->second;
00318     foreach (room* r, i->second) {
00319         store.insert(r->name());
00320     }
00321 }
00322 
00323 void room_manager::unstore_player_rooms(network::connection player)
00324 {
00325     player_map::iterator i = all_players_.find(player);
00326     if (i != all_players_.end()) {
00327         unstore_player_rooms(i);
00328     }
00329 }
00330 
00331 void room_manager::unstore_player_rooms(const player_map::iterator user)
00332 {
00333     t_player_stored_rooms_::iterator it = player_stored_rooms_.find(user->first);
00334     if (it == player_stored_rooms_.end()) {
00335         return;
00336     }
00337     simple_wml::document doc;
00338     simple_wml::node& join_msg = doc.root().add_child("room_join");
00339     join_msg.set_attr_dup("player", user->second.name().c_str());
00340     foreach (const std::string& room_name, it->second) {
00341         room* r = get_create_room(room_name, user->first);
00342         if (r == NULL) {
00343             LOG_LOBBY << "Player " << user->second.name() << " unable to rejoin room " << room_name << "\n";
00344             continue;
00345         }
00346         player_enters_room(user->first, r);
00347         join_msg.set_attr_dup("room", room_name.c_str());
00348         r->send_data(doc, user->first);
00349         join_msg.remove_child("members", 0);
00350         fill_member_list(r, join_msg);
00351         join_msg.set_attr_dup("topic", r->topic().c_str());
00352         send_to_one(doc, user->first);
00353     }
00354 }
00355 
00356 void room_manager::process_message(simple_wml::document &data, const player_map::iterator user)
00357 {
00358     simple_wml::node* const message = data.root().child("message");
00359     assert (message);
00360     message->set_attr_dup("sender", user->second.name().c_str());
00361     std::string room_name = message->attr("room").to_string();
00362     if (room_name.empty()) room_name = lobby_name_;
00363     room* r = require_member(room_name, user, "message");
00364     if (r == NULL) {
00365         std::stringstream ss;
00366         ss << "You are not a member of the room '" << room_name << "'. "
00367             << "Your message has not been relayed.";
00368         lobby_->send_server_message(ss.str(), user->first);
00369         return;
00370     }
00371     if (user->second.is_message_flooding()) {
00372         r->send_server_message(
00373                 "Warning: you are sending too many messages too fast. "
00374                 "Your message has not been relayed.", user->first);
00375         return;
00376     }
00377     const simple_wml::string_span& msg = (*message)["message"];
00378     chat_message::truncate_message(msg, *message);
00379     if (r->logged()) {
00380         if (msg.size() >= 3 && simple_wml::string_span(msg.begin(), 4) == "/me ") {
00381             LOG_SERVER << network::ip_address(user->first)
00382                 << "\t<" << user->second.name()
00383                 << simple_wml::string_span(msg.begin() + 3, msg.size() - 3)
00384                 << ">\n";
00385         } else {
00386             LOG_SERVER << network::ip_address(user->first) << "\t<"
00387                 << user->second.name() << "> " << msg << "\n";
00388         }
00389     }
00390     r->send_data(data, user->first, "message");
00391 }
00392 
00393 void room_manager::process_room_join(simple_wml::document &data, const player_map::iterator user)
00394 {
00395     simple_wml::node* const msg = data.root().child("room_join");
00396     assert(msg);
00397     std::string room_name = msg->attr("room").to_string();
00398     room* r = get_create_room(room_name, user->first);
00399     if (r == NULL) {
00400         return;
00401     }
00402     if (!player_enters_room(user->first, r)) {
00403         return; //player was unable to join room
00404     }
00405     // notify other members
00406     msg->set_attr_dup("player", user->second.name().c_str());
00407     r->send_data(data, user->first);
00408     // send member list to the new member
00409     fill_member_list(r, *msg);
00410     msg->set_attr_dup("topic", r->topic().c_str());
00411     send_to_one(data, user->first);
00412 }
00413 
00414 void room_manager::process_room_part(simple_wml::document &data, const player_map::iterator user)
00415 {
00416     simple_wml::node* const msg = data.root().child("room_part");
00417     assert(msg);
00418     std::string room_name = msg->attr("room").to_string();
00419     if (room_name == lobby_name_) {
00420         lobby_->send_server_message("You cannot quit the lobby", user->first);
00421         return;
00422     }
00423     room* r = require_member(room_name, user, "quit");
00424     if (r == NULL) return;
00425     player_exits_room(user->first, r);
00426     msg->set_attr_dup("player", user->second.name().c_str());
00427     r->send_data(data);
00428     if (r->empty() && !r->persistent()) {
00429         LOG_LOBBY << "Last player left room " << room_name << ". Deleting room.\n";
00430         rooms_by_name_.erase(room_name);
00431         delete r;
00432     }
00433     send_to_one(data, user->first);
00434 }
00435 
00436 void room_manager::process_room_query(simple_wml::document& data, const player_map::iterator user)
00437 {
00438     simple_wml::node* const msg = data.root().child("room_query");
00439     assert(msg);
00440     simple_wml::document doc;
00441     simple_wml::node& resp = doc.root().add_child("room_query_response");
00442     simple_wml::node* q;
00443     q = msg->child("rooms");
00444     if (q != NULL) {
00445         fill_room_list(resp);
00446         send_to_one(doc, user->first);
00447         return;
00448     }
00449     std::string room_name = msg->attr("room").to_string();
00450     if (room_name.empty()) room_name = lobby_name_;
00451 
00452     /* room-specific queries */
00453     room* r = require_room(room_name, user, "query");
00454     if (r == NULL) return;
00455     resp.set_attr_dup("room", room_name.c_str());
00456     q = msg->child("names");
00457     if (q != NULL) {
00458         fill_member_list(r, resp);
00459         send_to_one(doc, user->first);
00460         return;
00461     }
00462     q = msg->child("persist");
00463     if (q != NULL) {
00464         if (user->second.is_moderator()) {
00465             WRN_LOBBY << "Attempted room set persistent by non-moderator";
00466         } else {
00467             if (q->attr("value").empty()) {
00468                 if (r->persistent()) {
00469                     resp.set_attr("message", "Room is persistent.");
00470                 } else {
00471                     resp.set_attr("message", "Room is not persistent.");
00472                 }
00473             } else if (q->attr("value").to_bool()) {
00474                 r->set_persistent(true);
00475                 resp.set_attr("message", "Room set as persistent.");
00476                 dirty_ = true;
00477             } else {
00478                 r->set_persistent(false);
00479                 resp.set_attr("message", "Room set as not persistent.");
00480                 dirty_ = true;
00481             }
00482             send_to_one(doc, user->first);
00483         }
00484         return;
00485     }
00486     q = msg->child("logged");
00487     if (q != NULL) {
00488         if (user->second.is_moderator()) {
00489             WRN_LOBBY << "Attempted room set logged by non-moderator.";
00490         } else {
00491             if (q->attr("value").empty()) {
00492                 if (r->persistent()) {
00493                     resp.set_attr("message", "Room is logged.");
00494                 } else {
00495                     resp.set_attr("message", "Room is not logged.");
00496                 }
00497             } else if (q->attr("value").to_bool()) {
00498                 r->set_logged(true);
00499                 resp.set_attr("message", "Room set as logged.");
00500                 dirty_ = true;
00501             } else {
00502                 r->set_logged(false);
00503                 resp.set_attr("message", "Room set as not logged.");
00504                 dirty_ = true;
00505             }
00506             send_to_one(doc, user->first);
00507         }
00508         return;
00509     }
00510     q = msg->child("topic");
00511     if (q != NULL) {
00512         if (q->attr("value").empty()) {
00513             resp.set_attr_dup("topic", r->topic().c_str());
00514             send_to_one(doc, user->first);
00515         } else {
00516             if (user->second.is_moderator()) {
00517                 WRN_LOBBY << "Attempted room set topic by non-moderator.";
00518             } else {
00519                 r->set_topic(q->attr("value").to_string());
00520                 resp.set_attr("message", "Room topic changed.");
00521                 send_to_one(doc, user->first);
00522             }
00523         }
00524     }
00525     r->send_server_message("Unknown room query type", user->first);
00526 }
00527 
00528 void room_manager::fill_room_list(simple_wml::node& root)
00529 {
00530     simple_wml::node& rooms = root.add_child("rooms");
00531     foreach (const t_rooms_by_name_::value_type& tr, rooms_by_name_) {
00532         const room& r = *tr.second;
00533         simple_wml::node& room = rooms.add_child("room");
00534         room.set_attr_dup("name", r.name().c_str());
00535         room.set_attr_dup("size", lexical_cast<std::string>(r.members().size()).c_str());
00536     }
00537 }
00538 
00539 void room_manager::fill_member_list(const room* room, simple_wml::node& root)
00540 {
00541     simple_wml::node& members = root.add_child("members");
00542     foreach (network::connection m, room->members()) {
00543         simple_wml::node& member = members.add_child("member");
00544         player_map::const_iterator mi = all_players_.find(m);
00545         if (mi != all_players_.end()) {
00546             member.set_attr_dup("name", mi->second.name().c_str());
00547         }
00548     }
00549 }
00550 } //namespace wesnothd
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:10 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs