The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
multiplayer.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2007 - 2017 by David White <dave@whitevine.net>
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 "addon/manager.hpp" // for installed_addons
18 #include "formula/string_utils.hpp"
19 #include "game_config_manager.hpp"
23 #include "preferences/game.hpp"
24 #include "gettext.hpp"
25 #include "gui/dialogs/message.hpp"
33 #include "gui/widgets/settings.hpp"
34 #include "hash.hpp"
35 #include "log.hpp"
37 #include "settings.hpp"
38 #include "sound.hpp"
39 #include "statistics.hpp"
40 #include "video.hpp"
41 #include "wesnothd_connection.hpp"
42 #include "resources.hpp"
43 #include "replay.hpp"
44 
45 #include "utils/functional.hpp"
46 
47 static lg::log_domain log_mp("mp/main");
48 #define DBG_MP LOG_STREAM(debug, log_mp)
49 
50 /** Opens a new server connection and prompts the client for login credentials, if necessary. */
51 static wesnothd_connection_ptr open_connection(CVideo& video, const std::string& original_host)
52 {
53  DBG_MP << "opening connection" << std::endl;
54 
55  std::string h = original_host;
56  if(h.empty()) {
58  dlg.show(video);
59 
60  if(dlg.get_retval() != gui2::window::OK) {
61  return wesnothd_connection_ptr();
62  }
63 
65  }
66 
68 
69  const int colon_index = h.find_first_of(":");
70  std::string host;
71  unsigned int port;
72 
73  if(colon_index == -1) {
74  host = h;
75  port = 15000;
76  } else {
77  host = h.substr(0, colon_index);
78  port = lexical_cast_default<unsigned int>(h.substr(colon_index + 1), 15000);
79  }
80 
81  // shown_hosts is used to prevent the client being locked in a redirect loop.
82  typedef std::pair<std::string, int> hostpair;
83  std::set<hostpair> shown_hosts;
84  shown_hosts.insert(hostpair(host, port));
85 
86  config data;
87  sock = gui2::dialogs::network_transmission::wesnothd_connect_dialog(video, "connect to server", host, port);
88  do {
89  if(!sock) {
90  return sock;
91  }
92 
93  data.clear();
95  //mp::check_response(data_res, data);
96 
97  if(data.has_child("reject") || data.has_attribute("version")) {
99  if(const config& reject = data.child("reject")) {
100  version = reject["accepted_versions"].str();
101  } else {
102  // Backwards-compatibility "version" attribute
103  version = data["version"].str();
104  }
105 
106  utils::string_map i18n_symbols;
107  i18n_symbols["required_version"] = version;
108  i18n_symbols["your_version"] = game_config::version;
109  const std::string errorstring = vgettext("The server accepts versions '$required_version', but you are using version '$your_version'", i18n_symbols);
110  throw wesnothd_error(errorstring);
111  }
112 
113  // Check for "redirect" messages
114  if(const config& redirect = data.child("redirect"))
115  {
116  host = redirect["host"].str();
117  port =redirect["port"].to_int(15000);
118 
119  if(shown_hosts.find(hostpair(host,port)) != shown_hosts.end()) {
120  throw wesnothd_error(_("Server-side redirect loop"));
121  }
122  shown_hosts.insert(hostpair(host, port));
123  sock = wesnothd_connection_ptr();
124  sock = gui2::dialogs::network_transmission::wesnothd_connect_dialog(video, "redirect", host, port);
125  continue;
126  }
127 
128  if(data.child("version")) {
129  config cfg;
130  config res;
131  cfg["version"] = game_config::version;
132  res.add_child("version", cfg);
133  sock->send_data(res);
134  }
135 
136  // Continue if we did not get a direction to login
137  if(!data.child("mustlogin")) {
138  continue;
139  }
140 
141  // Enter login loop
142  for(;;) {
143  std::string password_reminder = "";
144 
146 
147  config response ;
148  config& sp = response.add_child("login") ;
149  sp["username"] = login ;
150 
151  // Login and enable selective pings -- saves server bandwidth
152  // If ping_timeout has a non-zero value, do not enable
153  // selective pings as this will cause clients to falsely
154  // believe the server has died and disconnect.
156  // Pings required so disable selective pings
157  sp["selective_ping"] = false;
158  } else {
159  // Client is bandwidth friendly so allow
160  // server to optimize ping frequency as needed.
161  sp["selective_ping"] = true;
162  }
163 
164  sock->send_data(response);
165  gui2::dialogs::network_transmission::wesnothd_receive_dialog(video, "login response", data, *sock);
166  config* warning = &data.child("warning");
167 
168  if(*warning) {
169  std::string warning_msg;
170 
171  if((*warning)["warning_code"] == MP_NAME_INACTIVE_WARNING) {
172  warning_msg = vgettext("The nickname ‘$nick’ is inactive. "
173  "You cannot claim ownership of this nickname until you "
174  "activate your account via email or ask an "
175  "administrator to do it for you.", {{"nick", login}});
176  } else {
177  warning_msg = (*warning)["message"].str();
178  }
179 
180  warning_msg += "\n\n";
181  warning_msg += _("Do you want to continue?");
182 
183  if(gui2::show_message(video, _("Warning"), warning_msg, gui2::dialogs::message::yes_no_buttons) != gui2::window::OK) {
184  return wesnothd_connection_ptr();
185  }
186  }
187 
188  config* error = &data.child("error");
189 
190  // ... and get us out of here if the server did not complain
191  if(!*error) break;
192 
193  do {
195 
196  bool fall_through = (*error)["force_confirmation"].to_bool() ?
197  (gui2::show_message(video, _("Confirm"), (*error)["message"], gui2::dialogs::message::ok_cancel_buttons) == gui2::window::CANCEL) :
198  false;
199 
200  const bool is_pw_request = !((*error)["password_request"].empty()) && !(password.empty());
201 
202  // If the server asks for a password, provide one if we can
203  // or request a password reminder.
204  // Otherwise or if the user pressed 'cancel' in the confirmation dialog
205  // above go directly to the username/password dialog
206  if((is_pw_request || !password_reminder.empty()) && !fall_through) {
207  if(is_pw_request) {
208  if((*error)["phpbb_encryption"].to_bool()) {
209  // Apparently HTML key-characters are passed to the hashing functions of phpbb in this escaped form.
210  // I will do closer investigations on this, for now let's just hope these are all of them.
211 
212  // Note: we must obviously replace '&' first, I wasted some time before I figured that out... :)
213  for(std::string::size_type pos = 0; (pos = password.find('&', pos)) != std::string::npos; ++pos)
214  password.replace(pos, 1, "&amp;");
215  for(std::string::size_type pos = 0; (pos = password.find('\"', pos)) != std::string::npos; ++pos)
216  password.replace(pos, 1, "&quot;");
217  for(std::string::size_type pos = 0; (pos = password.find('<', pos)) != std::string::npos; ++pos)
218  password.replace(pos, 1, "&lt;");
219  for(std::string::size_type pos = 0; (pos = password.find('>', pos)) != std::string::npos; ++pos)
220  password.replace(pos, 1, "&gt;");
221 
222  const std::string salt = (*error)["salt"];
223 
224  if(salt.length() < 12) {
225  throw wesnothd_error(_("Bad data received from server"));
226  }
227 
228  sp["password"] = utils::md5(utils::md5(password, utils::md5::get_salt(salt),
229  utils::md5::get_iteration_count(salt)).base64_digest(), salt.substr(12, 8)).base64_digest();
230 
231  } else {
232  sp["password"] = password;
233  }
234  }
235 
236  sp["password_reminder"] = password_reminder;
237 
238  // Once again send our request...
239  sock->send_data(response);
240  gui2::dialogs::network_transmission::wesnothd_receive_dialog(video, "login response", data, *sock);
241 
242  error = &data.child("error");
243 
244  // ... and get us out of here if the server is happy now
245  if(!*error) break;
246  }
247 
248  password_reminder = "";
249 
250  // Providing a password either was not attempted because we did not
251  // have any or failed:
252  // Now show a dialog that displays the error and allows to
253  // enter a new user name and/or password
254 
255  std::string error_message;
256  utils::string_map i18n_symbols;
257  i18n_symbols["nick"] = login;
258 
259  if((*error)["error_code"] == MP_MUST_LOGIN) {
260  error_message = _("You must login first.");
261  } else if((*error)["error_code"] == MP_NAME_TAKEN_ERROR) {
262  error_message = vgettext("The nickname ‘$nick’ is already taken.", i18n_symbols);
263  } else if((*error)["error_code"] == MP_INVALID_CHARS_IN_NAME_ERROR) {
264  error_message = vgettext("The nickname ‘$nick’ contains invalid "
265  "characters. Only alpha-numeric characters, underscores and "
266  "hyphens are allowed.", i18n_symbols);
267  } else if((*error)["error_code"] == MP_NAME_TOO_LONG_ERROR) {
268  error_message = vgettext("The nickname ‘$nick’ is too long. Nicks must "
269  "be 20 characters or less.", i18n_symbols);
270  } else if((*error)["error_code"] == MP_NAME_RESERVED_ERROR) {
271  error_message = vgettext("The nickname ‘$nick’ is reserved and cannot be used by players.", i18n_symbols);
272  } else if((*error)["error_code"] == MP_NAME_UNREGISTERED_ERROR) {
273  error_message = vgettext("The nickname ‘$nick’ is not registered on this server.", i18n_symbols)
274  + _(" This server disallows unregistered nicknames.");
275  } else if((*error)["error_code"] == MP_PASSWORD_REQUEST) {
276  error_message = vgettext("The nickname ‘$nick’ is registered on this server.", i18n_symbols);
277  } else if((*error)["error_code"] == MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME) {
278  error_message = vgettext("The nickname ‘$nick’ is registered on this server.", i18n_symbols)
279  + "\n\n" + _("WARNING: There is already a client using this nickname, "
280  "logging in will cause that client to be kicked!");
281  } else if((*error)["error_code"] == MP_NO_SEED_ERROR) {
282  error_message = _("Error in the login procedure (the server had no "
283  "seed for your connection).");
284  } else if((*error)["error_code"] == MP_INCORRECT_PASSWORD_ERROR) {
285  error_message = _("The password you provided was incorrect.");
286  } else if((*error)["error_code"] == MP_TOO_MANY_ATTEMPTS_ERROR) {
287  error_message = _("You have made too many login attempts.");
288  } else {
289  error_message = (*error)["message"].str();
290  }
291 
292  gui2::dialogs::mp_login dlg(host, error_message, !((*error)["password_request"].empty()));
293  dlg.show(video);
294 
295  switch(dlg.get_retval()) {
296  //Log in with password
297  case gui2::window::OK:
298  break;
299  //Request a password reminder
300  case 1:
301  password_reminder = "yes";
302  break;
303  // Cancel
304  default:
305  return wesnothd_connection_ptr();
306  }
307 
308  // If we have got a new username we have to start all over again
309  } while(login == preferences::login());
310 
311  // Somewhat hacky...
312  // If we broke out of the do-while loop above error
313  // is still going to be nullptr
314  if(!*error) break;
315  } // end login loop
316  } while(!(data.child("join_lobby") || data.child("join_game")));
317 
318  if(h != preferences::server_list().front().address) {
320  }
321 
322  if(data.child("join_lobby")) {
323  return sock;
324  }
325 
326  return wesnothd_connection_ptr();
327 }
328 
329 /** Helper struct to manage the MP workflow arguments. */
331 {
333  : video(video)
334  , game_config(gc)
335  , state(state)
336  , connection(connection)
337  , lobby_info(li)
338  {}
339 
341 
343 
345 
347 
349 };
350 
351 using mp_workflow_helper_ptr = std::shared_ptr<mp_workflow_helper>;
352 
353 /**
354  * The main components of the MP workflow. It consists of four screens:
355  *
356  * Host POV: LOBBY <---> CREATE GAME ---> STAGING ------------------> GAME BEGINS
357  * Player POV: LOBBY <---------------------------------> JOIN GAME ---> GAME BEGINS
358  *
359  * NOTE: since these functions are static, they appear here in the opposite order they'd be accessed.
360  */
361 static void enter_wait_mode(mp_workflow_helper_ptr helper, int game_id, bool observe)
362 {
363  DBG_MP << "entering wait mode" << std::endl;
364 
365  // The connection should never be null here, since one should never reach this screen in local game mode.
366  assert(helper->connection);
367 
369 
370  std::unique_ptr<mp_campaign_info> campaign_info(new mp_campaign_info(*helper->connection));
371  campaign_info->is_host = false;
372 
373  if(helper->lobby_info->get_game_by_id(game_id)) {
374  campaign_info->current_turn = helper->lobby_info->get_game_by_id(game_id)->current_turn;
375  }
376 
378  campaign_info->skip_replay = true;
380  }
381 
382  bool dlg_ok = false;
383  {
384  gui2::dialogs::mp_join_game dlg(helper->state, *helper->lobby_info, *helper->connection, true, observe);
385 
386  if(!dlg.fetch_game_config(helper->video)) {
387  return;
388  }
389 
390  dlg.show(helper->video);
391  dlg_ok = dlg.get_retval() == gui2::window::OK;
392  }
393 
394  if(dlg_ok) {
395  campaign_controller controller(helper->video, helper->state, helper->game_config, game_config_manager::get()->terrain_types());
396  controller.set_mp_info(campaign_info.get());
397  controller.play_game();
398  }
399 
400  helper->connection->send_data(config("leave_game"));
401 }
402 
404 {
405  DBG_MP << "entering connect mode" << std::endl;
406 
407  std::unique_ptr<mp_campaign_info> campaign_info;
408 
409  // If we have a connection, set the appropriate info. No connection means we're in local game mode.
410  if(helper->connection) {
411  campaign_info.reset(new mp_campaign_info(*helper->connection));
412  campaign_info->connected_players.insert(preferences::login());
413  campaign_info->is_host = true;
414  }
415 
416  bool dlg_ok = false;
417  {
418  ng::connect_engine_ptr connect_engine(new ng::connect_engine(helper->state, true, campaign_info.get()));
419 
420  gui2::dialogs::mp_staging dlg(*connect_engine, *helper->lobby_info, helper->connection);
421  dlg.show(helper->video);
422  dlg_ok = dlg.get_retval() == gui2::window::OK;
423  } // end connect_engine_ptr, dlg scope
424 
425  if(dlg_ok) {
426  campaign_controller controller(helper->video, helper->state, helper->game_config, game_config_manager::get()->terrain_types());
427  controller.set_mp_info(campaign_info.get());
428  controller.play_game();
429  }
430 
431  if(helper->connection) {
432  helper->connection->send_data(config("leave_game"));
433  }
434 }
435 
437 {
438  DBG_MP << "entering create mode" << std::endl;
439 
440  bool dlg_cancel = false;
441  {
442  ng::create_engine create_eng(helper->video, helper->state);
443 
444  gui2::dialogs::mp_create_game dlg(helper->game_config, create_eng);
445  dlg.show(helper->video);
446  dlg_cancel = dlg.get_retval() == gui2::window::CANCEL;
447  }
448 
449  if(!dlg_cancel) {
450  enter_staging_mode(helper);
451  } else if(helper->connection) {
452  helper->connection->send_data(config("refresh_lobby"));
453  }
454 }
455 
456 static bool enter_lobby_mode(mp_workflow_helper_ptr helper, const std::vector<std::string>& installed_addons)
457 {
458  DBG_MP << "entering lobby mode" << std::endl;
459 
460  // Connection should never be null in the lobby.
461  assert(helper->connection);
462 
463  // We use a loop here to allow returning to the lobby if you, say, cancel game creation.
464  while(true) {
465  if(const config& cfg = helper->game_config.child("lobby_music")) {
466  for(const config& i : cfg.child_range("music")) {
468  }
469 
471  } else {
474  }
475 
476  mp::lobby_info li(helper->game_config, installed_addons);
477  helper->lobby_info = &li;
478 
479  int dlg_retval = 0;
480  int dlg_joined_game_id = 0;
481  {
482 
483  gui2::dialogs::mp_lobby dlg(helper->game_config, li, *helper->connection);
484  dlg.show(helper->video);
485  dlg_retval = dlg.get_retval();
486  dlg_joined_game_id = dlg.get_joined_game_id();
487  }
488 
489  switch(dlg_retval) {
491  try {
492  enter_create_mode(helper);
493  } catch(config::error& error) {
494  if(!error.message.empty()) {
495  gui2::show_error_message(helper->video, error.message);
496  }
497 
498  // Update lobby content
499  helper->connection->send_data(config("refresh_lobby"));
500  }
501 
502  break;
505  try {
506  enter_wait_mode(helper,
507  dlg_joined_game_id,
509  );
510  } catch(config::error& error) {
511  if(!error.message.empty()) {
512  gui2::show_error_message(helper->video, error.message);
513  }
514 
515  // Update lobby content
516  helper->connection->send_data(config("refresh_lobby"));
517  }
518 
519  break;
521  // Let this function's caller reload the config and re-call.
522  return false;
523  default:
524  // Needed to handle the Quit signal and exit the loop
525  return true;
526  }
527  }
528 
529  return true;
530 }
531 
532 /** Pubic entry points for the MP workflow */
533 namespace mp {
534 
535 void start_client(CVideo& video, const config& game_config, saved_game& state, const std::string& host)
536 {
537  const config* game_config_ptr = &game_config;
538 
539  // This function does not refer to an addon database, it calls filesystem functions.
540  // For the sanity of the mp lobby, this list should be fixed for the entire lobby session,
541  // even if the user changes the contents of the addon directory in the meantime.
542  std::vector<std::string> installed_addons = ::installed_addons();
543 
544  DBG_MP << "starting client" << std::endl;
545 
547 
548  wesnothd_connection_ptr connection = open_connection(video, host);
549  if(!connection) {
550  return;
551  }
552 
553  mp_workflow_helper_ptr workflow_helper;
554  bool re_enter = false;
555 
556  do {
557  workflow_helper.reset(new mp_workflow_helper(video, *game_config_ptr, state, connection.get(), nullptr));
558 
559  // A return of false means a config reload was requested, so do that and then loop.
560  re_enter = !enter_lobby_mode(workflow_helper, installed_addons);
561 
562  if(re_enter) {
565  gcm->load_game_config_for_game(state.classification()); // NOTE: Using reload_changed_game_config only doesn't seem to work here
566 
567  game_config_ptr = &gcm->game_config();
568 
569  installed_addons = ::installed_addons(); // Refresh the installed add-on list for this session.
570 
571  connection->send_data(config("refresh_lobby"));
572  }
573  } while(re_enter);
574 }
575 
577 {
578  lobby_info li(game_config, {});
579 
580  gui2::dialogs::mp_staging dlg(engine, li, connection);
581  return dlg.show(video);
582 }
583 
584 bool goto_mp_wait(CVideo& video, saved_game& state, const config& game_config, wesnothd_connection* connection, bool observe)
585 {
586  lobby_info li(game_config, std::vector<std::string>());
587 
588  gui2::dialogs::mp_join_game dlg(state, li, *connection, false, observe);
589 
590  if(!dlg.fetch_game_config(video)) {
591  return false;
592  }
593 
594  if(dlg.started()) {
595  return true;
596  }
597 
598  return dlg.show(video);
599 }
600 
602 {
603  DBG_MP << "starting local game" << std::endl;
604 
606 
607  // TODO: should lobby_info take a nullptr in this case, or should we pass the installed_addons data here too?
608  lobby_info li(game_config, {});
609  mp_workflow_helper_ptr workflow_helper = std::make_shared<mp_workflow_helper>(video, game_config, state, nullptr, &li);
610 
611  enter_create_mode(workflow_helper);
612 }
613 
614 void start_local_game_commandline(CVideo& video, const config& game_config, saved_game& state, const commandline_options& cmdline_opts)
615 {
616  DBG_MP << "starting local MP game from commandline" << std::endl;
617 
618  // The setup is done equivalently to lobby MP games using as much of existing
619  // code as possible. This means that some things are set up that are not
620  // needed in commandline mode, but they are required by the functions called.
622 
623  DBG_MP << "entering create mode" << std::endl;
624 
625  // Set the default parameters
626  state.clear(); // This creates these parameters with default values defined in mp_game_settings.cpp
627  mp_game_settings& parameters = state.mp_settings();
628 
629  // Hardcoded default values
630  parameters.mp_era = "era_default";
631  parameters.name = "multiplayer_The_Freelands";
632 
633  // Default values for which at getter function exists
634  parameters.num_turns = settings::get_turns("");
635  parameters.village_gold = settings::get_village_gold("");
637  parameters.xp_modifier = settings::get_xp_modifier("");
638 
639  // Do not use map settings if --ignore-map-settings commandline option is set
640  if(cmdline_opts.multiplayer_ignore_map_settings) {
641  DBG_MP << "ignoring map settings" << std::endl;
642  parameters.use_map_settings = false;
643  } else {
644  parameters.use_map_settings = true;
645  }
646 
647  // None of the other parameters need to be set, as their creation values above are good enough for CL mode.
648  // In particular, we do not want to use the preferences values.
649 
650  state.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
651 
652  // [era] define.
653  if(cmdline_opts.multiplayer_era) {
654  parameters.mp_era = *cmdline_opts.multiplayer_era;
655  }
656 
657  if(const config& cfg_era = game_config.find_child("era", "id", parameters.mp_era)) {
658  state.classification().era_define = cfg_era["define"].str();
659  } else {
660  std::cerr << "Could not find era '" << parameters.mp_era << "'\n";
661  return;
662  }
663 
664  // [multiplayer] define.
665  if(cmdline_opts.multiplayer_scenario) {
666  parameters.name = *cmdline_opts.multiplayer_scenario;
667  }
668 
669  if(const config& cfg_multiplayer = game_config.find_child("multiplayer", "id", parameters.name)) {
670  state.classification().scenario_define = cfg_multiplayer["define"].str();
671  } else {
672  std::cerr << "Could not find [multiplayer] '" << parameters.name << "'\n";
673  return;
674  }
675 
678  config {"next_scenario", parameters.name}
679  );
680 
681  state.expand_random_scenario();
682  state.expand_mp_events();
683  state.expand_mp_options();
684 
685  // Should number of turns be determined from scenario data?
686  if(parameters.use_map_settings && state.get_starting_pos()["turns"]) {
687  DBG_MP << "setting turns from scenario data: " << state.get_starting_pos()["turns"] << std::endl;
688  parameters.num_turns = state.get_starting_pos()["turns"];
689  }
690 
691  DBG_MP << "entering connect mode" << std::endl;
692 
694 
695  {
696  ng::connect_engine_ptr connect_engine(new ng::connect_engine(state, true, nullptr));
697 
698  // Update the parameters to reflect game start conditions
699  connect_engine->start_game_commandline(cmdline_opts);
700  }
701 
702  if(resources::recorder && cmdline_opts.multiplayer_label) {
703  std::string label = *cmdline_opts.multiplayer_label;
704  resources::recorder->add_log_data("ai_log","ai_label",label);
705  }
706 
707  unsigned int repeat = (cmdline_opts.multiplayer_repeat) ? *cmdline_opts.multiplayer_repeat : 1;
708  for(unsigned int i = 0; i < repeat; i++){
709  saved_game state_copy(state);
710  campaign_controller controller(video, state_copy, game_config, game_config_manager::get()->terrain_types());
711  controller.play_game();
712  }
713 }
714 
715 } // end namespace mp
void empty_playlist()
Definition: sound.cpp:564
void send_data(const configr_of &request)
An error occured during when trying to coommunicate with the wesnothd server.
void show_error_message(CVideo &video, const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:207
Dialog is closed with ok button.
Definition: window.hpp:111
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:352
static bool wesnothd_receive_dialog(CVideo &video, const std::string &msg, config &cfg, wesnothd_connection &connection)
std::vector< char_t > string
static void enter_create_mode(mp_workflow_helper_ptr helper)
LEVEL_RESULT play_game()
bool skip_replay_blindfolded
void stop_music()
Definition: sound.cpp:509
std::map< std::string, t_string > string_map
unsigned int get_ping_timeout()
Definition: game.cpp:401
void start_client(CVideo &video, const config &game_config, saved_game &state, const std::string &host)
Starts a multiplayer game in client mode.
config & find_child(config_key_type 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:715
void add_log_data(const std::string &key, const std::string &var)
Definition: replay.cpp:301
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
#define MP_TOO_MANY_ATTEMPTS_ERROR
std::set< std::string > connected_players
players and observers
Definition: video.hpp:29
Shows an ok and cancel button.
Definition: message.hpp:75
std::string network_host()
Definition: game.cpp:386
boost::optional< std::string > multiplayer_scenario
Non-empty if –scenario was given on the command line. Dependent on –multiplayer.
void expand_random_scenario()
takes care of generate_map=, generate_scenario=, map= attributes This should be called before expandi...
Definition: saved_game.cpp:401
This class represents the collective information the client has about the players and games on the se...
Definition: lobby_info.hpp:28
#define MP_NO_SEED_ERROR
bool multiplayer_ignore_map_settings
True if –ignore-map-settings was given at the command line. Do not use map settings.
bool goto_mp_connect(CVideo &video, ng::connect_engine &engine, const config &game_config, wesnothd_connection *connection)
Opens mp::connect screen and sets game state according to the changes made.
mp::lobby_info * lobby_info
int get_village_gold(const std::string &value, const game_classification *classification)
Gets the village gold.
Definition: settings.cpp:40
void fresh_stats()
Definition: statistics.cpp:640
void clear()
Definition: saved_game.cpp:689
#define MP_NAME_TOO_LONG_ERROR
Helper struct to manage the MP workflow arguments.
Replay control code.
#define h
std::shared_ptr< mp_workflow_helper > mp_workflow_helper_ptr
void set_network_host(const std::string &host)
Definition: game.cpp:396
void clear()
Definition: config.cpp:742
bool goto_mp_wait(CVideo &video, saved_game &state, const config &game_config, wesnothd_connection *connection, bool observe)
Opens mp::wait screen and sets game state according to the changes made.
Define the errors the server may send during the login procedure.
boost::optional< std::string > multiplayer_era
Non-empty if –era was given on the command line. Dependent on –multiplayer.
-file util.hpp
boost::optional< std::string > multiplayer_label
Non-empty if –label was given on the command line. Dependent on –multiplayer.
static wesnothd_connection_ptr wesnothd_connect_dialog(CVideo &video, const std::string &msg, const std::string &hostname, int port)
#define MP_INCORRECT_PASSWORD_ERROR
int get_village_support(const std::string &value)
Gets the village unit level support.
Definition: settings.cpp:45
Pubic entry points for the MP workflow.
Definition: lobby_data.cpp:48
static game_config_manager * get()
static bool enter_lobby_mode(mp_workflow_helper_ptr helper, const std::vector< std::string > &installed_addons)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
#define MP_MUST_LOGIN
#define MP_NAME_UNREGISTERED_ERROR
bool fetch_game_config(CVideo &video)
FIXME: We shouldn't need to pass a CVideo argument here.
This file contains the settings handling of the widget library.
void expand_mp_options()
adds values of [option]s into [carryover_sides_start][variables] so that they are applied in the next...
Definition: saved_game.cpp:360
bool blindfold_replay()
Definition: game.cpp:606
A class that represents a TCP/IP connection to the wesnothd server.
const std::vector< game_config::server_info > & server_list()
Definition: game.cpp:369
unsigned current_turn
static void enter_staging_mode(mp_workflow_helper_ptr helper)
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
Definition: manager.cpp:148
Shows a yes and no button.
Definition: message.hpp:79
replay * recorder
Definition: resources.cpp:29
Used to reset is_authenticated flag after disconnecting.
Definition: game.hpp:51
config & get_starting_pos()
Definition: saved_game.cpp:488
void set_message_private(bool value)
Definition: game.cpp:857
static wesnothd_connection_ptr open_connection(CVideo &video, const std::string &original_host)
Opens a new server connection and prompts the client for login credentials, if necessary.
Definition: multiplayer.cpp:51
std::string era_define
If there is a define the era uses to customize data.
static std::string get_salt(const std::string &hash)
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:345
std::string login()
bool has_attribute(config_key_type key) const
Definition: config.cpp:196
#define DBG_MP
Definition: multiplayer.cpp:48
void show_message(CVideo &video, const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
int get_joined_game_id() const
Definition: lobby.hpp:79
Game configuration data as global variables.
Definition: build_info.cpp:53
const config & game_config() const
std::string scenario_define
If there is a define the scenario uses to customize data.
std::string password(const std::string &server, const std::string &login)
int get_xp_modifier(const std::string &value)
Gets the xp modifier.
Definition: settings.cpp:50
#define i
bool show(CVideo &video, const unsigned auto_close_time=0)
Shows the window.
void expand_mp_events()
adds [event]s from [era] and [modification] into this scenario does NOT expand [option]s because vari...
Definition: saved_game.cpp:316
const config & game_config
#define MP_PASSWORD_REQUEST
bool skip_mp_replay()
Definition: game.cpp:596
static lg::log_domain log_mp("mp/main")
#define MP_NAME_INACTIVE_WARNING
config & add_child(config_key_type key)
Definition: config.cpp:408
-file mapgen.hpp
std::string vgettext(const char *msgid, const utils::string_map &symbols)
#define MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME
#define MP_NAME_TAKEN_ERROR
void load_game_config_for_game(const game_classification &classification)
void start_local_game_commandline(CVideo &video, const config &game_config, saved_game &state, const commandline_options &cmdline_opts)
Starts a multiplayer game in single-user mode.
static void enter_wait_mode(mp_workflow_helper_ptr helper, int game_id, bool observe)
The main components of the MP workflow.
boost::optional< unsigned int > multiplayer_repeat
Repeats specified by –multiplayer-repeat option. Repeats a multiplayer game after it is finished...
game_classification & classification()
Definition: saved_game.hpp:55
void set_carryover_sides_start(config carryover_sides_start)
Definition: saved_game.cpp:124
wesnothd_connection * connection
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:31
wesnothd_connection * get() const
void play_music_config(const config &music_node, int i)
Definition: sound.cpp:656
#define MP_NAME_RESERVED_ERROR
const std::unique_ptr< connect_engine > connect_engine_ptr
void commit_music_changes()
Definition: sound.cpp:773
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:59
saved_game & state
int get_turns(const std::string &value)
Gets the number of turns.
Definition: settings.cpp:28
const ter_data_cache & terrain_types() const
const std::string version
Definition: game_config.cpp:39
Dialog is closed with the cancel button.
Definition: window.hpp:112
static int get_iteration_count(const std::string &hash)
mp_workflow_helper(CVideo &video, const config &gc, saved_game &state, wesnothd_connection *connection, mp::lobby_info *li)
#define MP_INVALID_CHARS_IN_NAME_ERROR
void start_local_game(CVideo &video, const config &game_config, saved_game &state)
Starts a multiplayer game in single-user mode.