The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
lobby_info.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2017 by Tomasz Sniatowski <kailoran@gmail.com>
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 
16 
17 #include "config.hpp"
18 #include "preferences/game.hpp"
19 #include "formula/string_utils.hpp"
20 #include "gettext.hpp"
21 #include "log.hpp"
22 #include "map/map.hpp"
23 #include "map/exception.hpp"
24 #include "wml_exception.hpp"
25 #include "wesnothd_connection.hpp"
26 #include "mp_ui_alerts.hpp"
27 #include <iterator>
28 
29 static lg::log_domain log_engine("engine");
30 #define WRN_NG LOG_STREAM(warn, log_engine)
31 
32 static lg::log_domain log_lobby("lobby");
33 #define DBG_LB LOG_STREAM(debug, log_lobby)
34 #define WRN_LB LOG_STREAM(warn, log_lobby)
35 #define ERR_LB LOG_STREAM(err, log_lobby)
36 #define SCOPE_LB log_scope2(log_lobby, __func__)
37 
38 namespace mp {
39 
40 lobby_info::lobby_info(const config& game_config, const std::vector<std::string>& installed_addons)
41  : game_config_(game_config)
42  , installed_addons_(installed_addons)
43  , gamelist_()
44  , gamelist_initialized_(false)
45  , rooms_()
46  , games_by_id_()
47  , games_()
48  , games_filtered_()
49  , users_()
50  , users_sorted_()
51  , whispers_()
52  , game_filters_()
53  , game_filter_invert_(false)
54  , games_visibility_()
55 {
56 }
57 
58 void do_notify(notify_mode mode, const std::string & sender, const std::string & message)
59 {
60  switch (mode) {
61  case NOTIFY_WHISPER:
63  case NOTIFY_OWN_NICK:
64  mp_ui_alerts::private_message(true, sender, message);
65  break;
67  mp_ui_alerts::friend_message(true, sender, message);
68  break;
70  mp_ui_alerts::server_message(true, sender, message);
71  break;
72  case NOTIFY_LOBBY_QUIT:
74  break;
75  case NOTIFY_LOBBY_JOIN:
77  break;
78  case NOTIFY_MESSAGE:
79  mp_ui_alerts::public_message(true, sender, message);
80  break;
81  default:
82  break;
83  }
84 }
85 
86 namespace
87 {
88 
89 std::string dump_games_map(const lobby_info::game_info_map& games)
90 {
91  std::stringstream ss;
92  for(const auto & v : games) {
93  const game_info& game = *v.second;
94  ss << "G" << game.id << "(" << game.name << ") "
95  << game.display_status_string() << " ";
96  }
97  ss << "\n";
98  return ss.str();
99 }
100 
101 std::string dump_games_config(const config& gamelist)
102 {
103  std::stringstream ss;
104  for(const auto & c : gamelist.child_range("game")) {
105  ss << "g" << c["id"] << "(" << c["name"] << ") "
106  << c[config::diff_track_attribute] << " ";
107  }
108  ss << "\n";
109  return ss.str();
110 }
111 
112 } // end anonymous namespace
113 
115 {
116  SCOPE_LB;
117  gamelist_ = data;
118  gamelist_initialized_ = true;
119 
120  games_by_id_.clear();
121 
122  for(const auto & c : gamelist_.child("gamelist").child_range("game")) {
123  std::unique_ptr<game_info> game(new game_info(c, game_config_, installed_addons_));
124  games_by_id_.emplace(game->id, std::move(game));
125  }
126 
127  DBG_LB << dump_games_map(games_by_id_);
128  DBG_LB << dump_games_config(gamelist_.child("gamelist"));
129 
131 }
132 
134 {
135  SCOPE_LB;
136  if(!gamelist_initialized_) {
137  return false;
138  }
139 
140  DBG_LB << "prediff " << dump_games_config(gamelist_.child("gamelist"));
141  try {
142  gamelist_.apply_diff(data, true);
143  } catch(config::error& e) {
144  ERR_LB << "Error while applying the gamelist diff: '" << e.message
145  << "' Getting a new gamelist.\n";
146  return false;
147  }
148 
149  DBG_LB << "postdiff " << dump_games_config(gamelist_.child("gamelist"));
150  DBG_LB << dump_games_map(games_by_id_);
151 
152  for(config& c : gamelist_.child("gamelist").child_range("game")) {
153  DBG_LB << "data process: " << c["id"] << " (" << c[config::diff_track_attribute] << ")\n";
154  const int game_id = c["id"];
155  if(game_id == 0) {
156  ERR_LB << "game with id 0 in gamelist config" << std::endl;
157  return false;
158  }
159 
160  game_info_map::iterator current_i = games_by_id_.find(game_id);
161 
162  const std::string& diff_result = c[config::diff_track_attribute];
163  if(diff_result == "new" || diff_result == "modified") {
164  if(current_i == games_by_id_.end()) {
165  games_by_id_.emplace(game_id, std::unique_ptr<game_info>(new game_info(c, game_config_, installed_addons_)));
166  continue;
167  }
168 
169  // Had a game with that id, so update it and mark it as such
170  *(current_i->second) = game_info(c, game_config_, installed_addons_);
171  current_i->second->display_status = game_info::UPDATED;
172  } else if(diff_result == "deleted") {
173  if(current_i == games_by_id_.end()) {
174  WRN_LB << "Would have to delete a game that I don't have: " << game_id << "\n";
175  continue;
176  }
177 
178  if(current_i->second->display_status == game_info::NEW) {
179  // This means the game never made it through to the user interface,
180  // so just deleting it is fine.
181  games_by_id_.erase(current_i);
182  } else {
183  current_i->second->display_status = game_info::DELETED;
184  }
185  }
186  }
187 
188  DBG_LB << dump_games_map(games_by_id_);
189  try {
191  } catch(config::error& e) {
192  ERR_LB << "Error while applying the gamelist diff (2): '" << e.message
193  << "' Getting a new gamelist.\n";
194  return false;
195  }
196 
197  DBG_LB << "postclean " << dump_games_config(gamelist_.child("gamelist"));
199 
200  return true;
201 }
202 
204 {
205  SCOPE_LB;
206  users_.clear();
207  for(const auto & c : gamelist_.child_range("user")) {
208  users_.emplace_back(c);
209  }
210 
211  for(auto & ui : users_) {
212  if(ui.game_id == 0) {
213  continue;
214  }
215 
216  game_info* g = get_game_by_id(ui.game_id);
217  if(!g) {
218  WRN_NG << "User " << ui.name << " has unknown game_id: " << ui.game_id << "\n";
219  continue;
220  }
221 
222  switch(ui.relation) {
223  case user_info::FRIEND:
224  g->has_friends = true;
225  break;
226  case user_info::IGNORED:
227  g->has_ignored = true;
228  break;
229  default:
230  break;
231  }
232  }
233 }
234 
236 {
237  DBG_LB << "lobby_info::sync_games_display_status";
238  DBG_LB << "games_by_id_ size: " << games_by_id_.size();
239 
241  while(i != games_by_id_.end()) {
242  if(i->second->display_status == game_info::DELETED) {
243 
244  i = games_by_id_.erase(i);
245  } else {
246  i->second->display_status = game_info::CLEAN;
247  ++i;
248  }
249  }
250 
251  DBG_LB << " -> " << games_by_id_.size() << "\n";
253 }
254 
256 {
258  return i == games_by_id_.end() ? nullptr : i->second.get();
259 }
260 
262 {
263  game_info_map::const_iterator i = games_by_id_.find(id);
264  return i == games_by_id_.end() ? nullptr : i->second.get();
265 }
266 
268 {
269  for(auto & r : rooms_) {
270  if(r.name() == name)
271  return &r;
272  }
273 
274  return nullptr;
275 }
276 
278 {
279  for(const auto & r : rooms_) {
280  if(r.name() == name) {
281  return &r;
282  }
283  }
284 
285  return nullptr;
286 }
287 
289 {
290  return get_room(name) != nullptr;
291 }
292 
294 {
295  for(auto& user : users_) {
296  if(user.name == name) {
297  return &user;
298  }
299  }
300 
301  return nullptr;
302 }
303 
305 {
306  return whispers_[name];
307 }
308 
310 {
311  if(!has_room(name)) {
312  rooms_.emplace_back(name);
313  }
314 }
315 
317 {
318  DBG_LB << "lobby info: closing room " << name << "\n";
319  if(room_info* r = get_room(name)) {
320  rooms_.erase(rooms_.begin() + (r - &rooms_[0]));
321  }
322 }
323 
324 const std::vector<game_info*>& lobby_info::games_filtered() const
325 {
326  return games_filtered_;
327 }
328 
330 {
331  game_filters_.push_back(func);
332 }
333 
335 {
336  game_filters_.clear();
337 }
338 
340 {
341  game_filter_invert_ = value;
342 }
343 
345 {
346  games_filtered_.clear();
347  games_visibility_.clear();
348  games_.clear();
349 
350  for(const auto & v : games_by_id_) {
351  games_.push_back(v.second.get());
352  }
353 }
354 
356 {
357  games_filtered_.clear();
358  games_visibility_.clear();
359  for(auto g : games_) {
360  game_info& gi = *g;
361 
362  bool show = true;
363  for(const auto& filter_func : game_filters_) {
364  show = filter_func(gi);
365  if(!show) {
366  break;
367  }
368  }
369 
370  if(game_filter_invert_) {
371  show = !show;
372  }
373 
374  games_visibility_.push_back(show);
375  if(show) {
376  games_filtered_.push_back(&gi);
377  }
378  }
379 }
380 
381 void lobby_info::update_user_statuses(int game_id, const room_info* room)
382 {
383  for(auto & user : users_) {
384  user.update_state(game_id, room);
385  }
386 }
387 
388 void lobby_info::sort_users(bool by_name, bool by_relation)
389 {
390  users_sorted_.clear();
391  for(auto& u : users_) {
392  users_sorted_.push_back(&u);
393  }
394 
395  std::sort(users_sorted_.begin(), users_sorted_.end(), [&](const user_info* u1, const user_info* u2) {
396  if(by_name && by_relation) {
397  return u1->relation < u2->relation ||
398  (u1->relation == u2->relation && translation::icompare(u1->name, u2->name) < 0);
399  }
400 
401  if(by_name) {
402  return translation::icompare(u1->name, u2->name) < 0;
403  }
404 
405  if(by_relation) {
406  return u1->relation < u2->relation;
407  }
408 
409  return true;
410  });
411 }
412 
413 const std::vector<user_info*>& lobby_info::users_sorted() const
414 {
415  return users_sorted_;
416 }
417 
418 }
#define SCOPE_LB
Definition: lobby_info.cpp:36
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:352
std::vector< char_t > string
user_info * get_user(const std::string &name)
Definition: lobby_info.cpp:293
std::map< std::string, chat_session > whispers_
Definition: lobby_info.hpp:114
void show(CVideo &video, const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:153
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
static lg::log_domain log_engine("engine")
This class represents the info a client has about a game on the server.
Definition: lobby_data.hpp:137
#define DBG_LB
Definition: lobby_info.cpp:33
child_itors child_range(config_key_type key)
Definition: config.cpp:295
void update_user_statuses(int game_id, const room_info *room)
Definition: lobby_info.cpp:381
std::vector< room_info > rooms_
Definition: lobby_info.hpp:106
void add_game_filter(game_filter_func func)
Definition: lobby_info.cpp:329
void clear_diff_track(const config &diff)
Clear any tracking info from a previous apply_diff call with tracking.
Definition: config.cpp:997
std::vector< game_filter_func > game_filters_
Definition: lobby_info.hpp:115
Definitions for the interface to Wesnoth Markup Language (WML).
this class memorizes a chat session.
Definition: lobby_data.hpp:41
void process_gamelist(const config &data)
Process a full gamelist.
Definition: lobby_info.cpp:114
void apply_game_filter()
Definition: lobby_info.cpp:355
bool has_room(const std::string &name) const
Definition: lobby_info.cpp:288
room_info * get_room(const std::string &name)
Definition: lobby_info.cpp:267
std::vector< game_info * > games_filtered_
Definition: lobby_info.hpp:111
game_info * get_game_by_id(int id)
Definition: lobby_info.cpp:255
void make_games_vector()
Definition: lobby_info.cpp:344
Pubic entry points for the MP workflow.
Definition: lobby_data.cpp:48
void friend_message(bool is_lobby, const std::string &sender, const std::string &message)
void do_notify(notify_mode mode, const std::string &sender, const std::string &message)
Definition: lobby_info.cpp:58
const std::vector< std::string > & installed_addons_
Definition: lobby_info.hpp:103
#define WRN_LB
Definition: lobby_info.cpp:34
void private_message(bool is_lobby, const std::string &sender, const std::string &message)
boost::dynamic_bitset games_visibility_
Definition: lobby_info.hpp:117
void sync_games_display_status()
Definition: lobby_info.cpp:235
static lg::log_domain log_lobby("lobby")
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
Definition: manager.cpp:148
void set_game_filter_invert(bool value)
Definition: lobby_info.cpp:339
game_info_map games_by_id_
Definition: lobby_info.hpp:108
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:934
std::map< int, std::unique_ptr< game_info > > game_info_map
Definition: lobby_info.hpp:33
bool gamelist_initialized_
Definition: lobby_info.hpp:105
std::string name
Definition: lobby_data.hpp:126
#define WRN_NG
Definition: lobby_info.cpp:30
Game configuration data as global variables.
Definition: build_info.cpp:53
const std::vector< user_info * > & users_sorted() const
Definition: lobby_info.cpp:413
void public_message(bool is_lobby, const std::string &sender, const std::string &message)
std::function< bool(const game_info &)> game_filter_func
Definition: lobby_info.hpp:35
double g
Definition: astarsearch.cpp:64
std::vector< user_info * > users_sorted_
Definition: lobby_info.hpp:113
#define i
This class represents the information a client has about a room.
Definition: lobby_data.hpp:66
void server_message(bool is_lobby, const std::string &sender, const std::string &message)
bool game_filter_invert_
Definition: lobby_info.hpp:116
static int sort(lua_State *L)
Definition: ltablib.cpp:411
chat_session & get_whisper_log(const std::string &name)
Definition: lobby_info.cpp:304
void clear_game_filter()
Definition: lobby_info.cpp:334
bool process_gamelist_diff(const config &data)
Process a gamelist diff.
Definition: lobby_info.cpp:133
const std::vector< game_info * > & games_filtered() const
Definition: lobby_info.cpp:324
void process_userlist()
Definition: lobby_info.cpp:203
void player_leaves(bool is_lobby)
This class represents the information a client has about another player.
Definition: lobby_data.hpp:103
static const char * diff_track_attribute
The name of the attribute used for tracking diff changes.
Definition: config.hpp:685
lobby_info(const config &game_config, const std::vector< std::string > &installed_addons)
Definition: lobby_info.cpp:40
void close_room(const std::string &name)
Definition: lobby_info.cpp:316
int icompare(const std::string &s1, const std::string &s2)
Case-insensitive lexicographical comparison.
void player_joins(bool is_lobby)
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:31
game_display_status display_status
Definition: lobby_data.hpp:186
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
notify_mode
Definition: lobby_info.hpp:120
#define e
const config & game_config_
Definition: lobby_info.hpp:102
void sort_users(bool by_name, bool by_relation)
Definition: lobby_info.cpp:388
#define ERR_LB
Definition: lobby_info.cpp:35
user_relation relation
Definition: lobby_data.hpp:128
void open_room(const std::string &name)
Definition: lobby_info.cpp:309
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
std::vector< user_info > users_
Definition: lobby_info.hpp:112
std::vector< game_info * > games_
Definition: lobby_info.hpp:110
mock_char c
std::string::const_iterator iterator
Definition: tokenizer.hpp:24