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