00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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
00059
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) {
00196
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
00222
00223
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
00243
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
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;
00404 }
00405
00406 msg->set_attr_dup("player", user->second.name().c_str());
00407 r->send_data(data, user->first);
00408
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
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 }