17 #include "addon/manager.hpp" 50 #define DBG_MP LOG_STREAM(debug, log_mp) 51 #define ERR_MP LOG_STREAM(err, log_mp) 56 std::pair<wesnothd_connection_ptr, config> open_connection(std::string host)
58 DBG_MP <<
"opening connection" << std::endl;
62 return std::make_pair(
nullptr,
config());
66 using hostpair = std::pair<std::string, std::string>;
68 std::set<hostpair> shown_hosts;
72 }
catch(
const std::runtime_error&) {
73 throw wesnothd_error(
_(
"Invalid address specified for multiplayer server"));
75 shown_hosts.insert(addr);
78 sock = std::make_unique<wesnothd_connection>(addr.first, addr.second);
80 return std::make_pair(
nullptr,
config());
87 sock->wait_for_handshake();
92 config initial_lobby_config;
94 bool received_join_lobby =
false;
95 bool received_gamelist =
false;
100 return std::make_pair(
nullptr,
config());
104 sock->wait_and_receive_data(data);
110 version = reject[
"accepted_versions"].str();
113 version = data[
"version"].str();
117 i18n_symbols[
"required_version"] = version;
120 const std::string errorstring =
VGETTEXT(
"The server accepts versions '$required_version', but you are using version '$your_version'", i18n_symbols);
126 auto redirect_host =
redirect[
"host"].str();
127 auto redirect_port =
redirect[
"port"].str(
"15000");
129 if(shown_hosts.find(hostpair(redirect_host, redirect_port)) != shown_hosts.end()) {
133 shown_hosts.emplace(redirect_host, redirect_port);
139 sock = std::make_unique<wesnothd_connection>(redirect_host, redirect_port);
142 sock->wait_for_handshake();
154 std::ifstream infofile(
"./data/dist");
155 if(infofile.is_open()){
159 if(info ==
"Default" || info ==
"Steam" || info ==
"SourceForge" || info ==
"Flatpak" 160 || info ==
"macOS App Store" || info ==
"Linux repository" || info ==
"iOS" || info ==
"Android" 161 || info ==
"BSD repository") {
162 cfg[
"client_source"] =
info;
164 cfg[
"client_source"] =
"Default";
167 cfg[
"client_source"] =
"Default";
169 res.
add_child(
"version", std::move(cfg));
170 sock->send_data(res);
176 received_gamelist =
true;
183 std::string error_message;
185 error_message = (*error)[
"message"].str();
191 if(
const config& message = data.
child(
"message")) {
206 sp[
"username"] =
login ;
208 sock->send_data(response);
209 sock->wait_and_receive_data(data);
216 std::string warning_msg;
219 warning_msg =
VGETTEXT(
"The nickname ‘$nick’ is inactive. " 220 "You cannot claim ownership of this nickname until you " 221 "activate your account via email or ask an " 222 "administrator to do it for you.", {{
"nick", login}});
224 warning_msg = (*warning)[
"message"].str();
227 warning_msg +=
"\n\n";
228 warning_msg +=
_(
"Do you want to continue?");
245 bool fall_through = (*error)[
"force_confirmation"].to_bool() ?
249 const bool is_pw_request = !((*error)[
"password_request"].empty()) && !(password.empty());
255 if(is_pw_request && !fall_through) {
256 if((*error)[
"phpbb_encryption"].to_bool()) {
261 for(std::string::size_type pos = 0; (pos = password.find(
'&', pos)) != std::string::npos; ++pos)
262 password.replace(pos, 1,
"&");
263 for(std::string::size_type pos = 0; (pos = password.find(
'\"', pos)) != std::string::npos; ++pos)
264 password.replace(pos, 1,
""");
265 for(std::string::size_type pos = 0; (pos = password.find(
'<', pos)) != std::string::npos; ++pos)
266 password.replace(pos, 1,
"<");
267 for(std::string::size_type pos = 0; (pos = password.find(
'>', pos)) != std::string::npos; ++pos)
268 password.replace(pos, 1,
">");
270 const std::string salt = (*error)[
"salt"];
272 if(salt.length() < 12) {
283 std::string outer_salt = salt.substr(bcrypt_salt.iteration_count_delim_pos + 23);
284 if(outer_salt.size() != 32)
286 sp[
"password"] =
utils::md5(hash.base64_digest(), outer_salt).base64_digest();
288 ERR_MP <<
"bcrypt hash failed: " << err.
what() << std::endl;
299 sock->send_data(response);
300 sock->wait_and_receive_data(data);
304 error = &data.
child(
"error");
315 std::string error_message;
317 i18n_symbols[
"nick"] =
login;
319 const bool has_extra_data = error->
has_child(
"data");
325 error_message =
_(
"You must login first.");
327 error_message =
VGETTEXT(
"The nickname ‘$nick’ is already taken.", i18n_symbols);
329 error_message =
VGETTEXT(
"The nickname ‘$nick’ contains invalid " 330 "characters. Only alpha-numeric characters (one at minimum), underscores and " 331 "hyphens are allowed.", i18n_symbols);
333 error_message =
VGETTEXT(
"The nickname ‘$nick’ is too long. Nicks must " 334 "be 20 characters or less.", i18n_symbols);
336 error_message =
VGETTEXT(
"The nickname ‘$nick’ is reserved and cannot be used by players.", i18n_symbols);
338 error_message =
VGETTEXT(
"The nickname ‘$nick’ is not registered on this server.", i18n_symbols)
339 +
_(
" This server disallows unregistered nicknames.");
342 error_message =
VGETTEXT(
"The nickname ‘$nick’ is banned on this server’s forums for $duration|.", i18n_symbols);
344 error_message =
VGETTEXT(
"The nickname ‘$nick’ is banned on this server’s forums.", i18n_symbols);
348 error_message =
VGETTEXT(
"Your IP address is banned on this server’s forums for $duration|.", i18n_symbols);
350 error_message =
_(
"Your IP address is banned on this server’s forums.");
354 error_message =
VGETTEXT(
"The email address for the nickname ‘$nick’ is banned on this server’s forums for $duration|.", i18n_symbols);
356 error_message =
VGETTEXT(
"The email address for the nickname ‘$nick’ is banned on this server’s forums.", i18n_symbols);
359 error_message =
VGETTEXT(
"The nickname ‘$nick’ is registered on this server.", i18n_symbols);
361 error_message =
VGETTEXT(
"The nickname ‘$nick’ is registered on this server.", i18n_symbols)
362 +
"\n\n" +
_(
"WARNING: There is already a client using this nickname, " 363 "logging in will cause that client to be kicked!");
365 error_message =
_(
"Error in the login procedure (the server had no " 366 "seed for your connection).");
368 error_message =
_(
"The password you provided was incorrect.");
370 error_message =
_(
"You have made too many login attempts.");
372 error_message = (*error)[
"message"].str();
399 received_join_lobby =
true;
403 }
while(!received_join_lobby || !received_gamelist);
405 return std::make_pair(std::move(sock), std::move(initial_lobby_config));
409 struct mp_workflow_helper
414 , connection(connection)
427 using mp_workflow_helper_ptr = std::shared_ptr<mp_workflow_helper>;
437 void enter_wait_mode(mp_workflow_helper_ptr helper,
int game_id,
bool observe)
439 DBG_MP <<
"entering wait mode" << std::endl;
442 assert(helper->connection);
446 auto campaign_info = std::make_unique<mp_campaign_info>(*helper->connection);
447 campaign_info->is_host =
false;
449 if(helper->lobby_info->get_game_by_id(game_id)) {
450 campaign_info->current_turn = helper->lobby_info->get_game_by_id(game_id)->current_turn;
454 campaign_info->skip_replay =
true;
463 helper->connection->send_data(
config(
"leave_game"));
477 helper->connection->send_data(
config(
"leave_game"));
480 void enter_staging_mode(mp_workflow_helper_ptr helper)
482 DBG_MP <<
"entering connect mode" << std::endl;
484 std::unique_ptr<mp_campaign_info> campaign_info;
487 if(helper->connection) {
508 if(helper->connection) {
509 helper->connection->send_data(
config(
"leave_game"));
515 DBG_MP <<
"entering create mode" << std::endl;
519 bool local_mode = helper->connection ==
nullptr;
531 enter_staging_mode(helper);
532 }
else if(helper->connection) {
533 helper->connection->send_data(
config(
"refresh_lobby"));
537 bool enter_lobby_mode(mp_workflow_helper_ptr helper,
const std::vector<std::string>&
installed_addons,
const config& initial_lobby_config)
539 DBG_MP <<
"entering lobby mode" << std::endl;
542 assert(helper->connection);
546 if(
const config& cfg = helper->game_config.
child(
"lobby_music")) {
547 for(
const config&
i : cfg.child_range(
"music")) {
558 helper->lobby_info = &li;
560 if(!initial_lobby_config.
empty()) {
565 int dlg_joined_game_id = 0;
584 helper->connection->send_data(
config(
"refresh_lobby"));
591 enter_wait_mode(helper,
601 helper->connection->send_data(
config(
"refresh_lobby"));
624 const config* game_config_ptr = &game_config;
631 DBG_MP <<
"starting client" << std::endl;
639 std::tie(connection, lobby_config) = open_connection(host);
646 mp_workflow_helper_ptr workflow_helper;
647 bool re_enter =
false;
650 workflow_helper.reset(
new mp_workflow_helper(*game_config_ptr, state, connection.get(),
nullptr));
653 re_enter = !enter_lobby_mode(workflow_helper, installed_addons, lobby_config);
664 connection->send_data(
config(
"refresh_lobby"));
673 return gui2::dialogs::mp_staging::execute(engine, li, connection);
682 if(!dlg.fetch_game_config()) {
696 DBG_MP <<
"starting local game" << std::endl;
702 mp_workflow_helper_ptr workflow_helper = std::make_shared<mp_workflow_helper>(game_config, state,
nullptr, &li);
709 DBG_MP <<
"starting local MP game from commandline" << std::endl;
716 DBG_MP <<
"entering create mode" << std::endl;
723 parameters.
mp_era =
"era_default";
724 parameters.
name =
"multiplayer_The_Freelands";
734 DBG_MP <<
"ignoring map settings" << std::endl;
743 state.
classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
753 std::cerr <<
"Could not find era '" << parameters.
mp_era <<
"'\n";
762 if(
const config& cfg_multiplayer = game_config.
find_child(
"multiplayer",
"id", parameters.
name)) {
765 std::cerr <<
"Could not find [multiplayer] '" << parameters.
name <<
"'\n";
784 DBG_MP <<
"entering connect mode" << std::endl;
792 connect_engine->start_game_commandline(cmdline_opts, game_config);
801 for(
unsigned int i = 0;
i < repeat;
i++){
void send_data(const configr_of &request)
An error occurred during when trying to coommunicate with the wesnothd server.
std::string format_timespan(std::time_t time)
Formats a timespan into human-readable text.
Dialog was closed with the CANCEL button.
int get_joined_game_id() const
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.
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...
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.
Error used when the client is rejected by the MP server.
std::map< std::string, t_string > string_map
#define MP_NAME_AUTH_BAN_USER_ERROR
static bool is_valid_prefix(const std::string &hash)
const ter_data_cache & terrain_types() const
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.
void add_log_data(const std::string &key, const std::string &var)
static l_noret error(LoadState *S, const char *why)
#define MP_TOO_MANY_ATTEMPTS_ERROR
bool has_attribute(config_key_type key) const
std::unique_ptr< wesnothd_connection > wesnothd_connection_ptr
std::set< std::string > connected_players
players and observers
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Shows an ok and cancel button.
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...
void parse_admin_authentication(const std::string &sender, const std::string &message)
This class represents the collective information the client has about the players and games on the se...
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.
static bool is_valid_prefix(const std::string &hash)
static int get_iteration_count(const std::string &hash)
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.
#define MP_NAME_TOO_LONG_ERROR
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 show(const unsigned auto_close_time=0)
Shows the window.
void process_gamelist(const config &data)
Process a full game list.
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)
#define MP_INCORRECT_PASSWORD_ERROR
int get_village_support(const std::string &value)
Gets the village unit level support.
Pubic entry points for the MP workflow.
static game_config_manager * get()
static UNUSEDNOWARN std::string _(const char *str)
#define MP_NAME_UNREGISTERED_ERROR
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...
static bcrypt from_salted_salt(const std::string &input)
A class that represents a TCP/IP connection to the wesnothd server.
std::vector< std::string > installed_addons()
Retrieves the names of all installed add-ons.
config & get_starting_point()
Shows a yes and no button.
Used to reset is_authenticated flag after disconnecting.
const char * what() const noexcept
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
void set_message_private(bool value)
General settings and defaults for scenarios.
std::string era_define
If there is a define the era uses to customize data.
void start_client(const config &game_config, saved_game &state, const std::string &host)
Starts a multiplayer game in client mode.
void start_local_game(const config &game_config, saved_game &state)
Starts a multiplayer game in single-user mode.
Game configuration data as global variables.
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.
void expand_mp_events()
adds [event]s from [era] and [modification] into this scenario does NOT expand [option]s because vari...
#define MP_PASSWORD_REQUEST
static lg::log_domain log_mp("mp/main")
#define MP_NAME_INACTIVE_WARNING
const version_info wesnoth_version(VERSION)
config & add_child(config_key_type key)
#define MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME
#define MP_NAME_TAKEN_ERROR
This class represents the information a client has about another player.
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...
void play_music_config(const config &music_node, bool allow_interrupt_current_track, int i)
game_classification & classification()
void set_carryover_sides_start(config carryover_sides_start)
Standard logging facilities (interface).
std::string str() const
Serializes the version number into string form.
static std::string get_salt(const std::string &hash)
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
static bcrypt hash_pw(const std::string &password, bcrypt &salt)
void reload_changed_game_config()
#define MP_NAME_RESERVED_ERROR
const std::unique_ptr< connect_engine > connect_engine_ptr
void commit_music_changes()
Dialog was closed with the OK button.
A config object defines a single node in a WML file, with access to child nodes.
#define MP_NAME_AUTH_BAN_EMAIL_ERROR
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
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.
const config & game_config() const
bool goto_mp_wait(saved_game &state, wesnothd_connection *connection, bool observe)
Opens mp::wait screen and sets game state according to the changes made.
bool goto_mp_connect(ng::connect_engine &engine, wesnothd_connection *connection)
Opens mp::connect screen and sets game state according to the changes made.
#define MP_NAME_AUTH_BAN_IP_ERROR
#define MP_INVALID_CHARS_IN_NAME_ERROR