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 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 "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 config& game_config, const std::vector<std::string>& installed_addons)
39  : game_config_(game_config)
40  , installed_addons_(installed_addons)
41  , gamelist_()
42  , gamelist_initialized_(false)
43  , rooms_()
44  , games_by_id_()
45  , games_()
46  , users_()
47  , whispers_()
48  , game_filters_()
49  , game_filter_invert_(false)
50  , games_visibility_()
51 {
52 }
53 
54 void do_notify(notify_mode mode, const std::string& sender, const std::string& message)
55 {
56  switch(mode) {
57  case NOTIFY_WHISPER:
59  case NOTIFY_OWN_NICK:
60  mp_ui_alerts::private_message(true, sender, message);
61  break;
63  mp_ui_alerts::friend_message(true, sender, message);
64  break;
66  mp_ui_alerts::server_message(true, sender, message);
67  break;
68  case NOTIFY_LOBBY_QUIT:
70  break;
71  case NOTIFY_LOBBY_JOIN:
73  break;
74  case NOTIFY_MESSAGE:
75  mp_ui_alerts::public_message(true, 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  SCOPE_LB;
132  if(!gamelist_initialized_) {
133  return false;
134  }
135 
136  DBG_LB << "prediff " << dump_games_config(gamelist_.child("gamelist"));
137 
138  try {
139  gamelist_.apply_diff(data, true);
140  } catch(const config::error& e) {
141  ERR_LB << "Error while applying the gamelist diff: '" << e.message << "' Getting a new gamelist.\n";
142  return false;
143  }
144 
145  DBG_LB << "postdiff " << dump_games_config(gamelist_.child("gamelist"));
146  DBG_LB << dump_games_map(games_by_id_);
147 
148  for(config& c : gamelist_.child("gamelist").child_range("game")) {
149  DBG_LB << "data process: " << c["id"] << " (" << c[config::diff_track_attribute] << ")\n";
150 
151  const int game_id = c["id"];
152  if(game_id == 0) {
153  ERR_LB << "game with id 0 in gamelist config" << std::endl;
154  return false;
155  }
156 
157  auto current_i = games_by_id_.find(game_id);
158 
159  const std::string& diff_result = c[config::diff_track_attribute];
160 
161  if(diff_result == "new" || diff_result == "modified") {
162  if(current_i == games_by_id_.end()) {
163  games_by_id_.emplace(game_id, game_info(c, game_config_, installed_addons_));
164  continue;
165  }
166 
167  // Had a game with that id, so update it and mark it as such
168  current_i->second = game_info(c, game_config_, installed_addons_);
169  current_i->second.display_status = game_info::UPDATED;
170  } else if(diff_result == "deleted") {
171  if(current_i == games_by_id_.end()) {
172  WRN_LB << "Would have to delete a game that I don't have: " << game_id << std::endl;
173  continue;
174  }
175 
176  if(current_i->second.display_status == game_info::NEW) {
177  // This means the game never made it through to the user interface,
178  // so just deleting it is fine.
179  games_by_id_.erase(current_i);
180  } else {
181  current_i->second.display_status = game_info::DELETED;
182  }
183  }
184  }
185 
186  DBG_LB << dump_games_map(games_by_id_);
187 
188  try {
190  } catch(const config::error& e) {
191  ERR_LB << "Error while applying the gamelist diff (2): '" << e.message << "' Getting a new gamelist.\n";
192  return false;
193  }
194 
195  DBG_LB << "postclean " << dump_games_config(gamelist_.child("gamelist"));
196 
198  return true;
199 }
200 
202 {
203  SCOPE_LB;
204 
205  users_.clear();
206  for(const auto& c : gamelist_.child_range("user")) {
207  users_.emplace_back(c);
208  }
209 
210  std::stable_sort(users_.begin(), users_.end());
211 
212  for(auto& ui : users_) {
213  if(ui.game_id == 0) {
214  continue;
215  }
216 
217  game_info* g = get_game_by_id(ui.game_id);
218  if(!g) {
219  WRN_NG << "User " << ui.name << " has unknown game_id: " << ui.game_id << std::endl;
220  continue;
221  }
222 
223  switch(ui.relation) {
224  case user_info::FRIEND:
225  g->has_friends = true;
226  break;
227  case user_info::IGNORED:
228  g->has_ignored = true;
229  break;
230  default:
231  break;
232  }
233  }
234 }
235 
237 {
238  DBG_LB << "lobby_info::sync_games_display_status";
239  DBG_LB << "games_by_id_ size: " << games_by_id_.size();
240 
241  auto i = games_by_id_.begin();
242 
243  while(i != games_by_id_.end()) {
244  if(i->second.display_status == game_info::DELETED) {
245  i = games_by_id_.erase(i);
246  } else {
247  i->second.display_status = game_info::CLEAN;
248  ++i;
249  }
250  }
251 
252  DBG_LB << " -> " << games_by_id_.size() << std::endl;
253 
255 }
256 
258 {
259  auto i = games_by_id_.find(id);
260  return i == games_by_id_.end() ? nullptr : &i->second;
261 }
262 
264 {
265  auto i = games_by_id_.find(id);
266  return i == games_by_id_.end() ? nullptr : &i->second;
267 }
268 
269 room_info* lobby_info::get_room(const std::string& name)
270 {
271  for(auto& r : rooms_) {
272  if(r.name() == name) {
273  return &r;
274  }
275  }
276 
277  return nullptr;
278 }
279 
280 const room_info* lobby_info::get_room(const std::string& name) const
281 {
282  for(const auto& r : rooms_) {
283  if(r.name() == name) {
284  return &r;
285  }
286  }
287 
288  return nullptr;
289 }
290 
291 bool lobby_info::has_room(const std::string& name) const
292 {
293  return get_room(name) != nullptr;
294 }
295 
296 user_info* lobby_info::get_user(const std::string& name)
297 {
298  for(auto& user : users_) {
299  if(user.name == name) {
300  return &user;
301  }
302  }
303 
304  return nullptr;
305 }
306 
307 void lobby_info::open_room(const std::string& name)
308 {
309  if(!has_room(name)) {
310  rooms_.emplace_back(name);
311  }
312 }
313 
314 void lobby_info::close_room(const std::string& name)
315 {
316  DBG_LB << "lobby info: closing room " << name << std::endl;
317 
318  if(room_info* r = get_room(name)) {
319  rooms_.erase(rooms_.begin() + (r - &rooms_[0]));
320  }
321 }
322 
324 {
325  games_.reserve(games_by_id_.size());
326  games_.clear();
327 
328  for(auto& v : games_by_id_) {
329  games_.push_back(&v.second);
330  }
331 
332  // Reset the visibility mask. Its size should then match games_'s and all its bits be true.
333  games_visibility_.resize(games_.size());
334  games_visibility_.reset();
335  games_visibility_.flip();
336 }
337 
339 {
340  // Since games_visibility_ is a visibility mask over games_,
341  // they need to be the same size or we'll end up with issues.
342  assert(games_visibility_.size() == games_.size());
343 
344  for(unsigned i = 0; i < games_.size(); ++i) {
345  bool show = true;
346 
347  for(const auto& filter_func : game_filters_) {
348  show = filter_func(*games_[i]);
349 
350  if(!show) {
351  break;
352  }
353  }
354 
355  if(game_filter_invert_) {
356  show = !show;
357  }
358 
360  }
361 }
362 
363 void lobby_info::update_user_statuses(int game_id, const room_info* room)
364 {
365  for(auto& user : users_) {
366  user.update_state(game_id, room);
367  }
368 }
369 
370 } // 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:59
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:296
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:363
std::vector< room_info > rooms_
Definition: lobby_info.hpp:154
void clear_diff_track(const config &diff)
Clear any tracking info from a previous apply_diff call with tracking.
Definition: config.cpp:1084
std::vector< game_filter_func > game_filters_
Definition: lobby_info.hpp:164
std::string name
Definition: lobby_data.hpp:147
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:338
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:269
bool has_room(const std::string &name) const
Returns whether a room with the given name has been opened.
Definition: lobby_info.cpp:291
game_info * get_game_by_id(int id)
Returns info on a game with the given game ID.
Definition: lobby_info.cpp:257
void make_games_vector()
Generates a new vector of game pointers from the ID/game map.
Definition: lobby_info.cpp:323
Pubic entry points for the MP workflow.
Definition: lobby_data.cpp:47
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:54
const std::vector< std::string > & installed_addons_
Definition: lobby_info.hpp:148
#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:168
void sync_games_display_status()
Definition: lobby_info.cpp:236
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:156
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:1021
bool gamelist_initialized_
Definition: lobby_info.hpp:152
const char * display_status_string() const
Definition: lobby_data.cpp:544
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:123
Game configuration data as global variables.
Definition: build_info.cpp:46
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:166
bool process_gamelist_diff(const config &data)
Process a gamelist diff.
Definition: lobby_info.cpp:129
void process_userlist()
Definition: lobby_info.cpp:201
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:657
lobby_info(const config &game_config, const std::vector< std::string > &installed_addons)
Definition: lobby_info.cpp:38
void close_room(const std::string &name)
Close the chat room with the given name.
Definition: lobby_info.cpp:314
void player_joins(bool is_lobby)
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:31
notify_mode
Definition: lobby_info.hpp:171
#define e
const config & game_config_
Definition: lobby_info.hpp:146
#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:307
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:160
std::vector< game_info * > games_
Definition: lobby_info.hpp:158
mock_char c
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