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