The Battle for Wesnoth  1.19.5+dev
lobby_info.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
3  by Tomasz Sniatowski <kailoran@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
17 
18 #include "addon/manager.hpp" // for installed_addons
19 #include "log.hpp"
20 #include "mp_ui_alerts.hpp"
21 
22 
23 static lg::log_domain log_engine("engine");
24 #define WRN_NG LOG_STREAM(warn, log_engine)
25 
26 static lg::log_domain log_lobby("lobby");
27 #define DBG_LB LOG_STREAM(debug, log_lobby)
28 #define WRN_LB LOG_STREAM(warn, log_lobby)
29 #define ERR_LB LOG_STREAM(err, log_lobby)
30 #define SCOPE_LB log_scope2(log_lobby, __func__)
31 
32 namespace mp
33 {
35  : installed_addons_()
36  , gamelist_()
37  , gamelist_initialized_(false)
38  , games_by_id_()
39  , games_()
40  , users_()
41  , game_filters_()
42  , game_filter_invert_()
43  , games_visibility_()
44 {
46 }
47 
49 {
50  // This function does not refer to an addon database, it calls filesystem functions.
51  // For the sanity of the mp lobby, this list should be fixed for the entire lobby session,
52  // even if the user changes the contents of the addon directory in the meantime.
54 }
55 
56 void do_notify(notify_mode mode, const std::string& sender, const std::string& message)
57 {
58  switch(mode) {
63  break;
66  break;
69  break;
72  break;
75  break;
78  break;
81  break;
82  default:
83  break;
84  }
85 }
86 
87 namespace
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 << ") " << game.display_status_string() << " ";
95  }
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"] << ") " << c[config::diff_track_attribute] << " ";
106  }
107 
108  ss << "\n";
109  return ss.str();
110 }
111 
112 } // end anonymous namespace
113 
115 {
116  SCOPE_LB;
117 
118  gamelist_ = data;
119  gamelist_initialized_ = true;
120 
121  games_by_id_.clear();
122 
123  for(const auto& c : gamelist_.mandatory_child("gamelist").child_range("game")) {
125  games_by_id_.emplace(game.id, std::move(game));
126  }
127 
128  DBG_LB << dump_games_map(games_by_id_);
129  DBG_LB << dump_games_config(gamelist_.mandatory_child("gamelist"));
130 
132 }
133 
135 {
137  // the gamelist is now corrupted, stop further processing and wait for a fresh list.
138  gamelist_initialized_ = false;
139  return false;
140  } else {
141  return true;
142  }
143 }
144 
146 {
147  SCOPE_LB;
148  if(!gamelist_initialized_) {
149  return false;
150  }
151 
152  DBG_LB << "prediff " << dump_games_config(gamelist_.mandatory_child("gamelist"));
153 
154  try {
155  gamelist_.apply_diff(data, true);
156  } catch(const config::error& e) {
157  ERR_LB << "Error while applying the gamelist diff: '" << e.message << "' Getting a new gamelist.";
158  return false;
159  }
160 
161  DBG_LB << "postdiff " << dump_games_config(gamelist_.mandatory_child("gamelist"));
162  DBG_LB << dump_games_map(games_by_id_);
163 
164  for(config& c : gamelist_.mandatory_child("gamelist").child_range("game")) {
165  DBG_LB << "data process: " << c["id"] << " (" << c[config::diff_track_attribute] << ")";
166 
167  const int game_id = c["id"].to_int();
168  if(game_id == 0) {
169  ERR_LB << "game with id 0 in gamelist config";
170  return false;
171  }
172 
173  auto current_i = games_by_id_.find(game_id);
174 
175  const std::string& diff_result = c[config::diff_track_attribute];
176 
177  if(diff_result == "new" || diff_result == "modified") {
178  // note: at this point (1.14.3) the server never sends a 'modified' and instead
179  // just sends a 'delete' followed by a 'new', it still works becasue the delete doesn't
180  // delete the element and just marks it as game_info::DELETED so that game_info::DELETED
181  // is replaced by game_info::UPDATED below. See also
182  // https://github.com/wesnoth/wesnoth/blob/1.14/src/server/server.cpp#L149
183  if(current_i == games_by_id_.end()) {
184  games_by_id_.emplace(game_id, game_info(c, installed_addons_));
185  continue;
186  }
187 
188  // Had a game with that id, so update it and mark it as such
189  current_i->second = game_info(c, installed_addons_);
190  current_i->second.display_status = game_info::disp_status::UPDATED;
191  } else if(diff_result == "deleted") {
192  if(current_i == games_by_id_.end()) {
193  WRN_LB << "Would have to delete a game that I don't have: " << game_id;
194  continue;
195  }
196 
197  if(current_i->second.display_status == game_info::disp_status::NEW) {
198  // This means the game never made it through to the user interface,
199  // so just deleting it is fine.
200  games_by_id_.erase(current_i);
201  } else {
202  current_i->second.display_status = game_info::disp_status::DELETED;
203  }
204  }
205  }
206 
207  DBG_LB << dump_games_map(games_by_id_);
208 
209  try {
211  } catch(const config::error& e) {
212  ERR_LB << "Error while applying the gamelist diff (2): '" << e.message << "' Getting a new gamelist.";
213  return false;
214  }
215 
216  DBG_LB << "postclean " << dump_games_config(gamelist_.mandatory_child("gamelist"));
217 
219  return true;
220 }
221 
223 {
224  SCOPE_LB;
225 
226  users_.clear();
227  for(const auto& c : gamelist_.child_range("user")) {
228  user_info& ui = users_.emplace_back(c);
229 
230  if(ui.game_id == 0) {
231  continue;
232  }
233 
235  if(!g) {
236  WRN_NG << "User " << ui.name << " has unknown game_id: " << ui.game_id;
237  continue;
238  }
239 
240  switch(ui.get_relation()) {
242  g->has_friends = true;
243  break;
245  g->has_ignored = true;
246  break;
247  default:
248  break;
249  }
250  }
251 
252  std::stable_sort(users_.begin(), users_.end());
253 }
254 
255 std::function<void()> lobby_info::begin_state_sync()
256 {
257  // First, update the list of game pointers to reflect any changes made to games_by_id_.
258  // This guarantees anything that calls games() before post cleanup has valid pointers,
259  // since there will likely have been changes to games_by_id_ caused by network traffic.
261 
262  return [this]() {
263  DBG_LB << "lobby_info, second state sync stage";
264  DBG_LB << "games_by_id_ size: " << games_by_id_.size();
265 
266  auto i = games_by_id_.begin();
267 
268  while(i != games_by_id_.end()) {
269  if(i->second.display_status == game_info::disp_status::DELETED) {
270  i = games_by_id_.erase(i);
271  } else {
272  i->second.display_status = game_info::disp_status::CLEAN;
273  ++i;
274  }
275  }
276 
277  DBG_LB << " -> " << games_by_id_.size();
278 
280 
281  // Now that both containers are again in sync, update the visibility mask. We want to do
282  // this last since the filer functions are expensive.
284  };
285 }
286 
288 {
289  auto i = games_by_id_.find(id);
290  return i == games_by_id_.end() ? nullptr : &i->second;
291 }
292 
294 {
295  auto i = games_by_id_.find(id);
296  return i == games_by_id_.end() ? nullptr : &i->second;
297 }
298 
299 user_info* lobby_info::get_user(const std::string& name)
300 {
301  for(auto& user : users_) {
302  if(user.name == name) {
303  return &user;
304  }
305  }
306 
307  return nullptr;
308 }
309 
311 {
312  games_.reserve(games_by_id_.size());
313  games_.clear();
314 
315  for(auto& v : games_by_id_) {
316  games_.push_back(&v.second);
317  }
318 
319  // Reset the visibility mask. Its size should then match games_'s and all its bits be true.
320  games_visibility_.resize(games_.size());
321  games_visibility_.reset();
322  games_visibility_.flip();
323 }
324 
326 {
327  for(const auto& filter_func : game_filters_) {
328  if(!game_filter_invert_(filter_func(game))) {
329  return false;
330  }
331  }
332 
333  return true;
334 }
335 
337 {
338  // Since games_visibility_ is a visibility mask over games_,
339  // they need to be the same size or we'll end up with issues.
340  assert(games_visibility_.size() == games_.size());
341 
342  for(unsigned i = 0; i < games_.size(); ++i) {
344  }
345 }
346 
347 } // end namespace mp
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
Definition: manager.cpp:192
double g
Definition: astarsearch.cpp:63
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:366
static const char * diff_track_attribute
The name of the attribute used for tracking diff changes.
Definition: config.hpp:839
child_itors child_range(config_key_type key)
Definition: config.cpp:272
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:1026
void clear_diff_track(const config &diff)
Clear any tracking info from a previous apply_diff call with tracking.
Definition: config.cpp:1087
std::function< bool(bool)> game_filter_invert_
Definition: lobby_info.hpp:149
void process_userlist()
Definition: lobby_info.cpp:222
bool gamelist_initialized_
Definition: lobby_info.hpp:139
bool is_game_visible(const game_info &)
Returns whether the game would be visible after the game filters are applied.
Definition: lobby_info.cpp:325
bool process_gamelist_diff_impl(const config &data)
Definition: lobby_info.cpp:145
void process_gamelist(const config &data)
Process a full game list.
Definition: lobby_info.cpp:114
bool process_gamelist_diff(const config &data)
Process a gamelist diff.
Definition: lobby_info.cpp:134
void refresh_installed_addons_cache()
Definition: lobby_info.cpp:48
std::function< void()> begin_state_sync()
Updates the game pointer list and returns a second stage cleanup function to be called after any acti...
Definition: lobby_info.cpp:255
game_info * get_game_by_id(int id)
Returns info on a game with the given game ID.
Definition: lobby_info.cpp:287
game_info_map games_by_id_
Definition: lobby_info.hpp:141
void make_games_vector()
Generates a new vector of game pointers from the ID/game map.
Definition: lobby_info.cpp:310
std::vector< user_info > users_
Definition: lobby_info.hpp:145
user_info * get_user(const std::string &name)
Returns info on the user with the given name, or nullptr if they don't eixst.
Definition: lobby_info.cpp:299
std::vector< game_info * > games_
Definition: lobby_info.hpp:143
boost::dynamic_bitset games_visibility_
Definition: lobby_info.hpp:151
std::map< int, game_info > game_info_map
Definition: lobby_info.hpp:35
void apply_game_filter()
Generates a new list of games that match the current filter functions and inversion setting.
Definition: lobby_info.cpp:336
std::vector< game_filter_func > game_filters_
Definition: lobby_info.hpp:147
std::vector< std::string > installed_addons_
Definition: lobby_info.hpp:135
std::size_t i
Definition: function.cpp:1028
#define WRN_NG
Definition: lobby_info.cpp:24
#define ERR_LB
Definition: lobby_info.cpp:29
static lg::log_domain log_engine("engine")
static lg::log_domain log_lobby("lobby")
#define DBG_LB
Definition: lobby_info.cpp:27
#define WRN_LB
Definition: lobby_info.cpp:28
#define SCOPE_LB
Definition: lobby_info.cpp:30
Standard logging facilities (interface).
void public_message(bool is_lobby, const std::string &sender, const std::string &message)
void game_created(const std::string &scenario, const std::string &name)
void private_message(bool is_lobby, const std::string &sender, const std::string &message)
void player_joins(bool is_lobby)
void friend_message(bool is_lobby, const std::string &sender, const std::string &message)
void server_message(bool is_lobby, const std::string &sender, const std::string &message)
void player_leaves(bool is_lobby)
Main entry points of multiplayer mode.
Definition: lobby_data.cpp:50
void do_notify(notify_mode mode, const std::string &sender, const std::string &message)
Definition: lobby_info.cpp:56
notify_mode
Definition: lobby_info.hpp:154
std::string_view data
Definition: picture.cpp:178
This class represents the info a client has about a game on the server.
Definition: lobby_data.hpp:63
This class represents the information a client has about another player.
Definition: lobby_data.hpp:30
user_relation get_relation() const
Definition: lobby_data.cpp:73
std::string name
Definition: lobby_data.hpp:51
mock_char c
#define e