The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
multiplayer_lobby.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2007 - 2016 by David White <[email protected]>
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 
15 /** @file multiplayer_lobby.cpp
16  * A section on the server where players can chat, create and join games.
17  */
18 
19 #include "global.hpp"
20 
21 #include "addon/manager_ui.hpp"
22 #include "construct_dialog.hpp"
23 #include "filesystem.hpp"
24 #include "game_preferences.hpp"
25 #include "lobby_preferences.hpp"
26 #include "map/exception.hpp"
27 #include "marked-up_text.hpp"
28 #include "minimap.hpp"
29 #include "multiplayer_lobby.hpp"
30 #include "gettext.hpp"
32 #include "gui/dialogs/message.hpp" // for gui2::show_message
34 #include "gui/widgets/window.hpp" // for gui2::twindow::OK
36 #include "log.hpp"
37 #include "sound.hpp"
38 #include "wml_exception.hpp"
39 #include "formula/string_utils.hpp"
40 #include "terrain/type_data.hpp"
41 #include "version.hpp"
42 #include "sdl/rect.hpp"
43 #include "sdl/utils.hpp"
44 #include "video.hpp"
45 
46 #include <cassert>
47 #include <boost/algorithm/string/predicate.hpp>
48 #include "utils/functional.hpp"
49 #include <boost/make_shared.hpp>
50 
51 static lg::log_domain log_config("config");
52 #define ERR_CF LOG_STREAM(err, log_config)
53 
54 static lg::log_domain log_lobby("mp/lobby");
55 #define ERR_MP LOG_STREAM(err, log_lobby)
56 #define WRN_MP LOG_STREAM(warn, log_lobby)
57 #define LOG_MP LOG_STREAM(info, log_lobby)
58 #define DBG_MP LOG_STREAM(debug, log_lobby)
59 
60 namespace {
61 std::vector<std::string> empty_string_vector;
62 }
63 
64 namespace mp {
65 gamebrowser::gamebrowser(CVideo& video, const config &map_hashes) :
66  menu(video, empty_string_vector, false, -1, -1, nullptr, &menu::bluebg_style),
67  gold_icon_locator_("themes/gold.png"),
68  xp_icon_locator_("themes/units.png"),
69  map_size_icon_locator_("misc/map.png"),
70  vision_icon_locator_("misc/visibility.png"),
71  time_limit_icon_locator_("themes/sand-clock.png"),
72  observer_icon_locator_("misc/eye.png"),
73  no_observer_icon_locator_("misc/no_observer.png"),
74  shuffle_sides_icon_locator_("misc/shuffle-sides.png"),
75  map_hashes_(map_hashes),
76  item_height_(100),
77  margin_(5),
78  minimap_size_(item_height_ - 2*margin_),
79  h_padding_(5),
80  h_padding_image_to_text_(4),
81  header_height_(20),
82  selected_(0),
83  visible_range_(std::pair<size_t,size_t>(0,0)),
84  games_(),
85  redraw_items_(),
86  widths_(),
87  double_clicked_(false),
88  ignore_next_doubleclick_(false),
89  last_was_doubleclick_(false)
90 {
92 }
93 
94 void gamebrowser::set_inner_location(const SDL_Rect& rect)
95 {
96  set_full_size(games_.size());
97  set_shown_size(rect.h / row_height());
98  bg_register(rect);
100 }
101 
102 void gamebrowser::scroll(unsigned int pos)
103 {
104  if(pos < games_.size()) {
105  visible_range_.first = pos;
106  visible_range_.second = std::min<size_t>(pos + inner_location().h / row_height(), games_.size());
107  set_dirty();
108  }
109 }
110 
111 SDL_Rect gamebrowser::get_item_rect(size_t index) const {
113  const SDL_Rect res = { 0, 0, 0, 0 };
114  return res;
115  }
116  const SDL_Rect& loc = inner_location();
117  return sdl::create_rect(
118  loc.x
119  , loc.y + (index - visible_range_.first) * row_height()
120  , loc.w
121  , row_height());
122 }
123 
125 {
126  if(hidden())
127  return;
128  if(dirty()) {
129  bg_restore();
130  util::scoped_ptr<clip_rect_setter> clipper(nullptr);
131  if(clip_rect())
132  clipper.assign(new clip_rect_setter(video().getSurface(), clip_rect()));
133  draw_contents();
135  set_dirty(false);
136  }
137 }
138 
140 {
141  if(!games_.empty()) {
142  for(size_t i = visible_range_.first; i != visible_range_.second; ++i) {
144  }
145  } else {
146  const SDL_Rect rect = inner_location();
147  font::draw_text(&video(), rect, font::SIZE_NORMAL, font::NORMAL_COLOR, _("--no games open--"), rect.x + margin_, rect.y + margin_);
148  }
149 }
150 
151 void gamebrowser::draw_row(const size_t index, const SDL_Rect& item_rect, ROW_TYPE /*type*/) {
152  const game_item& game = games_[index];
153  int xpos = item_rect.x + margin_;
154  int ypos = item_rect.y + margin_;
155  std::string no_era_string = "";
156  // Draw minimaps
157  if (game.mini_map != nullptr) {
158  int minimap_x = xpos + (minimap_size_ - game.mini_map->w)/2;
159  int minimap_y = ypos + (minimap_size_ - game.mini_map->h)/2;
160  video().blit_surface(minimap_x, minimap_y, game.mini_map);
161  }
162  xpos += minimap_size_ + margin_;
163 
164  // Set font color
165  SDL_Color font_color;
166  if (game.vacant_slots > 0) {
167  if (game.reloaded || game.started) {
168  font_color = font::YELLOW_COLOR;
169  } else {
170  font_color = font::GOOD_COLOR;
171  }
172  } else {
173  if (game.observers) {
174  font_color = font::NORMAL_COLOR;
175  } else {
176  font_color = font::BAD_COLOR;
177  }
178  }
179  if(!game.have_scenario && font_color != font::BAD_COLOR) {
180  font_color = font::DISABLED_COLOR;
181  }
182  if(!game.have_era && font_color != font::BAD_COLOR) {
183  font_color = font::DISABLED_COLOR;
184  no_era_string = _(" (Unknown Era)");
185  }
186  if(!game.have_all_mods && font_color != font::BAD_COLOR) {
187  font_color = font::DISABLED_COLOR;
188  }
189  if(game.addons_outcome != SATISFIED) {
190  font_color = font::DISABLED_COLOR;
191  if (game.addons_outcome == NEED_DOWNLOAD) {
192  no_era_string += _(" (Need to download addons)");
193  } else {
194  no_era_string += _(" (Outdated addons)");
195  }
196  } /*else {
197  no_era_string += _(" (You have this addon)");
198  }*/
199 
200  const surface status_text(font::get_rendered_text(game.status,
201  font::SIZE_NORMAL, font_color, TTF_STYLE_BOLD));
202  const int status_text_width = status_text ? status_text->w : 0;
203 
204  // First line: draw game name
205  const surface name_surf(font::get_rendered_text(
206  font::make_text_ellipsis(game.name + no_era_string, font::SIZE_PLUS,
207  (item_rect.x + item_rect.w) - xpos - margin_ - status_text_width - h_padding_),
208  font::SIZE_PLUS, font_color, TTF_STYLE_BOLD));
209  video().blit_surface(xpos, ypos, name_surf);
210 
211  // Draw status text
212  if(status_text) {
213  // Align the bottom of the text with the game name
214  video().blit_surface(item_rect.x + item_rect.w - margin_ - status_text_width,
215  ypos + name_surf->h - status_text->h, status_text);
216  }
217 
218  // Second line
219  ypos = item_rect.y + item_rect.h/3 + margin_;
220 
221  // Draw map info
222  const surface map_info_surf(font::get_rendered_text(
224  (item_rect.x + item_rect.w) - xpos - margin_),
226  if(map_info_surf) {
227  video().blit_surface(xpos, ypos - map_info_surf->h/2, map_info_surf);
228  }
229 
230  // Third line
231  ypos = item_rect.y + 2*item_rect.h/3 - margin_;
232 
233  // Draw modifications info
234  const surface era_and_mod_info_surf(font::get_rendered_text(
236  (item_rect.x + item_rect.w) - xpos - margin_),
238  if(era_and_mod_info_surf) {
239  video().blit_surface(xpos, ypos - era_and_mod_info_surf->h/2, era_and_mod_info_surf);
240  }
241 
242  // Fourth line
243  ypos = item_rect.y + item_rect.h - margin_;
244 
245  // Draw observer icon
246  const surface observer_icon(image::get_image(game.observers
248  if(observer_icon) {
249  video().blit_surface(xpos, ypos - observer_icon->h, observer_icon);
250 
251  // Set ypos to the middle of the line, so that
252  // all text and icons can be aligned symmetrical to it
253  ypos -= observer_icon->h/2;
254  xpos += observer_icon->w + 2 * h_padding_;
255  }
256 
257  // Draw shuffle icon
258  if (game.shuffle_sides)
259  {
261  if(shuffle_icon) {
262  video().blit_surface(xpos, ypos - shuffle_icon->h/2, shuffle_icon);
263 
264  xpos += shuffle_icon->w + 2 * h_padding_;
265  }
266  }
267 
268  // Draw gold icon
269  const surface gold_icon(image::get_image(gold_icon_locator_));
270  if(gold_icon) {
271  video().blit_surface(xpos, ypos - gold_icon->h/2, gold_icon);
272 
273  xpos += gold_icon->w + h_padding_image_to_text_;
274  }
275 
276  // Draw gold text
279  if(gold_text) {
280  video().blit_surface(xpos, ypos - gold_text->h/2, gold_text);
281 
282  xpos += gold_text->w + 2 * h_padding_;
283  }
284 
285  // Draw xp icon
286  const surface xp_icon(image::get_image(xp_icon_locator_));
287  if(xp_icon) {
288  video().blit_surface(xpos, ypos - xp_icon->h/2, xp_icon);
289 
290  xpos += xp_icon->w + h_padding_image_to_text_;
291  }
292 
293  // Draw xp text
294  const surface xp_text(font::get_rendered_text(game.xp, font::SIZE_NORMAL, font::NORMAL_COLOR));
295  if(xp_text) {
296  video().blit_surface(xpos, ypos - xp_text->h/2, xp_text);
297 
298  xpos += xp_text->w + 2 * h_padding_;
299  }
300 
301  if(!game.map_data.empty()) {
302  // Draw map size icon
303  const surface map_size_icon(image::get_image(map_size_icon_locator_));
304  if(map_size_icon) {
305  video().blit_surface(xpos, ypos - map_size_icon->h/2, map_size_icon);
306 
307  xpos += map_size_icon->w + h_padding_image_to_text_;
308  }
309 
310  // Draw map size text
311  const surface map_size_text(font::get_rendered_text(game.map_info_size,
312  font::SIZE_NORMAL, font::NORMAL_COLOR));
313  if(map_size_text) {
314  video().blit_surface(xpos, ypos - map_size_text->h/2, map_size_text);
315 
316  xpos += map_size_text->w + 2 * h_padding_;
317  }
318  }
319 
320  if(!game.time_limit.empty()) {
321  // Draw time icon
323  video().blit_surface(xpos, ypos - time_icon->h/2, time_icon);
324 
325  xpos += time_icon->w + h_padding_image_to_text_;
326 
327  // Draw time text
328  const surface time_text(font::get_rendered_text(game.time_limit,
329  font::SIZE_NORMAL, font::NORMAL_COLOR));
330  video().blit_surface(xpos, ypos - time_text->h/2, time_text);
331 
332  xpos += time_text->w + 2 * h_padding_;
333  }
334 
335  // Draw vision icon
336  const surface vision_icon(image::get_image(vision_icon_locator_));
337  if(vision_icon) {
338  video().blit_surface(xpos, ypos - vision_icon->h/2, vision_icon);
339 
340  xpos += vision_icon->w + h_padding_image_to_text_;
341  }
342 
343  // Draw vision text
344  const surface vision_text(font::get_rendered_text(
346  (item_rect.x + item_rect.w) - xpos - margin_),
348  game.use_map_settings ? font::GRAY_COLOR : font::NORMAL_COLOR));
349  if(vision_text) {
350  video().blit_surface(xpos, ypos - vision_text->h/2, vision_text);
351  }
352 
353  // Draw map settings text
354  if (game.use_map_settings) {
355  xpos += vision_text->w + 3 * h_padding_;
356  const surface map_settings_text(font::get_rendered_text(
357  font::make_text_ellipsis(_("Use map settings"), font::SIZE_NORMAL,
358  (item_rect.x + item_rect.w) - xpos - margin_),
360  (game.verified && game.vacant_slots > 0)
361  ? font::GOOD_COLOR : font::NORMAL_COLOR));
362  video().blit_surface(xpos, ypos - map_settings_text->h/2, map_settings_text);
363  }
364 }
365 
366 void gamebrowser::handle_event(const SDL_Event& event)
367 {
368  scrollarea::handle_event(event);
369  if(event.type == SDL_KEYDOWN) {
370  if(focus(&event) && !games_.empty()) {
371  switch(event.key.keysym.sym) {
372  case SDLK_UP:
373  if(selected_ > 0) {
374  --selected_;
376  set_dirty();
377  }
378  break;
379  case SDLK_DOWN:
380  if(selected_ < games_.size() - 1) {
381  ++selected_;
383  set_dirty();
384  }
385  break;
386  case SDLK_PAGEUP:
387  {
388  const long items_on_screen = visible_range_.second - visible_range_.first;
389  selected_ = static_cast<size_t>(std::max<long>(static_cast<long>(selected_) - items_on_screen, 0));
391  set_dirty();
392  }
393  break;
394  case SDLK_PAGEDOWN:
395  {
396  const size_t items_on_screen = visible_range_.second - visible_range_.first;
397  selected_ = std::min<size_t>(selected_ + items_on_screen, games_.size() - 1);
399  set_dirty();
400  }
401  break;
402  case SDLK_HOME:
403  selected_ = 0;
405  set_dirty();
406  break;
407  case SDLK_END:
408  selected_ = games_.size() - 1;
410  set_dirty();
411  break;
412  default:
413  break;
414  }
415  }
416  } else if((event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) || event.type == DOUBLE_CLICK_EVENT) {
417  int x = 0;
418  int y = 0;
419  if(event.type == SDL_MOUSEBUTTONDOWN) {
420  x = event.button.x;
421  y = event.button.y;
422  } else {
423  x = reinterpret_cast<size_t>(event.user.data1);
424  y = reinterpret_cast<size_t>(event.user.data2);
425  }
426  const SDL_Rect& loc = inner_location();
427 
428  if(!games_.empty() && sdl::point_in_rect(x, y, loc)) {
429  for(size_t i = visible_range_.first; i != visible_range_.second; ++i) {
430  const SDL_Rect& item_rect = get_item_rect(i);
431 
432  if(sdl::point_in_rect(x, y, item_rect)) {
433  set_focus(true);
434  selected_ = i;
435  break;
436  }
437  }
438  if(event.type == DOUBLE_CLICK_EVENT) {
440  ignore_next_doubleclick_ = false;
442  double_clicked_ = true;
443  last_was_doubleclick_ = true;
444  }
445  } else if (last_was_doubleclick_) {
446  // If we have a double click as the next event, it means
447  // this double click was generated from a click that
448  // already has helped in generating a double click.
449  // ??
450  SDL_Event ev;
451  SDL_PeepEvents(&ev, 1, SDL_PEEKEVENT,
453  if (ev.type == DOUBLE_CLICK_EVENT) {
455  }
456  last_was_doubleclick_ = false;
457  }
458  }
459  }
460 }
461 
462 static mp::ADDON_REQ check_addon_version_compatibility (const config & local_item, const config & game, std::vector<required_addon> & req_list);
463 
464 // Determine if this is a campaign or scenario and add info string to this game item.
466 {
467  if (game["mp_campaign"].empty()) {
468  if (!game["mp_scenario"].empty()) {
469  // Check if it's a multiplayer scenario.
470  const config* level_cfg = &game_config.find_child("multiplayer",
471  "id", game["mp_scenario"]);
472  if (!*level_cfg) {
473  // Check if it's a user map.
474  level_cfg = &game_config.find_child("generic_multiplayer",
475  "id", game["mp_scenario"]);
476  }
477  if (*level_cfg) {
478  item.map_info = _("Scenario:");
479  item.map_info += " ";
480  item.map_info += (*level_cfg)["name"].str();
481  // Reloaded games do not match the original scenario hash,
482  // so it makes no sense to test them,
483  // they always would appear as remote scenarios.
484  if (map_hashes_ && !item.reloaded) {
485  std::string hash = game["hash"];
486  bool hash_found = false;
488 
489  if (i.first == game["mp_scenario"] &&
490  i.second == hash) {
491 
492  hash_found = true;
493  break;
494  }
495  }
496  if (!hash_found) {
497  item.map_info += " — ";
498  item.map_info += _("Remote scenario");
499  verified = false;
500  }
501  }
502 
503  if ((*level_cfg)["require_scenario"].to_bool(false)) {
504  mp::ADDON_REQ result = check_addon_version_compatibility((*level_cfg), game, item.addons);
505  item.addons_outcome = std::max(item.addons_outcome, result); //elevate to most severe error level encountered so far
506  }
507 
508  } else {
509  utils::string_map symbols;
510  symbols["scenario_id"] = game["mp_scenario"];
511  item.map_info =
512  vgettext("Unknown scenario: $scenario_id", symbols);
513  verified = false;
514 
515  if (game["require_scenario"].to_bool(false)) {
516  item.have_scenario = false;
517  }
518  }
519  } else {
520  item.map_info = _("Unknown scenario");
521  verified = false;
522 
523  if (game["require_scenario"].to_bool(false)) {
524  item.have_scenario = false;
525  }
526  }
527  } else { // Is a campaign
528  const config* level_cfg = &game_config.find_child("campaign", "id",
529  game["mp_campaign"]);
530  if (*level_cfg) {
531  item.map_info = _("Campaign:");
532  item.map_info += " ";
533  item.map_info += (*level_cfg)["name"].str();
534  item.map_info += " — ";
535  item.map_info += game["mp_scenario_name"].str();
536 
537  // Difficulty.
538  const std::vector<std::string> difficulties =
539  utils::split((*level_cfg)["difficulties"]);
540  const std::string difficulty_descriptions =
541  (*level_cfg)["difficulty_descriptions"];
542  std::vector<std::string> difficulty_options =
543  utils::split(difficulty_descriptions, ';');
544  int index = 0;
545  //TODO: use difficulties instead of difficulty_descriptions if
546  //difficulty_descriptions is not available
547  assert(difficulties.size() == difficulty_options.size());
548  for (const std::string& difficulty : difficulties) {
549  if (difficulty == game["difficulty_define"]) {
550  gui2::tlegacy_menu_item menu_item(difficulty_options[index]);
551  item.map_info += " — ";
552  item.map_info += menu_item.label();
553  item.map_info += " ";
554  item.map_info += menu_item.description();
555 
556  break;
557  }
558  index++;
559  }
560 
561  } else {
562  utils::string_map symbols;
563  symbols["campaign_id"] = game["mp_campaign"];
564  item.map_info =
565  vgettext("Unknown campaign: $campaign_id", symbols);
566  verified = false;
567 
568  if (game["require_scenario"].to_bool(false)) {
569  item.have_scenario = false;
570  }
571  }
572  }
573 }
574 
576 
578  map_data(),
579  mini_map(),
580  map_info_size()
581  {
582  }
583 
587 };
588 
589 // Handle the minimap data. Some caching is taking place to avoid repeatedly rendering the minimaps.
591 {
592  // Don't throw the rendered minimaps away
593  std::vector<minimap_cache_item> minimap_cache;
594  for(std::vector<game_item>::iterator oldgame = games_.begin(); oldgame != games_.end(); ++oldgame) {
595  minimap_cache_item item;
596  item.map_data = oldgame->map_data;
597  item.mini_map = oldgame->mini_map;
598  item.map_info_size = oldgame->map_info_size;
599  minimap_cache.push_back(item);
600  }
601 
602  item.map_data = game["map_data"].str();
603  if(item.map_data.empty()) {
604  item.map_data = filesystem::read_map(game["map_file"]);
605  }
606  if(! item.map_data.empty()) {
607  try {
609  bool found = false;
610  for(i = minimap_cache.begin(); i != minimap_cache.end() && !found; ++i) {
611  if (i->map_data == item.map_data) {
612  found = true;
613  item.map_info_size = i->map_info_size;
614  item.mini_map = i->mini_map;
615  }
616  }
617  if (!found) {
618  // Parsing the map and generating the minimap are both cpu expensive
619  gamemap map(boost::make_shared<terrain_type_data>(game_config), item.map_data);
621  item.map_info_size = std::to_string(map.w()) + utils::unicode_multiplication_sign
622  + std::to_string(map.h());
623  }
624  } catch (incorrect_map_format_error &e) {
625  ERR_CF << "illegal map: " << e.message << '\n';
626  verified = false;
627  } catch(twml_exception& e) {
628  ERR_CF << "map could not be loaded: " << e.dev_message << '\n';
629  verified = false;
630  }
631  }
632 }
633 
634 // local_item is either an [era] or [modification] tag, something with addon_version and addon_id.
635 // (These are currently added at add-on loading time in the game_config_manager.)
636 // It is checked whether the local item's add-on version is required for this game, and if the
637 // versions are compatible. If it's not, a record is made in the req_list passed as argument.
638 static mp::ADDON_REQ check_addon_version_compatibility (const config & local_item, const config & game, std::vector<required_addon> & req_list)
639 {
640  if (local_item.has_attribute("addon_id") && local_item.has_attribute("addon_version")) {
641  if (const config & game_req = game.find_child("addon", "id", local_item["addon_id"])) {
642  // Record object which we will potentially store for this check
644  r.addon_id = local_item["addon_id"].str();
645 
646  const version_info local_ver(local_item["addon_version"].str());
647  version_info local_min_ver(local_item.has_attribute("addon_min_version") ? local_item["addon_min_version"] : local_item["addon_version"]);
648  // If UMC didn't specify last compatible version, assume no backwards compatibility.
649  if (local_min_ver > local_ver) {
650  // Some sanity checking regarding min version. If the min ver doens't make sense, ignore it.
651  local_min_ver = local_ver;
652  }
653 
654  const version_info remote_ver(game_req["version"].str());
655  version_info remote_min_ver(game_req.has_attribute("min_version") ? game_req["min_version"] : game_req["version"]);
656  if (remote_min_ver > remote_ver) {
657  remote_min_ver = remote_ver;
658  }
659 
660  // Check if the host is too out of date to play.
661  if (local_min_ver > remote_ver) {
663 
664  utils::string_map symbols;
665  symbols["addon"] = r.addon_id; // TODO: Figure out how to ask the add-on manager for the user-friendly name of this add-on.
666  symbols["host_ver"] = remote_ver.str();
667  symbols["local_ver"] = local_ver.str();
668  r.message = vgettext("Host's version of $addon is too old: host's version $host_ver < your version $local_ver.", symbols);
669  req_list.push_back(r);
670  return r.outcome;
671  }
672 
673  // Check if our version is too out of date to play.
674  if (remote_min_ver > local_ver) {
676 
677  utils::string_map symbols;
678  symbols["addon"] = r.addon_id; // TODO: Figure out how to ask the add-on manager for the user-friendly name of this add-on.
679  symbols["host_ver"] = remote_ver.str();
680  symbols["local_ver"] = local_ver.str();
681  r.message = vgettext("Your version of $addon is out of date: host's version $host_ver > your version $local_ver.", symbols);
682  req_list.push_back(r);
683  return r.outcome;
684  }
685  }
686  }
687 
688  return SATISFIED;
689 }
690 
691 // Check the era of the game, whether it is available, and compatible with installed content, and add info strings to this game_item.
693  if (!game["mp_era"].empty())
694  {
695  const config &era_cfg = game_config.find_child("era", "id", game["mp_era"]);
696  utils::string_map symbols;
697  symbols["era_id"] = game["mp_era"];
698  if (era_cfg) {
699  item.era_and_mod_info = _("Era:");
700  item.era_and_mod_info += " ";
701  item.era_and_mod_info += era_cfg["name"].str();
702 
704  item.addons_outcome = std::max(item.addons_outcome, result); //elevate to most severe error level encountered so far
705  } else {
706  if (!game["require_era"].to_bool(true)) {
707  item.have_era = true;
708  } else {
709  item.have_era = false;
710  }
711  item.era_and_mod_info = vgettext("Unknown era: $era_id", symbols);
712  verified = false;
713  }
714  } else {
715  item.era_and_mod_info = _("Unknown era");
716  verified = false;
717  }
718 }
719 
720 // Check the mods applied to the game, whether they are available / required, and compatible with installed content, and add info strings to this game_item.
722  (void) verified;
723  if (!game.child_or_empty("modification").empty()) {
724  item.have_all_mods = true;
725  item.era_and_mod_info += " — ";
726  item.era_and_mod_info += _("Modifications:");
727  item.era_and_mod_info += " ";
728 
729  for (const config& m : game.child_range("modification")) {
730  const config& mod_cfg = game_config.find_child("modification", "id", m["id"]);
731  if (mod_cfg) {
732  item.era_and_mod_info += mod_cfg["name"].str();
733  item.era_and_mod_info += ", ";
734 
736  item.addons_outcome = std::max(item.addons_outcome, result); //elevate to most severe error level encountered so far
737  } else {
738  item.era_and_mod_info += m["id"].str();
739  if (m["require_modification"].to_bool(false)) {
740  item.have_all_mods = false;
741  item.era_and_mod_info += _(" (missing)");
742  }
743  item.era_and_mod_info += ", ";
744  }
745  }
746  item.era_and_mod_info.erase(item.era_and_mod_info.size()-2, 2);
747  } else {
748  item.have_all_mods = true;
749  }
750 }
751 
752 // Do this before populating eras and mods, to get reports of missing add-ons in the most sensible order.
754 {
755  for (const config & addon : game.child_range("addon")) {
756  if (addon.has_attribute("id")) {
757  if (std::find(installed_addons.begin(), installed_addons.end(), addon["id"].str()) == installed_addons.end()) {
759  r.addon_id = addon["id"].str();
761 
762  utils::string_map symbols;
763  symbols["id"] = addon["id"].str();
764  r.message = vgettext("Missing addon: $id", symbols);
765  item.addons.push_back(r);
766  if (item.addons_outcome == SATISFIED) {
768  }
769  }
770  }
771  }
772 }
773 
774 // Build an appropriate game_item corresponding to this game (config) which we retrieved from [gamelist] from the server.
776 {
777  bool verified = true;
778  item.password_required = game["password"].to_bool();
779  item.reloaded = game["savegame"].to_bool();
780  item.have_era = true;
781  item.have_scenario = true;
782 
783  populate_game_item_addons_installed(item, game, installed_addons);
784  populate_game_item_campaign_or_scenario_info(item, game, game_config, verified);
785  populate_game_item_map_info(item, game, game_config, verified);
786  populate_game_item_era_info(item, game, game_config, verified);
787  populate_game_item_mod_info(item, game, game_config, verified);
788 
789  if (item.reloaded) {
790  item.map_info += " — ";
791  item.map_info += _("Reloaded game");
792  verified = false;
793  }
794  item.id = game["id"].str();
795  item.name = game["name"].str();
796  std::string turn = game["turn"];
797  std::string slots = game["slots"];
798  item.vacant_slots = lexical_cast_default<size_t>(slots, 0);
799  item.current_turn = 0;
800  if (!turn.empty()) {
801  item.started = true;
802  int index = turn.find_first_of('/');
803  if (index > -1){
804  const std::string current_turn = turn.substr(0, index);
805  item.current_turn = lexical_cast<unsigned int>(current_turn);
806  }
807  item.status = _("Turn ") + turn;
808  } else {
809  item.started = false;
810  if (item.vacant_slots > 0) {
811  item.status = std::string(_n("Vacant Slot:", "Vacant Slots:",
812  item.vacant_slots)) + " " + slots;
813  if (item.password_required) {
814  item.status += std::string(" (") + std::string(_("Password Required")) + ")";
815  }
816  }
817  }
818 
819  item.use_map_settings = game["mp_use_map_settings"].to_bool();
820  item.gold = game["mp_village_gold"].str();
821  if (game["mp_fog"].to_bool()) {
822  item.vision = _("Fog");
823  item.fog = true;
824  if (game["mp_shroud"].to_bool()) {
825  item.vision += "/";
826  item.vision += _("Shroud");
827  item.shroud = true;
828  } else {
829  item.shroud = false;
830  }
831  } else if (game["mp_shroud"].to_bool()) {
832  item.vision = _("Shroud");
833  item.fog = false;
834  item.shroud = true;
835  } else {
836  item.vision = _("none");
837  item.fog = false;
838  item.shroud = false;
839  }
840  if (game["mp_countdown"].to_bool()) {
841  item.time_limit = game["mp_countdown_init_time"].str() + " / +"
842  + game["mp_countdown_turn_bonus"].str() + " "
843  + game["mp_countdown_action_bonus"].str();
844  } else {
845  item.time_limit = "";
846  }
847  item.xp = game["experience_modifier"].str() + "%";
848  item.registered_users_only = game["registered_users_only"].to_bool(true);
849  item.observers = game["observer"].to_bool(true);
850  item.shuffle_sides = game["shuffle_sides"].to_bool(true);
851  item.verified = verified;
852 }
853 
854 void gamebrowser::set_game_items(const config& cfg, const config& game_config, const std::vector<std::string> & installed_addons)
855 {
856  //DBG_MP << "** gamelist **\n" << cfg.debug() << "****\n";
857 
858  const bool scrolled_to_max = (has_scrollbar() && get_position() == get_max_position());
859  const bool selection_visible = (selected_ >= visible_range_.first && selected_ <= visible_range_.second);
860  const std::string selected_game = (selected_ < games_.size()) ? games_[selected_].id : "";
861 
862  item_height_ = 100;
863 
864  games_.clear();
865 
866  for (const config &game : cfg.child("gamelist").child_range("game"))
867  {
868  games_.push_back(game_item());
869  populate_game_item(games_.back(), game, game_config, installed_addons);
870  // Hack...
872  {
874  !game_matches_filter(games_.back(), cfg))
875  {
876  games_.pop_back();
877  }
878  }
879  }
880  set_full_size(games_.size());
882 
883  // Try to preserve the game selection
884  if (!selected_game.empty()) {
885  for (unsigned int i=0; i < games_.size(); i++) {
886  if (games_[i].id == selected_game) {
887  selected_ = i;
888  break;
889  }
890  }
891  }
892  if(selected_ >= games_.size())
893  selected_ = std::max<long>(static_cast<long>(games_.size()) - 1, 0);
894 
895  if (scrolled_to_max) {
897  } else {
898  // Keep the selected game visible if it was visible before
899  if (selection_visible && (visible_range_.first > selected_
900  || visible_range_.second < selected_)) {
902  }
903  }
904  scroll(get_position());
905  set_dirty();
906 }
907 
909  if (id.empty()) return;
910 
911  for (unsigned int i=0; i < games_.size(); i++) {
912  if (games_[i].id == id) {
913  selected_ = i;
914  break;
915  }
916  }
918  set_dirty();
919 }
920 
922 {
924  return false;
925  }
927  bool found_friend = false;
928  for (const config &user : cfg.child_range("user")) {
929  if(preferences::is_friend(user["name"]) && user["game_id"] == i.id) {
930  found_friend = true;
931  break;
932  }
933  }
934  if(!found_friend) {
935  return false;
936  }
937  }
938 
939  if(!preferences::fi_text().empty()) {
940  bool found_match = true;
941  for (const std::string& search_string : utils::split(preferences::fi_text(), ' ', utils::STRIP_SPACES)) {
942 
943  if(!boost::contains(i.map_info, search_string, chars_equal_insensitive) &&
944  !boost::contains(i.name, search_string, chars_equal_insensitive) &&
946 
947  found_match = false;
948  break;
949  }
950  }
951  if(!found_match) {
952  return false;
953  }
954  }
955 
956  return true;
957 }
958 
960 {
961  set_alpha_sort(1);
962 }
963 
965 {
966  switch(column)
967  {
968  case MAP_COLUMN:
969  case STATUS_COLUMN:
970  return true;
971  default:
972  return basic_sorter::column_sortable(column);
973  }
974 }
975 
976 bool lobby::lobby_sorter::less(int column, const gui::menu::item& row1, const gui::menu::item& row2) const
977 {
978  const config &list = cfg_.child("gamelist");
979  if (!list) {
980  return false;
981  }
982 
983  size_t nb = list.child_count("game");
984  if(row1.id >= nb || row2.id >= nb) {
985  return false;
986  }
987 
988  config::const_child_iterator gi = list.child_range("game").first, gs = gi;
989  std::advance(gi, row1.id);
990  const config &game1 = *gi;
991  gi = gs;
992  std::advance(gi, row2.id);
993  const config &game2 = *gi;
994 
995  if(column == MAP_COLUMN) {
996  size_t mapsize1 = game1["map_data"].str().size();
997  if(mapsize1 == 0) {
998  mapsize1 = game1["map"].str().size();
999  }
1000 
1001  size_t mapsize2 = game2["map_data"].str().size();
1002  if(mapsize2 == 0) {
1003  mapsize2 = game2["map"].str().size();
1004  }
1005 
1006  return mapsize1 < mapsize2;
1007 
1008  } else if(column == STATUS_COLUMN) {
1009  int nslots1 = game1["slots"].to_int();
1010  int nslots2 = game2["slots"].to_int();
1011 
1012  int turn1 = game1["turn"].to_int();
1013  int turn2 = game2["turn"].to_int();
1014 
1015  if(nslots1 > nslots2) {
1016  return true;
1017  } else if(nslots1 < nslots2) {
1018  return false;
1019  } else {
1020  return turn1 < turn2;
1021  }
1022  }
1023 
1024  return basic_sorter::less(column,row1,row2);
1025 }
1026 
1027 lobby::lobby(CVideo& v, twesnothd_connection* wesnothd_connection, const config& cfg, chat& c, config& gamelist, const std::vector<std::string> & installed_addons) :
1028  mp::ui(v, wesnothd_connection, _("Game Lobby"), cfg, c, gamelist),
1029 
1030  current_turn(0),
1032  game_observers_(),
1033 
1034  observe_game_(video(), _("Observe Game")),
1035  join_game_(video(), _("Join Game")),
1036  create_game_(video(), _("Create Game")),
1037  replay_options_(video(), std::vector<std::string>()),
1038  game_preferences_(video(), _("Preferences")),
1039  quit_game_(video(), _("Quit")),
1040  apply_filter_(video(), _("Apply filter"), gui::button::TYPE_CHECK),
1041  invert_filter_(video(), _("Invert"), gui::button::TYPE_CHECK),
1042  vacant_slots_(video(), _("Vacant slots"), gui::button::TYPE_CHECK),
1043  friends_in_game_(video(), _("Friends in game"), gui::button::TYPE_CHECK),
1044  filter_label_(video(), _("Search:")),
1045  filter_text_(video(), 150),
1046  last_selected_game_(-1), sorter_(gamelist),
1047  games_menu_(video(),cfg.child("multiplayer_hashes")),
1048  minimaps_(),
1050  installed_addons_(installed_addons)
1051 {
1052  std::vector<std::string> replay_options_strings_;
1053  replay_options_strings_.push_back(_("Normal Replays"));
1054  replay_options_strings_.push_back(_("Quick Replays"));
1055  replay_options_strings_.push_back(_("Enter Blindfolded"));
1056 
1057  replay_options_.set_items(replay_options_strings_);
1058 
1059  std::string help_string1 = _("Skip quickly to the active turn when observing");
1060  std::string help_string2 = _("Do not show the map until given control of a side");
1061  replay_options_.set_help_string(help_string1 + " / " + help_string2);
1062 
1066  }
1067 
1070  }
1071 
1073  apply_filter_.set_help_string(_("Enable the games filter. If unchecked all games are shown, regardless of any filter."));
1074 
1076  invert_filter_.set_help_string(_("Show all games that do *not* match your filter. Useful for hiding games you are not interested in."));
1078 
1080  vacant_slots_.set_help_string(_("Only show games that have at least one vacant slot"));
1082 
1084  friends_in_game_.set_help_string(_("Only show games that are played or observed by at least one of your friends"));
1086 
1088 
1090  filter_text_.set_help_string(_("Only show games whose title, description, era or mods contain the entered text"));
1092 
1093  gamelist_updated();
1095 
1096  plugins_context_.reset(new plugins_context("Multiplayer Lobby"));
1097 
1098  //These structure initializers create a lobby::process_data_event
1099  plugins_context_->set_callback("join", std::bind(&lobby::plugin_event_helper, this, process_event_data (true, false, false, false)));
1100  plugins_context_->set_callback("observe", std::bind(&lobby::plugin_event_helper, this, process_event_data (false, true, false, false)));
1101  plugins_context_->set_callback("create", std::bind(&lobby::plugin_event_helper, this, process_event_data (false, false, true, false)));
1102  plugins_context_->set_callback("quit", std::bind(&lobby::plugin_event_helper, this, process_event_data (false, false, false, true)));
1103  plugins_context_->set_callback("chat", std::bind(&lobby::send_chat_message, this, std::bind(get_str, _1, "message"), false), true);
1104  plugins_context_->set_callback("select_game", std::bind(&gamebrowser::select_game, &(this->games_menu_), std::bind(get_str, _1, "id")), true);
1105 
1106  plugins_context_->set_accessor("game_list", std::bind(&lobby::gamelist, this));
1107  plugins_context_->set_accessor("game_config", std::bind(&lobby::game_config, this));
1108 }
1109 
1110 void lobby::hide_children(bool hide)
1111 {
1112  ui::hide_children(hide);
1113 
1114  games_menu_.hide(hide);
1115  observe_game_.hide(hide);
1116  join_game_.hide(hide);
1117  create_game_.hide(hide);
1118  replay_options_.hide(hide);
1119  game_preferences_.hide(hide);
1120  quit_game_.hide(hide);
1121  apply_filter_.hide(hide);
1122  invert_filter_.hide(hide);
1123  vacant_slots_.hide(hide);
1124  friends_in_game_.hide(hide);
1125  filter_label_.hide(hide);
1126  filter_text_.hide(hide);
1127 }
1128 
1129 void lobby::layout_children(const SDL_Rect& rect)
1130 {
1131  ui::layout_children(rect);
1132 
1133  int btn_space = 5;
1134  int xborder = 10;
1135  int yborder = 7;
1136 
1137  // Align to the left border
1138  join_game_.set_location(xscale(xborder), yscale(yborder));
1139  observe_game_.set_location(join_game_.location().x + join_game_.location().w + btn_space, yscale(yborder));
1140  create_game_.set_location(observe_game_.location().x + observe_game_.location().w + btn_space, yscale(yborder));
1141 
1142  // Align 'Quit' to the right border
1143  quit_game_.set_location(xscale(xscale_base - xborder) - quit_game_.location().w, yscale(yborder));
1144 
1145  // Align in the middle between the right and left buttons
1147  - replay_options_.location().w - game_preferences_.location().w - btn_space) / 2;
1148  if (space < btn_space) space = btn_space;
1151 
1155  - apply_filter_.location().h
1156  );
1157 
1164  + (apply_filter_.location().h - filter_label_.location().h) / 2);
1165 
1166 }
1167 
1168 void lobby::gamelist_updated(bool silent)
1169 {
1170  ui::gamelist_updated(silent);
1171  const config &list = gamelist().child("gamelist");
1172  if (!list) {
1173  // No gamelist yet. Do not update anything.
1174  return;
1175  }
1179 }
1180 
1182 {
1184  data.join = join_game_.pressed();
1185  data.observe = observe_game_.pressed();
1186  data.create = create_game_.pressed();
1187  data.quit = quit_game_.pressed();
1188 
1189  process_event_impl(data);
1190 }
1191 
1192 static void handle_addon_requirements_gui(CVideo & v, const std::vector<required_addon> & reqs, mp::ADDON_REQ addon_outcome)
1193 {
1194  if (addon_outcome == CANNOT_SATISFY) {
1195  std::string e_title = _("Incompatible user-made content.");
1196  std::string err_msg = _("This game cannot be joined because the host has out-of-date add-ons which are incompatible with your version. You might suggest to them that they update their add-ons.");
1197 
1198  err_msg +="\n\n";
1199  err_msg += _("Details:");
1200  err_msg += "\n";
1201 
1202  for (const required_addon & a : reqs) {
1203  if (a.outcome == CANNOT_SATISFY) {
1204  err_msg += a.message;
1205  err_msg += "\n";
1206  }
1207  }
1208  gui2::show_message(v, e_title, err_msg, gui2::tmessage::auto_close);
1209  } else if (addon_outcome == NEED_DOWNLOAD) {
1210  std::string e_title = _("Missing user-made content.");
1211  std::string err_msg = _("This game requires one or more user-made addons to be installed or updated in order to join. Do you want to try to install them?");
1212 
1213  err_msg +="\n\n";
1214  err_msg += _("Details:");
1215  err_msg += "\n";
1216 
1217  std::vector<std::string> needs_download;
1218  for (const required_addon & a : reqs) {
1219  if (a.outcome == NEED_DOWNLOAD) {
1220  err_msg += a.message;
1221  err_msg += "\n";
1222 
1223  needs_download.push_back(a.addon_id);
1224  } else if (a.outcome == CANNOT_SATISFY) {
1225  assert(false);
1226  }
1227  }
1228  assert(needs_download.size() > 0);
1229 
1231  ad_hoc_addon_fetch_session(v, needs_download);
1233  }
1234  }
1235 }
1236 
1237 // The return value should be true if the gui result was not chnaged
1239 {
1242 
1243  // check whehter to try to download addons
1244  if ((games_menu_.selected() || data.observe || data.join) && games_menu_.selection_needs_addons())
1245  {
1246  if (const std::vector<required_addon> * reqs = games_menu_.selection_addon_requirements()) {
1248  } else {
1249  gui2::show_error_message(video(), _("Something is wrong with the addon version check database supporting the multiplayer lobby, please report this at bugs.wesnoth.org."));
1250  }
1252  return ;
1253  }
1254 
1256  const bool join = (data.join || games_menu_.selected()) && games_menu_.selection_is_joinable();
1257 
1261 
1263 
1264  int selected_game = games_menu_.selection();
1265  if (selected_game != last_selected_game_) {
1266  if (games_menu_.empty()) {
1267  set_selected_game("");
1268  } else {
1270  }
1272  last_selected_game_ = selected_game;
1273  }
1274 
1275  if(selected_user_changed()) {
1278  }
1279 
1280  if(join || observe) {
1281  const int selected = games_menu_.selection();
1282  if(!games_menu_.empty() && selected >= 0) {
1284 
1286  if(join && game.password_required) {
1288  return;
1289  }
1290  }
1291 
1292  config response;
1293  config& join = response.add_child("join");
1294  join["id"] = game.id;
1295  join["observe"] = observe;
1296 
1297  if(!password.empty()) {
1298  join["password"] = password;
1299  }
1300  send_to_server(response);
1301 
1302  if(observe) {
1303  this->current_turn = game.current_turn;
1305  } else {
1306  set_result(JOIN);
1307  }
1308  }
1309  return;
1310  }
1311 
1312  if(data.create) {
1313  set_result(CREATE);
1314  return;
1315  }
1316 
1317  if(game_preferences_.pressed()) {
1319  return;
1320  }
1321 
1322  if(data.quit) {
1323  set_result(QUIT);
1324  return;
1325  }
1326 
1327  if(apply_filter_.pressed()) {
1333 
1334  /** @todo I am aware that the box is not grayed out even though
1335  it definitely should be. This is because the textbox
1336  class currently does not really have an easy way to do
1337  that. I'll have to look into this.
1338  */
1340  gamelist_updated();
1341  return;
1342  }
1343 
1344  if(invert_filter_.pressed()) {
1346  gamelist_updated();
1347  return;
1348  }
1349 
1350  if(vacant_slots_.pressed()) {
1352  gamelist_updated();
1353  return;
1354  }
1355 
1356  if(friends_in_game_.pressed()) {
1358  gamelist_updated();
1359  return;
1360  }
1361 
1362  if(search_string_ != filter_text_.text()) {
1365  gamelist_updated();
1366  }
1367 
1368 }
1369 
1371 {
1372  process_event_impl(data);
1373  return get_result() == mp::ui::CONTINUE;
1374 }
1375 
1377 {
1379 
1380  // Invalidate game selection for the player list
1381  last_selected_game_ = -1;
1382 }
1383 
1384 } // end namespace mp
1385 
basic_sorter & set_alpha_sort(int column)
Definition: menu.cpp:48
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
child_itors child_range(const std::string &key)
Definition: config.cpp:613
static lg::log_domain log_config("config")
surface getMinimap(int w, int h, const gamemap &map, const team *vw, const std::map< map_location, unsigned int > *reach_map)
function to create the minimap for a given map the surface returned must be freed by the user ...
Definition: minimap.cpp:43
void populate_game_item_campaign_or_scenario_info(game_item &, const config &, const config &, bool &)
image::locator time_limit_icon_locator_
void show_error_message(CVideo &video, const std::string &message, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:198
void set_check(bool check)
Definition: button.cpp:352
void set_filter_lobby(bool value)
gui::label filter_label_
bool selected_user_changed() const
virtual void process_event()
void set_shown_size(unsigned h)
Definition: scrollarea.cpp:110
GLvoid **typedef void(GLAPIENTRY *PFNGLGETVERTEXATTRIBDVPROC)(GLuint
Definition: glew.h:1806
void set_numeric_keypress_selection(bool value)
Definition: menu.cpp:761
image::locator shuffle_sides_icon_locator_
unsigned int row_height() const
void adjust_position(unsigned pos)
Definition: scrollarea.cpp:100
static lg::log_domain log_lobby("mp/lobby")
bool plugin_event_helper(const process_event_data &)
virtual void enable(bool new_val=true)
Definition: widget.cpp:204
std::string search_string_
const GLfloat * c
Definition: glew.h:12741
int pos
Definition: formula.cpp:800
#define ERR_CF
std::vector< bool > game_observers_
void set_inner_location(const SDL_Rect &rect)
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
boost::scoped_ptr< plugins_context > plugins_context_
void populate_game_item(game_item &, const config &, const config &, const std::vector< std::string > &)
Definition: video.hpp:58
std::vector< game_item > games_
void set_fi_vacant_slots(bool value)
virtual void enable(bool new_val=true)
Definition: button.cpp:386
void bg_register(SDL_Rect const &rect)
Definition: widget.cpp:109
gui::button friends_in_game_
ROW_TYPE
Definition: menu.hpp:32
std::vector< bool > game_vacant_slots_
virtual void layout_children(const SDL_Rect &rect)
Lays the children out.
result get_result()
Returns the result of the current widget.
This file contains the window object, this object is a top level container which has the event manage...
std::string lobby_music
Definition: game_config.cpp:68
Enables auto close.
Definition: message.hpp:65
attribute_map::value_type attribute
Definition: config.hpp:393
CVideo & video() const
Definition: widget.hpp:83
General purpose widgets.
bool has_scrollbar() const
Definition: scrollarea.cpp:35
std::vector< std::string > empty_string_vector
Definition: help_impl.cpp:75
void set_text(const std::string &text, const SDL_Color &color=font::NORMAL_COLOR)
Definition: textbox.cpp:76
void set_focus(bool focus)
Definition: widget.cpp:149
STL namespace.
void draw_row(const size_t row_index, const SDL_Rect &rect, ROW_TYPE type)
gui::button observe_game_
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
const std::string unicode_multiplication_sign
lobby_sorter sorter_
void set_fi_friends_in_game(bool value)
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Definition: video.cpp:290
const std::string text() const
Definition: textbox.cpp:69
Implements simple parsing of legacy GUI1 item markup.
Definition: old_markup.hpp:26
const int SIZE_PLUS
Definition: font.hpp:65
const config & child_or_empty(const std::string &key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:722
const std::vector< required_addon > * selection_addon_requirements() const
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
bool empty() const
Definition: config.cpp:1105
To lexical_cast(From value)
Lexical cast converts one type to another.
int selected() const
Definition: combo.cpp:38
bool selected() const
void set_fi_text(const std::string &search_string)
const SDL_Color NORMAL_COLOR
Definition: font.cpp:564
-file util.hpp
void process_event_impl(const process_event_data &)
game_item selected_game()
const int SIZE_NORMAL
Definition: font.hpp:58
const SDL_Color GOOD_COLOR
Definition: font.cpp:567
lobby_sorter(const config &cfg)
virtual void process_network_data(const config &data)
Processes any pending network data.
virtual void hide_children(bool hide=true)
Hides or shows all gui::widget children of this widget.
std::vector< required_addon > addons
void set_measurements(int w, int h)
Definition: widget.cpp:129
bool contains(const tpane::titem &item, const std::string &tag, const ttext_box &text_box)
A filter testing whether a search string is used in a text.
Definition: filter.hpp:66
This module controls the multiplayer lobby.
STRIP_SPACES : strips leading and trailing blank spaces.
bool selection_is_joinable() const
bool hidden() const
Definition: widget.cpp:198
const SDL_Color DISABLED_COLOR
Definition: font.cpp:576
void set_full_size(unsigned h)
Definition: scrollarea.cpp:117
void set_dirty(bool dirty=true)
Definition: widget.cpp:217
gui::button join_game_
void set_game_items(const config &cfg, const config &game_config, const std::vector< std::string > &installed_addons)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
bool chars_equal_insensitive(char a, char b)
Definition: util.hpp:206
GLuint64EXT * result
Definition: glew.h:10727
Dialog is closed with ok button.
Definition: window.hpp:125
std::map< std::string, t_string > string_map
bool is_friend(const std::string &nick)
A class that represents a TCP/IP connection to the wesnothd server.
image::locator xp_icon_locator_
result set_result(result res)
Sets the result of this dialog, to be checked by get_result().
void set_position(unsigned pos)
Definition: scrollarea.cpp:95
bool ad_hoc_addon_fetch_session(CVideo &v, const std::vector< std::string > &addon_ids)
Conducts an ad-hoc add-ons server connection to download an add-on with a particular id and all it's ...
SDL_Rect inner_location() const
Definition: scrollarea.cpp:138
const SDL_Rect * clip_rect() const
Definition: widget.cpp:104
const SDL_Color BAD_COLOR
Definition: font.cpp:568
bool focus(const SDL_Event *event)
Definition: widget.cpp:157
static const int xscale_base
const std::vector< std::string > & installed_addons_
void set_selected_user_changed(const bool &changed)
const config & game_config() const
Returns the main game config, as defined by loading the preprocessed WML files.
virtual void hide(bool value=true)
Definition: widget.cpp:162
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
const GLdouble * v
Definition: glew.h:1359
virtual void hide_children(bool hide=true)
Hides or shows all gui::widget children of this widget.
bool dirty() const
Definition: widget.cpp:227
int w() const
Effective map width.
Definition: map.hpp:105
std::string selected
Definition: game_config.cpp:84
void show_message(CVideo &video, const std::string &title, const std::string &message, const std::string &button_caption, const bool auto_close, const bool message_use_markup)
Shows a message to the user.
Definition: message.cpp:143
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
std::string dev_message
The message for developers telling which problem was triggered, this shouldn't be translated...
const gui::label & title() const
Encapsulates the map of the game.
Definition: map.hpp:37
static UNUSEDNOWARN std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:86
void set_fi_invert(bool value)
bool selection_is_observable() const
virtual void join()
Definition: events.cpp:173
config & add_child(const std::string &key)
Definition: config.cpp:743
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
Definition: manager.cpp:150
void send_to_server(const config &cfg) override
int yscale(int y) const
Modify, read and display user preferences.
unsigned int item_height_
void handle_event(const SDL_Event &event)
void scroll(unsigned int pos)
bool pressed()
Definition: button.cpp:770
gui::button create_game_
image::locator vision_icon_locator_
int selection() const
void set_blindfold_replay(bool value)
CVideo & video()
void set_message_private(bool value)
gui::button apply_filter_
const std::string & description() const
Definition: old_markup.hpp:55
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:47
std::string read_map(const std::string &name)
std::string str() const
Serializes the version number into string form.
Definition: version.cpp:90
gamebrowser games_menu_
gui::button quit_game_
void set_help_string(const std::string &str)
Definition: widget.cpp:314
unsigned get_position() const
Definition: scrollarea.cpp:85
bool less(int column, const gui::menu::item &row1, const gui::menu::item &row2) const
GLenum GLenum GLvoid GLvoid * column
Definition: glew.h:3805
SDL_Rect draw_text(surface &dst, const SDL_Rect &area, int size, const SDL_Color &color, const std::string &txt, int x, int y, bool use_tooltips, int style)
Function to draw text on a surface.
A class which implements an approximation of template typedef scoped_resource scoped_ptr;.
std::map< std::string, std::string > minimaps_
GLuint res
Definition: glew.h:9258
gui::button game_preferences_
void send_chat_message(const std::string &message, bool allies_only=false)
#define DOUBLE_CLICK_EVENT
Definition: events.hpp:23
gui::combo replay_options_
lobby(CVideo &v, twesnothd_connection *wesnothd_connection, const config &cfg, chat &c, config &gamelist, const std::vector< std::string > &installed_addons)
static void handle_addon_requirements_gui(CVideo &v, const std::vector< required_addon > &reqs, mp::ADDON_REQ addon_outcome)
image::locator map_size_icon_locator_
SDL_Rect client_area() const
int h() const
Effective map height.
Definition: map.hpp:108
void set_selected(int val)
Definition: combo.cpp:68
std::pair< size_t, size_t > visible_range_
const std::string space
whitespace is possible
Game configuration data as global variables.
Definition: build_info.cpp:38
const_attr_itors attribute_range() const
Definition: config.cpp:984
GLuint index
Definition: glew.h:1782
static bool execute(std::string &password, CVideo &video)
The execute function – see tdialog for more information.
ADDON_REQ selection_addon_outcome() const
config & gamelist()
Returns the current gamelist.
bool selection_needs_addons() const
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
size_t i
Definition: function.cpp:1057
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
void set_items(const std::vector< std::string > &items)
Definition: combo.cpp:52
Declarations for File-IO.
unsigned get_max_position() const
Definition: scrollarea.cpp:90
void set_skip_mp_replay(bool value)
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
gui::button vacant_slots_
const std::string & label() const
Definition: old_markup.hpp:50
void populate_game_item_era_info(game_item &, const config &, const config &, bool &)
unsigned child_count(const std::string &key) const
Definition: config.cpp:635
Represents version numbers.
Definition: version.hpp:44
virtual void hide(bool value=true)
Definition: scrollarea.cpp:78
size_t id
Definition: menu.hpp:131
void populate_game_item_map_info(game_item &, const config &, const config &, bool &)
int xscale(int x) const
std::string vgettext(const char *msgid, const utils::string_map &symbols)
std::string make_text_ellipsis(const std::string &text, int font_size, int max_width, int style)
If the text exceeds the specified max width, end it with an ellipsis (...)
Definition: font.cpp:996
static mp::ADDON_REQ check_addon_version_compatibility(const config &local_item, const config &game, std::vector< required_addon > &req_list)
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
Definition: rect.cpp:28
#define SDL_EVENTMASK(EVENT)
Definition: compat.hpp:39
gui::button invert_filter_
gamebrowser(CVideo &video, const config &map_hashes)
Contains the SDL_Rect helper code.
image::locator observer_icon_locator_
void bg_restore() const
Definition: widget.cpp:251
SDL_Rect get_item_rect(size_t index) const
bool has_attribute(const std::string &key) const
Definition: config.cpp:514
const GLdouble * m
Definition: glew.h:6968
image::locator gold_icon_locator_
bool find(E event, F functor)
Tests whether an event handler is available.
cl_event event
Definition: glew.h:3070
void set_selected_game(const std::string &game_name)
Sets the name of the selected game which is used to highlight the names of the players which have joi...
bool column_sortable(int column) const
virtual void process_network_data(const config &data)
Processes any pending network data.
void play_music_repeatedly(const std::string &id)
Definition: sound.cpp:536
image::locator no_observer_icon_locator_
config & child(const std::string &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:658
const SDL_Color GRAY_COLOR
Definition: font.cpp:565
bool checked() const
Definition: button.cpp:381
config & find_child(const std::string &key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:1010
Standard logging facilities (interface).
bool game_matches_filter(const game_item &i, const config &cfg)
const std::function< std::string(const config &, const std::string &) > get_str
Definition: context.cpp:121
std::string message
Definition: exceptions.hpp:29
virtual void set_location(SDL_Rect const &rect)
Definition: widget.cpp:85
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
#define e
this class memorizes a chat session.
const SDL_Color YELLOW_COLOR
Definition: font.cpp:570
SDL_Rect const & location() const
Definition: widget.cpp:144
void select_game(const std::string &id)
void populate_game_item_addons_installed(game_item &, const config &, const std::vector< std::string > &)
std::string get_selected_user_game()
const int ButtonVPadding
Definition: show_dialog.cpp:44
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
surface get_rendered_text(const std::string &str, int size, const SDL_Color &color, int style)
Definition: font.cpp:886
Helper class, don't construct this directly.
void update_rect(const SDL_Rect &)
Definition: dummy_video.cpp:27
Interfaces for manipulating version numbers of engine, add-ons, etc.
virtual void draw_row(menu &menu_ref, const size_t row_index, const SDL_Rect &rect, ROW_TYPE type)
Definition: menu.cpp:879
virtual void gamelist_updated(bool silent=true)
Called each time the gamelist_ variable is updated.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
const config & map_hashes_
Shows a yes and no button.
Definition: message.hpp:75
virtual void layout_children(const SDL_Rect &rect)
Lays the children out.
style * style_
Definition: menu.hpp:232
a base class for the different multiplayer base dialogs: game list, create game, wait game...
virtual void gamelist_updated(bool silent=true)
Called each time the gamelist_ variable is updated.
int height() const
Definition: widget.cpp:139
std::string fi_text()
void populate_game_item_mod_info(game_item &, const config &, const config &, bool &)
gui::textbox filter_text_