The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
room_manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2016 by Tomasz Sniatowski <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 #include "game.hpp"
15 #include "player_network.hpp"
16 #include "room_manager.hpp"
17 
18 #include "serialization/parser.hpp"
21 #include "util.hpp"
22 #include "filesystem.hpp"
23 #include "log.hpp"
24 
25 static lg::log_domain log_server_lobby("server/lobby");
26 #define ERR_LOBBY LOG_STREAM(err, log_server_lobby)
27 #define WRN_LOBBY LOG_STREAM(warn, log_server_lobby)
28 #define LOG_LOBBY LOG_STREAM(info, log_server_lobby)
29 #define DBG_LOBBY LOG_STREAM(debug, log_server_lobby)
30 
31 static lg::log_domain log_server("server");
32 #define ERR_SERVER LOG_STREAM(err, log_server)
33 #define WRN_SERVER LOG_STREAM(warn, log_server)
34 #define LOG_SERVER LOG_STREAM(info, log_server)
35 #define DBG_SERVER LOG_STREAM(debug, log_server)
36 
37 namespace wesnothd {
38 
39 const char* const room_manager::lobby_name_ = "lobby";
40 
42  : all_players_(all_players)
43  , lobby_(nullptr)
44  , rooms_by_name_()
45  , rooms_by_player_()
46  , player_stored_rooms_()
47  , filename_()
48  , compress_stored_rooms_(true)
49  , new_room_policy_(PP_EVERYONE)
50  , dirty_(false)
51 {
52 }
53 
55 {
56  // this assumes the server is shutting down, so there's no need to
57  // send the actual room-quit messages to clients
58  write_rooms();
59  for (t_rooms_by_name_::value_type i : rooms_by_name_) {
60  delete i.second;
61  }
62 }
63 
65 {
66  if (str == "everyone") {
67  return PP_EVERYONE;
68  } else if (str == "registered") {
69  return PP_REGISTERED;
70  } else if (str == "admins") {
71  return PP_ADMINS;
72  } else if (str == "nobody") {
73  return PP_NOBODY;
74  }
75  return PP_COUNT;
76 }
77 
79 {
80  filename_ = cfg["room_save_file"].str();
81  compress_stored_rooms_ = cfg["compress_stored_rooms"].to_bool(true);
82  PRIVILEGE_POLICY pp = pp_from_string(cfg["new_room_policy"]);
83  if (pp != PP_COUNT) new_room_policy_ = pp;
84 }
85 
87 {
88  if (!filename_.empty() && filesystem::file_exists(filename_)) {
89  LOG_LOBBY << "Reading rooms from " << filename_ << "\n";
90  config cfg;
93  read_gz(cfg, *file);
94  } else {
95  read(cfg, *file);
96  }
97 
98  for (const config &c : cfg.child_range("room")) {
99  room* r(new room(c));
100  if (room_exists(r->name())) {
101  ERR_LOBBY << "Duplicate room ignored in stored rooms: "
102  << r->name() << "\n";
103  delete r;
104  } else {
105  rooms_by_name_.insert(std::make_pair(r->name(), r));
106  }
107  }
108  }
110  if (lobby_ == nullptr) {
112  lobby_->set_persistent(true);
113  lobby_->set_logged(true);
114  dirty_ = true;
115  }
116 }
117 
119 {
120  if (filename_.empty()) return;
121  LOG_LOBBY << "Writing rooms to " << filename_ << "\n";
122  config cfg;
123  for (const t_rooms_by_name_::value_type& v : rooms_by_name_) {
124  const room& r = *v.second;
125  if (r.persistent()) {
126  config& c = cfg.add_child("room");
127  r.write(c);
128  }
129  }
130 
133  writer.write(cfg);
134  dirty_ = false;
135 }
136 
137 
138 
140 {
142  if (i != rooms_by_name_.end()) {
143  return i->second;
144  } else {
145  return nullptr;
146  }
147 }
148 
150 {
151  return rooms_by_name_.find(name) != rooms_by_name_.end();
152 }
153 
155 {
156  if (room_exists(name)) {
157  DBG_LOBBY << "Requested creation of already existing room '" << name << "'\n";
158  return nullptr;
159  }
160  room* r = new room(name);
161  rooms_by_name_.insert(std::make_pair(name, r));
162  return r;
163 }
164 
166 {
167  room* r = get_room(name);
168  if (r == nullptr) {
169  bool can_create = false;
170  switch (new_room_policy_) {
171  case PP_EVERYONE:
172  can_create = true;
173  break;
174  case PP_REGISTERED:
175  {
176  player_map::iterator i = all_players_.find(player);
177  if (i != all_players_.end()) {
178  can_create = i->second.registered();
179  }
180  }
181  break;
182  case PP_ADMINS:
183  {
184  player_map::iterator i = all_players_.find(player);
185  if (i != all_players_.end()) {
186  can_create = i->second.is_moderator();
187  }
188  }
189  break;
190  default:
191  break;
192  }
193  if (can_create) { //TODO: check if player can create room
194  //TODO: filter room names for abuse?
195  r = create_room(name);
196  } else {
197  lobby_->send_server_message("The room does not exist", player);
198  return nullptr;
199  }
200  }
201  return r;
202 }
203 
205 {
206  lobby_->add_player(player);
207  unstore_player_rooms(player);
208 }
209 
211 {
212  for (network::connection player : game.all_game_users()) {
214  }
215 }
216 
218 {
219  // No messages are sent to the rooms the player is in because other members
220  // will receive the "player-entered-game" message (or similar) anyway, and
221  // will be able to deduce that he or she is no longer in any rooms
222  lobby_->remove_player(player);
223  store_player_rooms(player);
225  if (i != rooms_by_player_.end()) {
226  for (room* r : i->second) {
227  r->remove_player(player);
228  }
229  }
230  rooms_by_player_.erase(player);
231 }
232 
234 {
235  return lobby_->is_member(player);
236 }
237 
239 {
240  // No messages are sent since a player-quit message is sent to everyone
241  // anyway.
242  lobby_->remove_player(player);
244  if (i != rooms_by_player_.end()) {
245  for (room* r : i->second) {
246  r->remove_player(player);
247  }
248  }
249  rooms_by_player_.erase(player);
250  player_stored_rooms_.erase(player);
251 }
252 
254  const player_map::iterator user,
255  const char *log_string)
256 {
257  room* r = get_room(room_name);
258  if (r == nullptr) {
259  lobby_->send_server_message("The room does not exist", user->first);
260  WRN_LOBBY << "Player " << user->second.name()
261  << " (conn " << user->first << ")"
262  << " attempted to " << log_string
263  << "a nonexistent room '" << room_name << "'\n";
264  return nullptr;
265  }
266  return r;
267 }
268 
270  const player_map::iterator user,
271  const char *log_string)
272 {
273  room* r = require_room(room_name, user, log_string);
274  if (r == nullptr) return nullptr;
275  if (!r->is_member(user->first)) {
276  lobby_->send_server_message("You are not a member of this room", user->first);
277  WRN_LOBBY << "Player " << user->second.name()
278  << " (conn " << user->first << ")"
279  << " attempted to " << log_string
280  << "room '" << room_name << "', but is not a member of that room\n";
281  return nullptr;
282  }
283  return r;
284 }
285 
287 {
288  if (room->is_member(player)) {
289  room->send_server_message("You are already in this room", player);
290  return false;
291  }
292  //TODO: implement per-room bans, check ban status here
293  room->add_player(player);
294  rooms_by_player_[player].insert(room);
295  return true;
296 }
297 
299 {
300  room->remove_player(player);
301  rooms_by_player_[player].erase(room);
302 }
303 
305 {
307  if (i == rooms_by_player_.end()) {
308  return;
309  }
310  if (i->second.size() < 1) {
311  return;
312  }
314  player_stored_rooms_.insert(std::make_pair(player, std::set<std::string>())).first;
315  std::set<std::string>& store = it->second;
316  for (room* r : i->second) {
317  store.insert(r->name());
318  }
319 }
320 
322 {
323  player_map::iterator i = all_players_.find(player);
324  if (i != all_players_.end()) {
326  }
327 }
328 
330 {
332  if (it == player_stored_rooms_.end()) {
333  return;
334  }
336  simple_wml::node& join_msg = doc.root().add_child("room_join");
337  join_msg.set_attr_dup("player", user->second.name().c_str());
338  for (const std::string& room_name : it->second) {
339  room* r = get_create_room(room_name, user->first);
340  if (r == nullptr) {
341  LOG_LOBBY << "Player " << user->second.name() << " unable to rejoin room " << room_name << "\n";
342  continue;
343  }
344  player_enters_room(user->first, r);
345  join_msg.set_attr_dup("room", room_name.c_str());
346  r->send_data(doc, user->first);
347  join_msg.remove_child("members", 0);
348  fill_member_list(r, join_msg);
349  join_msg.set_attr_dup("topic", r->topic().c_str());
350  send_to_one(doc, user->first);
351  }
352 }
353 
355 {
356  simple_wml::node* const message = data.root().child("message");
357  assert (message);
358  message->set_attr_dup("sender", user->second.name().c_str());
359  std::string room_name = message->attr("room").to_string();
360  if (room_name.empty()) room_name = lobby_name_;
361  room* r = require_member(room_name, user, "message");
362  if (r == nullptr) {
363  std::stringstream ss;
364  ss << "You are not a member of the room '" << room_name << "'. "
365  << "Your message has not been relayed.";
366  lobby_->send_server_message(ss.str(), user->first);
367  return;
368  }
369  if (user->second.is_message_flooding()) {
371  "Warning: you are sending too many messages too fast. "
372  "Your message has not been relayed.", user->first);
373  return;
374  }
375  const simple_wml::string_span& msg = (*message)["message"];
376  chat_message::truncate_message(msg, *message);
377  if (r->logged()) {
378  if (msg.size() >= 3 && simple_wml::string_span(msg.begin(), 4) == "/me ") {
379  LOG_SERVER << network::ip_address(user->first)
380  << "\t<" << user->second.name()
381  << simple_wml::string_span(msg.begin() + 3, msg.size() - 3)
382  << ">\n";
383  } else {
384  LOG_SERVER << network::ip_address(user->first) << "\t<"
385  << user->second.name() << "> " << msg << "\n";
386  }
387  }
388  r->send_data(data, user->first, "message");
389 }
390 
392 {
393  simple_wml::node* const msg = data.root().child("room_join");
394  assert(msg);
395  std::string room_name = msg->attr("room").to_string();
396  room* r = get_create_room(room_name, user->first);
397  if (r == nullptr) {
398  return;
399  }
400  if (!player_enters_room(user->first, r)) {
401  return; //player was unable to join room
402  }
403  // notify other members
404  msg->set_attr_dup("player", user->second.name().c_str());
405  r->send_data(data, user->first);
406  // send member list to the new member
407  fill_member_list(r, *msg);
408  msg->set_attr_dup("topic", r->topic().c_str());
409  send_to_one(data, user->first);
410 }
411 
413 {
414  simple_wml::node* const msg = data.root().child("room_part");
415  assert(msg);
416  std::string room_name = msg->attr("room").to_string();
417  if (room_name == lobby_name_) {
418  lobby_->send_server_message("You cannot quit the lobby", user->first);
419  return;
420  }
421  room* r = require_member(room_name, user, "quit");
422  if (r == nullptr) return;
423  player_exits_room(user->first, r);
424  msg->set_attr_dup("player", user->second.name().c_str());
425  r->send_data(data);
426  if (r->empty() && !r->persistent()) {
427  LOG_LOBBY << "Last player left room " << room_name << ". Deleting room.\n";
428  rooms_by_name_.erase(room_name);
429  delete r;
430  }
431  send_to_one(data, user->first);
432 }
433 
435 {
436  simple_wml::node* const msg = data.root().child("room_query");
437  assert(msg);
439  simple_wml::node& resp = doc.root().add_child("room_query_response");
441  q = msg->child("rooms");
442  if (q != nullptr) {
443  fill_room_list(resp);
444  send_to_one(doc, user->first);
445  return;
446  }
447  std::string room_name = msg->attr("room").to_string();
448  if (room_name.empty()) room_name = lobby_name_;
449 
450  /* room-specific queries */
451  room* r = require_room(room_name, user, "query");
452  if (r == nullptr) return;
453  resp.set_attr_dup("room", room_name.c_str());
454  q = msg->child("names");
455  if (q != nullptr) {
456  fill_member_list(r, resp);
457  send_to_one(doc, user->first);
458  return;
459  }
460  q = msg->child("persist");
461  if (q != nullptr) {
462  if (user->second.is_moderator()) {
463  WRN_LOBBY << "Attempted room set persistent by non-moderator";
464  } else {
465  if (q->attr("value").empty()) {
466  if (r->persistent()) {
467  resp.set_attr("message", "Room is persistent.");
468  } else {
469  resp.set_attr("message", "Room is not persistent.");
470  }
471  } else if (q->attr("value").to_bool()) {
472  r->set_persistent(true);
473  resp.set_attr("message", "Room set as persistent.");
474  dirty_ = true;
475  } else {
476  r->set_persistent(false);
477  resp.set_attr("message", "Room set as not persistent.");
478  dirty_ = true;
479  }
480  send_to_one(doc, user->first);
481  }
482  return;
483  }
484  q = msg->child("logged");
485  if (q != nullptr) {
486  if (user->second.is_moderator()) {
487  WRN_LOBBY << "Attempted room set logged by non-moderator.";
488  } else {
489  if (q->attr("value").empty()) {
490  if (r->persistent()) {
491  resp.set_attr("message", "Room is logged.");
492  } else {
493  resp.set_attr("message", "Room is not logged.");
494  }
495  } else if (q->attr("value").to_bool()) {
496  r->set_logged(true);
497  resp.set_attr("message", "Room set as logged.");
498  dirty_ = true;
499  } else {
500  r->set_logged(false);
501  resp.set_attr("message", "Room set as not logged.");
502  dirty_ = true;
503  }
504  send_to_one(doc, user->first);
505  }
506  return;
507  }
508  q = msg->child("topic");
509  if (q != nullptr) {
510  if (q->attr("value").empty()) {
511  resp.set_attr_dup("topic", r->topic().c_str());
512  send_to_one(doc, user->first);
513  } else {
514  if (user->second.is_moderator()) {
515  WRN_LOBBY << "Attempted room set topic by non-moderator.";
516  } else {
517  r->set_topic(q->attr("value").to_string());
518  resp.set_attr("message", "Room topic changed.");
519  send_to_one(doc, user->first);
520  }
521  }
522  }
523  r->send_server_message("Unknown room query type", user->first);
524 }
525 
527 {
528  simple_wml::node& rooms = root.add_child("rooms");
529  for (const t_rooms_by_name_::value_type& tr : rooms_by_name_) {
530  const room& r = *tr.second;
531  simple_wml::node& room = rooms.add_child("room");
532  room.set_attr_dup("name", r.name().c_str());
533  room.set_attr_dup("size", lexical_cast_default<std::string>(r.members().size()).c_str());
534  }
535 }
536 
538 {
539  simple_wml::node& members = root.add_child("members");
540  for (network::connection m : room->members()) {
541  simple_wml::node& member = members.add_child("member");
542  player_map::const_iterator mi = all_players_.find(m);
543  if (mi != all_players_.end()) {
544  member.set_attr_dup("name", mi->second.name().c_str());
545  }
546  }
547 }
548 } //namespace wesnothd
node & add_child(const char *name)
Definition: simple_wml.cpp:465
Definition: plugin.cc:260
void process_room_part(simple_wml::document &data, const player_map::iterator user)
Process a player's request to leave a room.
child_itors child_range(const std::string &key)
Definition: config.cpp:613
void process_message(simple_wml::document &data, const player_map::iterator user)
Process a message (chat message) sent to a room.
static lg::log_domain log_server("server")
t_rooms_by_name_ rooms_by_name_
static PRIVILEGE_POLICY pp_from_string(const std::string &str)
void write(const config &cfg)
std::string to_string() const
Definition: simple_wml.cpp:181
void set_logged(bool v)
Set the room's logged flag.
Definition: room.cpp:86
const std::string & topic() const
This room's topic/motd, sent to all joining players.
Definition: room.cpp:71
room * lobby_
The lobby-room, treated separetely.
void fill_member_list(const room *room, simple_wml::node &root)
Fill a wml node (message) with members of a room.
bool is_member(network::connection player) const
Membership checker.
Definition: room.hpp:109
const GLfloat * c
Definition: glew.h:12741
const std::string & name() const
The name of this room.
Definition: room.cpp:56
void unstore_player_rooms(const player_map::iterator user)
Unstores (rejoins) player's rooms that were previously stored.
void player_exits_room(network::connection player, room *room)
Removes a player from a room, maintaining internal consistency.
void send_server_message(const char *message, network::connection sock, simple_wml::document *docptr=nullptr) const
Prepare a text message and/or send it to a player.
Definition: room.cpp:129
node & set_attr(const char *key, const char *value)
Definition: simple_wml.cpp:411
std::string filename_
Persistent room storage filename.
const user_vector all_game_users() const
Adds players and observers into one vector and returns that.
Definition: game.cpp:1586
void store_player_rooms(network::connection player)
Stores the room names (other than lobby) of the given player for future use (rejoin) ...
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
void fill_room_list(simple_wml::node &root)
Fill a wml node (message) with a room list.
room_manager(player_map &all_players)
Room manager constructor.
void read_gz(config &cfg, std::istream &file, abstract_validator *validator)
might throw a std::ios_base::failure especially a gzip_error
Definition: parser.cpp:454
bool in_lobby(network::connection player) const
std::string filename_
Definition: action_wml.cpp:506
std::map< network::connection, player > player_map
Definition: room.hpp:25
~room_manager()
Room manager destructor.
GLdouble GLdouble GLdouble GLdouble q
Definition: glew.h:1382
node * child(const char *name)
Definition: simple_wml.cpp:607
room * get_room(const std::string &name)
Get a room by name, or nullptr if it does not exist.
static lg::log_domain log_server_lobby("server/lobby")
void remove_player(network::connection player)
Leaving the room.
Definition: room.cpp:102
room * require_member(const std::string &room_name, const player_map::iterator user, const char *log_string="use")
Check if the room exists and if the player is a member, log failures.
void truncate_message(const simple_wml::string_span &str, simple_wml::node &message)
Function to ensure a text message is within the allowed length.
t_rooms_by_player_ rooms_by_player_
A room is a group of players that can communicate via messages.
Definition: room.hpp:31
void read_rooms()
Reads stored rooms from a file on disk, or returns immediately if load_config was not called before o...
void send_data(simple_wml::document &data, const network::connection exclude=0, std::string packet_type="") const
Convenience function for sending a wml document to all (or all except one) members.
Definition: room.cpp:115
Class for writing a config out to a file in pieces.
void process_room_join(simple_wml::document &data, const player_map::iterator user)
Process a player's request to join a room.
bool add_player(network::connection player)
Joining the room.
Definition: room.cpp:91
const GLdouble * v
Definition: glew.h:1359
void remove_child(const char *name, size_t index)
Definition: simple_wml.cpp:602
std::istream * istream_file(const std::string &fname, bool treat_failure_as_error=true)
#define WRN_LOBBY
bool compress_stored_rooms_
Flag controlling whether to compress the stored rooms or not.
#define LOG_LOBBY
std::ostream * ostream_file(std::string const &fname, bool create_directory=true)
room * get_create_room(const std::string &name, network::connection player)
Get a room by name or create that room if it does not exist and creating rooms is allowed...
config & add_child(const std::string &key)
Definition: config.cpp:743
Templates and utility-routines for strings and numbers.
PRIVILEGE_POLICY new_room_policy_
Policy regarding who can create new rooms.
void exit_lobby(network::connection player)
Player exits lobby.
void set_persistent(bool v)
Set the persistent flag for this room.
Definition: room.cpp:66
room * require_room(const std::string &room_name, const player_map::iterator user, const char *log_string="use")
Check if the room exists, log failures.
void load_config(const config &cfg)
Load settings from the main config file.
node & set_attr_dup(const char *key, const char *value)
Definition: simple_wml.cpp:427
void write_rooms()
Writes rooms to the storage file or returns immediately if load_config was not called beforethe stora...
void process_room_query(simple_wml::document &data, const player_map::iterator user)
Process a general room query.
size_t i
Definition: function.cpp:1057
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
void write(config &cfg) const
Write room info to a config.
Definition: room.cpp:48
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:400
static int writer(lua_State *L, const void *b, size_t size, void *B)
Definition: lstrlib.cpp:166
bool to_bool(bool default_value=false) const
Definition: simple_wml.cpp:157
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
player_map & all_players_
Reference to the all players map.
#define LOG_SERVER
const char * begin() const
Definition: simple_wml.hpp:90
GLuint const GLchar * name
Definition: glew.h:1782
bool dirty_
'Dirty' flag with regards to room info that will be stored on disk
void remove_player(network::connection player)
Remove info abut given player from all rooms.
const GLdouble * m
Definition: glew.h:6968
-file actions.hpp
std::string ip_address(connection connection_num)
Function to get the remote ip address of a socket.
Definition: network.cpp:1179
static const char *const lobby_name_
The main (lobby) room name.
bool player_enters_room(network::connection player, room *room)
Adds a player to a room, maintaining internal consistency Will send appropriate error messages to the...
Definition: ban.cpp:28
Standard logging facilities (interface).
bool persistent() const
Whether this room should be 'persistent', i.e.
Definition: room.cpp:61
void enter_lobby(network::connection player)
Player-enters-lobby action.
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
int connection
Definition: network.hpp:74
bool room_exists(const std::string &name) const
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
bool logged() const
Whether the room is logged (and might end up in e.g.
Definition: room.cpp:81
bool empty() const
Return true iif the room is empty.
Definition: room.hpp:94
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
void set_topic(const std::string &v)
Set the topic for this room.
Definition: room.cpp:76
#define ERR_LOBBY
GLsizei const GLcharARB ** string
Definition: glew.h:4503
#define DBG_LOBBY
t_player_stored_rooms_ player_stored_rooms_
const string_span & attr(const char *key) const
Definition: simple_wml.hpp:124
const std::vector< network::connection > & members() const
Return the members of this room.
Definition: room.hpp:101
room * create_room(const std::string &name)
Create room named "name" if it does not exist already.