The Battle for Wesnoth  1.15.0-dev
lobby_info.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Tomasz Sniatowski <kailoran@gmail.com>
3  Part of the Battle for Wesnoth Project https://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 "gettext.hpp"
18 #include "log.hpp"
19 #include "map/exception.hpp"
20 #include "map/map.hpp"
21 #include "mp_ui_alerts.hpp"
22 #include "preferences/game.hpp"
23 #include "wesnothd_connection.hpp"
24 
25 #include <iterator>
26 
27 static lg::log_domain log_engine("engine");
28 #define WRN_NG LOG_STREAM(warn, log_engine)
29 
30 static lg::log_domain log_lobby("lobby");
31 #define DBG_LB LOG_STREAM(debug, log_lobby)
32 #define WRN_LB LOG_STREAM(warn, log_lobby)
33 #define ERR_LB LOG_STREAM(err, log_lobby)
34 #define SCOPE_LB log_scope2(log_lobby, __func__)
35 
36 namespace mp
37 {
38 lobby_info::lobby_info(const std::vector<std::string>& installed_addons)
39  : installed_addons_(installed_addons)
40  , gamelist_()
41  , gamelist_initialized_(false)
42  , rooms_()
43  , games_by_id_()
44  , games_()
45  , users_()
46  , whispers_()
47  , game_filters_()
48  , game_filter_invert_(false)
49  , games_visibility_()
50 {
51 }
52 
53 void do_notify(notify_mode mode, const std::string& sender, const std::string& message)
54 {
55  switch(mode) {
56  case NOTIFY_WHISPER:
58  case NOTIFY_OWN_NICK:
59  mp_ui_alerts::private_message(true, sender, message);
60  break;
62  mp_ui_alerts::friend_message(true, sender, message);
63  break;
65  mp_ui_alerts::server_message(true, sender, message);
66  break;
67  case NOTIFY_LOBBY_QUIT:
69  break;
70  case NOTIFY_LOBBY_JOIN:
72  break;
73  case NOTIFY_MESSAGE:
74  mp_ui_alerts::public_message(true, sender, message);
75  break;
76  default:
77  break;
78  }
79 }
80 
81 namespace
82 {
83 std::string dump_games_map(const lobby_info::game_info_map& games)
84 {
85  std::stringstream ss;
86  for(const auto& v : games) {
87  const game_info& game = v.second;
88  ss << "G" << game.id << "(" << game.name << ") " << game.display_status_string() << " ";
89  }
90 
91  ss << "\n";
92  return ss.str();
93 }
94 
95 std::string dump_games_config(const config& gamelist)
96 {
97  std::stringstream ss;
98  for(const auto& c : gamelist.child_range("game")) {
99  ss << "g" << c["id"] << "(" << c["name"] << ") " << c[config::diff_track_attribute] << " ";
100  }
101 
102  ss << "\n";
103  return ss.str();
104 }
105 
106 } // end anonymous namespace
107 
109 {
110  SCOPE_LB;
111 
112  gamelist_ = data;
113  gamelist_initialized_ = true;
114 
115  games_by_id_.clear();
116 
117  for(const auto& c : gamelist_.child("gamelist").child_range("game")) {
119  games_by_id_.emplace(game.id, std::move(game));
120  }
121 
122  DBG_LB << dump_games_map(games_by_id_);
123  DBG_LB << dump_games_config(gamelist_.child("gamelist"));
124 
126 }
127 
129 {
130  if(!process_gamelist_diff_impl(data)) {
131  // the gamelist is now corrupted, stop further processing and wait for a fresh list.
132  gamelist_initialized_ = false;
133  return false;
134  }
135  else {
136  return true;
137  }
138 }
140 {
141  SCOPE_LB;
142  if(!gamelist_initialized_) {
143  return false;
144  }
145 
146  DBG_LB << "prediff " << dump_games_config(gamelist_.child("gamelist"));
147 
148  try {
149  gamelist_.apply_diff(data, true);
150  } catch(const config::error& e) {
151  ERR_LB << "Error while applying the gamelist diff: '" << e.message << "' Getting a new gamelist.\n";
152  return false;
153  }
154 
155  DBG_LB << "postdiff " << dump_games_config(gamelist_.child("gamelist"));
156  DBG_LB << dump_games_map(games_by_id_);
157 
158  for(config& c : gamelist_.child("gamelist").child_range("game")) {
159  DBG_LB << "data process: " << c["id"] << " (" << c[config::diff_track_attribute] << ")\n";
160 
161  const int game_id = c["id"];
162  if(game_id == 0) {
163  ERR_LB << "game with id 0 in gamelist config" << std::endl;
164  return false;
165  }
166 
167  auto current_i = games_by_id_.find(game_id);
168 
169  const std::string& diff_result = c[config::diff_track_attribute];
170 
171  if(diff_result == "new" || diff_result == "modified") {
172  // note: at this point (1.14.3) the server never sends a 'modified' and instead
173  // just sends a 'delete' followed by a 'new', it still works becasue the delete doesn't
174  // delete the element and just marks it as game_info::DELETED so that game_info::DELETED
175  // is replaced by game_info::UPDATED below. See also
176  // https://github.com/wesnoth/wesnoth/blob/1.14/src/server/server.cpp#L149
177  if(current_i == games_by_id_.end()) {
178  games_by_id_.emplace(game_id, game_info(c, installed_addons_));
179  continue;
180  }
181 
182  // Had a game with that id, so update it and mark it as such
183  current_i->second = game_info(c, installed_addons_);
184  current_i->second.display_status = game_info::UPDATED;
185  } else if(diff_result == "deleted") {
186  if(current_i == games_by_id_.end()) {
187  WRN_LB << "Would have to delete a game that I don't have: " << game_id << std::endl;
188  continue;
189  }
190 
191  if(current_i->second.display_status == game_info::NEW) {
192  // This means the game never made it through to the user interface,
193  // so just deleting it is fine.
194  games_by_id_.erase(current_i);
195  } else {
196  current_i->second.display_status = game_info::DELETED;
197  }
198  }
199  }
200 
201  DBG_LB << dump_games_map(games_by_id_);
202 
203  try {
205  } catch(const config::error& e) {
206  ERR_LB << "Error while applying the gamelist diff (2): '" << e.message << "' Getting a new gamelist.\n";
207  return false;
208  }
209 
210  DBG_LB << "postclean " << dump_games_config(gamelist_.child("gamelist"));
211 
213  return true;
214 }
215 
217 {
218  SCOPE_LB;
219 
220  users_.clear();
221  for(const auto& c : gamelist_.child_range("user")) {
222  users_.emplace_back(c);
223  }
224 
225  std::stable_sort(users_.begin(), users_.end());
226 
227  for(auto& ui : users_) {
228  if(ui.game_id == 0) {
229  continue;
230  }
231 
232  game_info* g = get_game_by_id(ui.game_id);
233  if(!g) {
234  WRN_NG << "User " << ui.name << " has unknown game_id: " << ui.game_id << std::endl;
235  continue;
236  }
237 
238  switch(ui.relation) {
239  case user_info::FRIEND:
240  g->has_friends = true;
241  break;
242  case user_info::IGNORED:
243  g->has_ignored = true;
244  break;
245  default:
246  break;
247  }
248  }
249 }
250 
252 {
253  DBG_LB << "lobby_info::sync_games_display_status";
254  DBG_LB << "games_by_id_ size: " << games_by_id_.size();
255 
256  auto i = games_by_id_.begin();
257 
258  while(i != games_by_id_.end()) {
259  if(i->second.display_status == game_info::DELETED) {
260  i = games_by_id_.erase(i);
261  } else {
262  i->second.display_status = game_info::CLEAN;
263  ++i;
264  }
265  }
266 
267  DBG_LB << " -> " << games_by_id_.size() << std::endl;
268 
270 }
271 
273 {
274  auto i = games_by_id_.find(id);
275  return i == games_by_id_.end() ? nullptr : &i->second;
276 }
277 
279 {
280  auto i = games_by_id_.find(id);
281  return i == games_by_id_.end() ? nullptr : &i->second;
282 }
283 
284 room_info* lobby_info::get_room(const std::string& name)
285 {
286  for(auto& r : rooms_) {
287  if(r.name() == name) {
288  return &r;
289  }
290  }
291 
292  return nullptr;
293 }
294 
295 const room_info* lobby_info::get_room(const std::string& name) const
296 {
297  for(const auto& r : rooms_) {
298  if(r.name() == name) {
299  return &r;
300  }
301  }
302 
303  return nullptr;
304 }
305 
306 bool lobby_info::has_room(const std::string& name) const
307 {
308  return get_room(name) != nullptr;
309 }
310 
311 user_info* lobby_info::get_user(const std::string& name)
312 {
313  for(auto& user : users_) {
314  if(user.name == name) {
315  return &user;
316  }
317  }
318 
319  return nullptr;
320 }
321 
322 void lobby_info::open_room(const std::string& name)
323 {
324  if(!has_room(name)) {
325  rooms_.emplace_back(name);
326  }
327 }
328 
329 void lobby_info::close_room(const std::string& name)
330 {
331  DBG_LB << "lobby info: closing room " << name << std::endl;
332 
333  if(room_info* r = get_room(name)) {
334  rooms_.erase(rooms_.begin() + (r - &rooms_[0]));
335  }
336 }
337 
339 {
340  games_.reserve(games_by_id_.size());
341  games_.clear();
342 
343  for(auto& v : games_by_id_) {
344  games_.push_back(&v.second);
345  }
346 
347  // Reset the visibility mask. Its size should then match games_'s and all its bits be true.
348  games_visibility_.resize(games_.size());
349  games_visibility_.reset();
350  games_visibility_.flip();
351 }
352 
354 {
355  // Since games_visibility_ is a visibility mask over games_,
356  // they need to be the same size or we'll end up with issues.
357  assert(games_visibility_.size() == games_.size());
358 
359  for(unsigned i = 0; i < games_.size(); ++i) {
360  bool show = true;
361 
362  for(const auto& filter_func : game_filters_) {
363  show = filter_func(*games_[i]);
364 
365  if(!show) {
366  break;
367  }
368  }
369 
370  if(game_filter_invert_) {
371  show = !show;
372  }
373 
375  }
376 }
377 
378 void lobby_info::update_user_statuses(int game_id, const room_info* room)
379 {
380  for(auto& user : users_) {
381  user.update_state(game_id, room);
382  }
383 }
384 
385 } // end namespace mp
#define SCOPE_LB
Definition: lobby_info.cpp:34
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:423
const config & gamelist() const
Returns the raw game list config data.
Definition: lobby_info.hpp:61
user_info * get_user(const std::string &name)
Returns info on the user with the given name, or nullptr if they don&#39;t eixst.
Definition: lobby_info.cpp:311
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:138
#define DBG_LB
Definition: lobby_info.cpp:31
child_itors child_range(config_key_type key)
Definition: config.cpp:366
std::map< int, game_info > game_info_map
Definition: lobby_info.hpp:35
void update_user_statuses(int game_id, const room_info *room)
Definition: lobby_info.cpp:378
std::vector< room_info > rooms_
Definition: lobby_info.hpp:158
void clear_diff_track(const config &diff)
Clear any tracking info from a previous apply_diff call with tracking.
Definition: config.cpp:1120
std::vector< game_filter_func > game_filters_
Definition: lobby_info.hpp:168
std::string name
Definition: lobby_data.hpp:147
void process_gamelist(const config &data)
Process a full game list.
Definition: lobby_info.cpp:108
void apply_game_filter()
Generates a new list of games that match the current filter functions and inversion setting...
Definition: lobby_info.cpp:353
room_info * get_room(const std::string &name)
Returns info on room with the given name, or nullptr if it doesn&#39;t exist.
Definition: lobby_info.cpp:284
bool has_room(const std::string &name) const
Returns whether a room with the given name has been opened.
Definition: lobby_info.cpp:306
game_info * get_game_by_id(int id)
Returns info on a game with the given game ID.
Definition: lobby_info.cpp:272
void make_games_vector()
Generates a new vector of game pointers from the ID/game map.
Definition: lobby_info.cpp:338
Pubic entry points for the MP workflow.
Definition: lobby_data.cpp:49
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:53
const t_string name
const std::vector< std::string > & installed_addons_
Definition: lobby_info.hpp:152
#define WRN_LB
Definition: lobby_info.cpp:32
void private_message(bool is_lobby, const std::string &sender, const std::string &message)
boost::dynamic_bitset games_visibility_
Definition: lobby_info.hpp:172
void sync_games_display_status()
Definition: lobby_info.cpp:251
static lg::log_domain log_lobby("lobby")
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
Definition: manager.cpp:146
game_info_map games_by_id_
Definition: lobby_info.hpp:160
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:1057
bool gamelist_initialized_
Definition: lobby_info.hpp:156
const char * display_status_string() const
Definition: lobby_data.cpp:532
std::size_t i
Definition: function.cpp:933
#define WRN_NG
Definition: lobby_info.cpp:28
const std::vector< game_info * > & games() const
Definition: lobby_info.hpp:125
void public_message(bool is_lobby, const std::string &sender, const std::string &message)
double g
Definition: astarsearch.cpp:63
This class represents the information a client has about a room.
Definition: lobby_data.hpp:67
void server_message(bool is_lobby, const std::string &sender, const std::string &message)
bool game_filter_invert_
Definition: lobby_info.hpp:170
bool process_gamelist_diff(const config &data)
Process a gamelist diff.
Definition: lobby_info.cpp:128
lobby_info(const std::vector< std::string > &installed_addons)
Definition: lobby_info.cpp:38
void process_userlist()
Definition: lobby_info.cpp:216
void player_leaves(bool is_lobby)
This class represents the information a client has about another player.
Definition: lobby_data.hpp:104
static const char * diff_track_attribute
The name of the attribute used for tracking diff changes.
Definition: config.hpp:659
void close_room(const std::string &name)
Close the chat room with the given name.
Definition: lobby_info.cpp:329
void player_joins(bool is_lobby)
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:31
notify_mode
Definition: lobby_info.hpp:175
#define e
#define ERR_LB
Definition: lobby_info.cpp:33
void open_room(const std::string &name)
Open a new chat room with the given name.
Definition: lobby_info.cpp:322
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
std::vector< user_info > users_
Definition: lobby_info.hpp:164
std::vector< game_info * > games_
Definition: lobby_info.hpp:162
mock_char c
bool process_gamelist_diff_impl(const config &data)
Definition: lobby_info.cpp:139
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:153