50 #define DBG_MP LOG_STREAM(debug, log_mp)
51 #define LOG_MP LOG_STREAM(info, log_mp)
52 #define WRN_MP LOG_STREAM(warn, log_mp)
53 #define ERR_MP LOG_STREAM(err, log_mp)
60 class mp_manager* manager =
nullptr;
71 mp_manager(
const std::optional<std::string> host);
78 if(network_worker.joinable()) {
80 network_worker.join();
90 void run_lobby_loop();
94 bool post_scenario_wait(
bool observe);
98 struct session_metadata
100 session_metadata() =
default;
102 session_metadata(
const config& cfg)
103 : is_moderator(cfg[
"is_moderator"].to_bool(
false))
104 , profile_url_prefix(cfg[
"profile_url_prefix"].str())
109 bool is_moderator =
false;
112 std::string profile_url_prefix;
116 std::unique_ptr<wesnothd_connection> open_connection(std::string host);
119 bool enter_lobby_mode();
122 void enter_create_mode();
125 void enter_staging_mode();
128 void enter_wait_mode(
int game_id,
bool observe);
131 std::thread network_worker;
134 std::atomic_bool stop;
137 std::unique_ptr<wesnothd_connection> connection;
140 session_metadata session_info;
147 std::list<mp::network_registrar::handler> process_handlers;
150 const session_metadata& get_session_info()
const
155 auto add_network_handler(decltype(process_handlers)::value_type func)
157 return [
this, iter = process_handlers.insert(process_handlers.end(), func)]() { process_handlers.erase(iter); };
161 mp_manager::mp_manager(
const std::optional<std::string> host)
164 , connection(
nullptr)
170 state.classification().type = campaign_type::type::multiplayer;
174 connection = open_connection(*host);
179 if(connection ==
nullptr) {
188 connection->wait_and_receive_data(
data);
190 if(
const auto error =
data.optional_child(
"error")) {
194 else if(
data.has_child(
"gamelist")) {
195 this->lobby_info.process_gamelist(
data);
199 else if(
const auto gamelist_diff =
data.optional_child(
"gamelist_diff")) {
200 this->lobby_info.process_gamelist_diff(*gamelist_diff);
205 for(
const auto& handler : process_handlers) {
219 std::unique_ptr<wesnothd_connection> mp_manager::open_connection(std::string host)
221 DBG_MP <<
"opening connection";
228 std::set<std::pair<std::string, std::string>> shown_hosts;
229 auto addr = shown_hosts.end();
233 }
catch(
const std::runtime_error&) {
234 throw wesnothd_error(
_(
"Invalid address specified for multiplayer server"));
241 auto conn = std::make_unique<wesnothd_connection>(addr->first, addr->second);
244 conn->wait_for_handshake();
253 conn->wait_and_receive_data(
data);
255 if(
const auto reject =
data.optional_child(
"reject"); reject ||
data.has_attribute(
"version")) {
259 version = (*reject)[
"accepted_versions"].str();
262 version =
data[
"version"].str();
266 i18n_symbols[
"required_version"] = version;
269 const std::string errorstring =
VGETTEXT(
"The server accepts versions '$required_version', but you are using version '$your_version'", i18n_symbols);
274 if(
const auto redirect =
data.optional_child(
"redirect")) {
275 auto redirect_host = (*redirect)[
"host"].str();
276 auto redirect_port = (*redirect)[
"port"].str(
"15000");
279 std::tie(std::ignore, recorded_host) = shown_hosts.emplace(redirect_host, redirect_port);
289 conn = std::make_unique<wesnothd_connection>(redirect_host, redirect_port);
292 conn->wait_for_handshake();
298 if(
data.has_child(
"version")) {
303 conn->send_data(res);
306 if(
const auto error =
data.optional_child(
"error")) {
311 if(!
data.has_child(
"mustlogin")) {
323 conn->send_data(response);
324 conn->wait_and_receive_data(
data);
328 if(
const auto warning =
data.optional_child(
"warning")) {
329 std::string warning_msg;
332 warning_msg =
VGETTEXT(
"The nickname ‘$nick’ is inactive. "
333 "You cannot claim ownership of this nickname until you "
334 "activate your account via email or ask an "
335 "administrator to do it for you.", {{
"nick",
login}});
337 warning_msg = (*warning)[
"message"].str();
340 warning_msg +=
"\n\n";
341 warning_msg +=
_(
"Do you want to continue?");
350 auto error =
data.optional_child(
"error");
358 const bool fall_through = (*error)[
"force_confirmation"].to_bool()
378 conn->send_data(response);
379 conn->wait_and_receive_data(
data);
383 error =
data.optional_child(
"error");
394 std::string error_message;
396 i18n_symbols[
"nick"] =
login;
398 const auto extra_data = error->optional_child(
"data");
403 const std::string ec = (*error)[
"error_code"];
406 error_message =
_(
"The remote server requested a password while using an insecure connection.");
408 error_message =
_(
"You must login first.");
410 error_message =
VGETTEXT(
"The nickname ‘$nick’ is already taken.", i18n_symbols);
412 error_message =
VGETTEXT(
"The nickname ‘$nick’ contains invalid "
413 "characters. Only alpha-numeric characters (one at minimum), underscores and "
414 "hyphens are allowed.", i18n_symbols);
416 error_message =
VGETTEXT(
"The nickname ‘$nick’ is too long. Nicks must be 20 characters or less.", i18n_symbols);
418 error_message =
VGETTEXT(
"The nickname ‘$nick’ is reserved and cannot be used by players.", i18n_symbols);
420 error_message =
VGETTEXT(
"The nickname ‘$nick’ is not registered on this server.", i18n_symbols)
421 +
_(
" This server disallows unregistered nicknames.");
424 error_message =
VGETTEXT(
"The nickname ‘$nick’ is banned on this server’s forums for $duration|.", i18n_symbols);
426 error_message =
VGETTEXT(
"The nickname ‘$nick’ is banned on this server’s forums.", i18n_symbols);
430 error_message =
VGETTEXT(
"Your IP address is banned on this server’s forums for $duration|.", i18n_symbols);
432 error_message =
_(
"Your IP address is banned on this server’s forums.");
436 error_message =
VGETTEXT(
"The email address for the nickname ‘$nick’ is banned on this server’s forums for $duration|.", i18n_symbols);
438 error_message =
VGETTEXT(
"The email address for the nickname ‘$nick’ is banned on this server’s forums.", i18n_symbols);
441 error_message =
VGETTEXT(
"The nickname ‘$nick’ is registered on this server.", i18n_symbols);
443 error_message =
VGETTEXT(
"The nickname ‘$nick’ is registered on this server.", i18n_symbols)
444 +
"\n\n" +
_(
"WARNING: There is already a client using this nickname, "
445 "logging in will cause that client to be kicked!");
447 error_message =
_(
"The password you provided was incorrect.");
449 error_message =
_(
"You have made too many login attempts.");
451 error_message =
_(
"Password hashing failed.");
453 error_message = (*error)[
"message"].str();
478 if(
const auto join_lobby =
data.optional_child(
"join_lobby")) {
480 session_info = { join_lobby.value() };
490 void mp_manager::run_lobby_loop()
499 while(!enter_lobby_mode()) {
504 lobby_info.refresh_installed_addons_cache();
506 connection->send_data(
config(
"refresh_lobby"));
510 bool mp_manager::enter_lobby_mode()
512 DBG_MP <<
"entering lobby mode";
520 for(
const config&
i : cfg->child_range(
"music")) {
531 int dlg_joined_game_id = 0;
561 connection->send_data(
config(
"refresh_lobby"));
568 void mp_manager::enter_create_mode()
570 DBG_MP <<
"entering create mode";
572 if(gui2::dialogs::mp_create_game::execute(state, connection ==
nullptr)) {
573 enter_staging_mode();
574 }
else if(connection) {
575 connection->send_data(
config(
"refresh_lobby"));
579 void mp_manager::enter_staging_mode()
581 DBG_MP <<
"entering connect mode";
583 std::unique_ptr<mp_game_metadata> metadata;
587 metadata = std::make_unique<mp_game_metadata>(*connection);
589 metadata->is_host =
true;
595 dlg_ok = gui2::dialogs::mp_staging::execute(connect_engine, connection.get());
605 connection->send_data(
config(
"leave_game"));
609 void mp_manager::enter_wait_mode(
int game_id,
bool observe)
611 DBG_MP <<
"entering wait mode";
619 if(
const mp::game_info* gi = lobby_info.get_game_by_id(game_id)) {
633 connection->send_data(
config(
"leave_game"));
646 connection->send_data(
config(
"leave_game"));
651 return gui2::dialogs::mp_staging::execute(engine, connection.get());
654 bool mp_manager::post_scenario_wait(
bool observe)
659 connection->send_data(
config(
"leave_game"));
676 DBG_MP <<
"starting client";
677 mp_manager(host).run_lobby_loop();
682 DBG_MP <<
"starting local game";
686 mp_manager(std::nullopt).enter_create_mode();
691 DBG_MP <<
"starting local MP game from commandline";
700 DBG_MP <<
"entering create mode";
710 parameters.
name =
"multiplayer_The_Freelands";
720 DBG_MP <<
"ignoring map settings";
748 if(
auto cfg_multiplayer =
game_config.find_child(
"multiplayer",
"id", parameters.
name)) {
751 PLAIN_LOG <<
"Could not find [multiplayer] '" << parameters.
name <<
"'";
771 DBG_MP <<
"entering connect mode";
786 for(
unsigned int i = 0;
i < repeat;
i++){
795 return manager && manager->post_scenario_staging(engine);
800 return manager && manager->post_scenario_wait(observe);
805 return manager && manager->get_session_info().is_moderator;
811 const std::string& prefix = manager->get_session_info().profile_url_prefix;
813 if(!prefix.empty()) {
814 return prefix + std::to_string(user_id);
823 if(manager && manager->connection) {
824 manager->connection->send_data(
data);
844 return manager ? &manager->
lobby_info :
nullptr;
std::optional< unsigned int > multiplayer_repeat
Repeats specified by –multiplayer-repeat option.
std::optional< std::string > multiplayer_label
Non-empty if –label was given on the command line.
std::optional< std::string > multiplayer_scenario
Non-empty if –scenario was given on the command line.
std::optional< std::string > multiplayer_era
Non-empty if –era was given on the command line.
bool multiplayer_ignore_map_settings
True if –ignore-map-settings was given at the command line.
A config object defines a single node in a WML file, with access to child nodes.
config & add_child(config_key_type key)
std::string scenario_define
If there is a define the scenario uses to customize data.
std::string era_define
If there is a define the era uses to customize data.
static game_config_manager * get()
void load_game_config_for_game(const game_classification &classification, const std::string &scenario_id)
void load_game_config_for_create(bool is_mp, bool is_test=false)
void reload_changed_game_config()
const game_config_view & game_config() const
A class grating read only view to a vector of config objects, viewed as one config with all children ...
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
static void display(std::function< void()> f)
@ yes_no_buttons
Shows a yes and no button.
@ ok_cancel_buttons
Shows an ok and cancel button.
bool show(const unsigned auto_close_time=0)
Shows the window.
int get_retval() const
Returns the cached window exit code.
This shows the dialog to log in to the MP server.
This class represents the collective information the client has about the players and games on the se...
std::function< void()> remove_handler
std::function< void(const config &)> handler
network_registrar(handler func)
void start_game_commandline(const commandline_options &cmdline_opts, const game_config_view &game_config)
void add_log_data(const std::string &key, const std::string &var)
game_classification & classification()
void expand_mp_options()
adds values of [option]s into [carryover_sides_start][variables] so that they are applied in the next...
void set_carryover_sides_start(config carryover_sides_start)
std::string get_scenario_id() const
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
config & get_starting_point()
void expand_mp_events()
adds [event]s from [era] and [modification] into this scenario does NOT expand [option]s because vari...
void expand_random_scenario()
takes care of generate_map=, generate_scenario=, map= attributes This should be called before expandi...
std::string str() const
Serializes the version number into string form.
static std::string _(const char *str)
std::string label
What to show in the filter's drop-down list.
Standard logging facilities (interface).
General settings and defaults for scenarios.
static lg::log_domain log_mp("mp/main")
Define the errors the server may send during the login procedure.
#define MP_INCORRECT_PASSWORD_ERROR
#define MP_NAME_AUTH_BAN_USER_ERROR
#define MP_NAME_RESERVED_ERROR
#define MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME
#define MP_NAME_AUTH_BAN_EMAIL_ERROR
#define MP_TOO_MANY_ATTEMPTS_ERROR
#define MP_HASHING_PASSWORD_FAILED
#define MP_NAME_TOO_LONG_ERROR
#define MP_NAME_AUTH_BAN_IP_ERROR
#define MP_PASSWORD_REQUEST
#define MP_NAME_INACTIVE_WARNING
#define MP_NAME_UNREGISTERED_ERROR
#define MP_INVALID_CHARS_IN_NAME_ERROR
#define MP_NAME_TAKEN_ERROR
void call_in_main_thread(const std::function< void(void)> &f)
Game configuration data as global variables.
const version_info wesnoth_version(VERSION)
std::string dist_channel_id()
Return the distribution channel identifier, or "Default" if missing.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
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.
@ OK
Dialog was closed with the OK button.
@ CANCEL
Dialog was closed with the CANCEL button.
Main entry points of multiplayer mode.
lobby_info * get_lobby_info()
Returns the lobby_info object for the given session.
void send_to_server(const config &data)
Attempts to send given data to server if a connection is open.
void start_local_game()
Starts a multiplayer game in single-user mode.
bool goto_mp_staging(ng::connect_engine &engine)
Opens the MP Staging screen and sets the game state according to the changes made.
void start_local_game_commandline(const commandline_options &cmdline_opts)
Starts a multiplayer game in single-user mode using command line settings.
std::string get_profile_link(int user_id)
Gets the forum profile link for the given user.
void start_client(const std::string &host)
Pubic entry points for the MP workflow.
bool logged_in_as_moderator()
Gets whether the currently logged-in user is a moderator.
bool goto_mp_wait(bool observe)
Opens the MP Join Game screen and sets the game state according to the changes made.
void set_message_private(bool value)
std::string password(const std::string &server, const std::string &login)
int get_village_support(const std::string &value)
Gets the village unit level support.
int get_turns(const std::string &value)
Gets the number of turns.
int get_xp_modifier(const std::string &value)
Gets the xp modifier.
int get_village_gold(const std::string &value, const game_classification *classification)
Gets the village gold.
void play_music_config(const config &music_node, bool allow_interrupt_current_track, int i)
void commit_music_changes()
std::string format_timespan(std::time_t time, bool detailed)
Formats a timespan into human-readable text for player authentication functions.
std::map< std::string, t_string > string_map
std::pair< std::string, std::string > parse_network_address(const std::string &address, const std::string &default_port)
Parse a host:port style network address, supporting [] notation for ipv6 addresses.
This class represents the info a client has about a game on the server.
An error occurred during when trying to communicate with the wesnothd server.
Error used when the client is rejected by the MP server.