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