Skip to content

Commit

Permalink
Refactor MP connection code to avoid constantly creating loading screens
Browse files Browse the repository at this point in the history
Previously, the use of the wesnothd_connect_dialog and wesnothd_receive_dialog wrappers meant
multiple loading screen instances were created when connecting to MP. This change means only
one (or two, if the config is reloaded) loading screen objects are created when connecting to
the server. It also ensures the entire connection process (ie, the extent of open_connection)
is done with a loading screen displayed.
  • Loading branch information
Vultraz authored and GregoryLundberg committed Nov 30, 2017
1 parent dddd0c9 commit 386336c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 46 deletions.
99 changes: 72 additions & 27 deletions src/game_initialization/multiplayer.cpp
Expand Up @@ -15,13 +15,15 @@
#include "game_initialization/multiplayer.hpp"

#include "addon/manager.hpp" // for installed_addons
#include "events.hpp"
#include "formula/string_utils.hpp"
#include "game_config_manager.hpp"
#include "game_initialization/mp_game_utils.hpp"
#include "game_initialization/playcampaign.hpp"
#include "preferences/credentials.hpp"
#include "preferences/game.hpp"
#include "gettext.hpp"
#include "gui/dialogs/loading_screen.hpp"
#include "gui/dialogs/message.hpp"
#include "gui/dialogs/multiplayer/lobby.hpp"
#include "gui/dialogs/multiplayer/mp_create_game.hpp"
Expand All @@ -46,8 +48,10 @@
static lg::log_domain log_mp("mp/main");
#define DBG_MP LOG_STREAM(debug, log_mp)

namespace
{
/** Opens a new server connection and prompts the client for login credentials, if necessary. */
static wesnothd_connection_ptr open_connection(CVideo& video, std::string host)
wesnothd_connection_ptr open_connection(CVideo& video, std::string host)
{
DBG_MP << "opening connection" << std::endl;

Expand All @@ -68,23 +72,50 @@ static wesnothd_connection_ptr open_connection(CVideo& video, std::string host)
}

// shown_hosts is used to prevent the client being locked in a redirect loop.
typedef std::pair<std::string, int> hostpair;
using hostpair = std::pair<std::string, int>;

std::set<hostpair> shown_hosts;
shown_hosts.insert(hostpair(host, port));
shown_hosts.emplace(host, port);

// Initializes the connection to the server.
sock = wesnothd_connection::create(host, std::to_string(port));
if(!sock) {
return sock;
}

config data;
sock = gui2::dialogs::network_transmission::wesnothd_connect_dialog(video, "connect to server", host, port);

// Start stage
gui2::dialogs::loading_screen::progress("connect to server");

// First, spin until we get a handshake from the server.
while(!sock->handshake_finished()) {
sock->poll();
SDL_Delay(1);
}

gui2::dialogs::loading_screen::progress("waiting");

const auto wait_for_server_to_send_data = [&sock, &data]() {
while(!sock->has_data_received()) {
SDL_Delay(1);
}

sock->receive_data(data);
};

// Then, log in and wait for the lobby/game join prompt.
do {
if(!sock) {
return sock;
}

data.clear();
gui2::dialogs::network_transmission::wesnothd_receive_dialog(video, "waiting", data, *sock);
//mp::check_response(data_res, data);
wait_for_server_to_send_data();

if(data.has_child("reject") || data.has_attribute("version")) {
std::string version;

if(const config& reject = data.child("reject")) {
version = reject["accepted_versions"].str();
} else {
Expand All @@ -95,26 +126,29 @@ static wesnothd_connection_ptr open_connection(CVideo& video, std::string host)
utils::string_map i18n_symbols;
i18n_symbols["required_version"] = version;
i18n_symbols["your_version"] = game_config::version;

const std::string errorstring = vgettext("The server accepts versions '$required_version', but you are using version '$your_version'", i18n_symbols);
throw wesnothd_error(errorstring);
}

// Check for "redirect" messages
if(const config& redirect = data.child("redirect"))
{
if(const config& redirect = data.child("redirect")) {
host = redirect["host"].str();
port =redirect["port"].to_int(15000);
port = redirect["port"].to_int(15000);

if(shown_hosts.find(hostpair(host,port)) != shown_hosts.end()) {
if(shown_hosts.find(hostpair(host, port)) != shown_hosts.end()) {
throw wesnothd_error(_("Server-side redirect loop"));
}
shown_hosts.insert(hostpair(host, port));

shown_hosts.emplace(host, port);

// Open a new connection with the new host and port.
sock = wesnothd_connection_ptr();
sock = gui2::dialogs::network_transmission::wesnothd_connect_dialog(video, "redirect", host, port);
sock = wesnothd_connection::create(host, std::to_string(port));
continue;
}

if(data.child("version")) {
if(data.has_child("version")) {
config cfg;
config res;
cfg["version"] = game_config::version;
Expand All @@ -123,7 +157,7 @@ static wesnothd_connection_ptr open_connection(CVideo& video, std::string host)
}

// Continue if we did not get a direction to login
if(!data.child("mustlogin")) {
if(!data.has_child("mustlogin")) {
continue;
}

Expand Down Expand Up @@ -151,7 +185,10 @@ static wesnothd_connection_ptr open_connection(CVideo& video, std::string host)
}

sock->send_data(response);
gui2::dialogs::network_transmission::wesnothd_receive_dialog(video, "login response", data, *sock);
wait_for_server_to_send_data();

gui2::dialogs::loading_screen::progress("login response");

config* warning = &data.child("warning");

if(*warning) {
Expand Down Expand Up @@ -226,7 +263,9 @@ static wesnothd_connection_ptr open_connection(CVideo& video, std::string host)

// Once again send our request...
sock->send_data(response);
gui2::dialogs::network_transmission::wesnothd_receive_dialog(video, "login response", data, *sock);
wait_for_server_to_send_data();

gui2::dialogs::loading_screen::progress("login response");

error = &data.child("error");

Expand Down Expand Up @@ -279,7 +318,9 @@ static wesnothd_connection_ptr open_connection(CVideo& video, std::string host)
}

gui2::dialogs::mp_login dlg(host, error_message, !((*error)["password_request"].empty()));
dlg.show(video);

// Need to show the dialog from the main thread or it won't appear.
events::call_in_main_thread([&dlg, &video]() { dlg.show(video); });

switch(dlg.get_retval()) {
//Log in with password
Expand All @@ -304,11 +345,11 @@ static wesnothd_connection_ptr open_connection(CVideo& video, std::string host)
} // end login loop
} while(!(data.child("join_lobby") || data.child("join_game")));

if(data.child("join_lobby")) {
return sock;
if(!data.has_child("join_lobby")) {
return wesnothd_connection_ptr();
}

return wesnothd_connection_ptr();
return sock;
}

/** Helper struct to manage the MP workflow arguments. */
Expand Down Expand Up @@ -343,7 +384,7 @@ using mp_workflow_helper_ptr = std::shared_ptr<mp_workflow_helper>;
*
* NOTE: since these functions are static, they appear here in the opposite order they'd be accessed.
*/
static void enter_wait_mode(mp_workflow_helper_ptr helper, int game_id, bool observe)
void enter_wait_mode(mp_workflow_helper_ptr helper, int game_id, bool observe)
{
DBG_MP << "entering wait mode" << std::endl;

Expand Down Expand Up @@ -385,7 +426,7 @@ static void enter_wait_mode(mp_workflow_helper_ptr helper, int game_id, bool obs
helper->connection->send_data(config("leave_game"));
}

static void enter_staging_mode(mp_workflow_helper_ptr helper)
void enter_staging_mode(mp_workflow_helper_ptr helper)
{
DBG_MP << "entering connect mode" << std::endl;

Expand Down Expand Up @@ -418,7 +459,7 @@ static void enter_staging_mode(mp_workflow_helper_ptr helper)
}
}

static void enter_create_mode(mp_workflow_helper_ptr helper)
void enter_create_mode(mp_workflow_helper_ptr helper)
{
DBG_MP << "entering create mode" << std::endl;

Expand All @@ -438,7 +479,7 @@ static void enter_create_mode(mp_workflow_helper_ptr helper)
}
}

static bool enter_lobby_mode(mp_workflow_helper_ptr helper, const std::vector<std::string>& installed_addons)
bool enter_lobby_mode(mp_workflow_helper_ptr helper, const std::vector<std::string>& installed_addons)
{
DBG_MP << "entering lobby mode" << std::endl;

Expand Down Expand Up @@ -514,9 +555,11 @@ static bool enter_lobby_mode(mp_workflow_helper_ptr helper, const std::vector<st
return true;
}

/** Pubic entry points for the MP workflow */
namespace mp {
} // end anon namespace

/** Pubic entry points for the MP workflow */
namespace mp
{
void start_client(CVideo& video, const config& game_config, saved_game& state, const std::string& host)
{
const config* game_config_ptr = &game_config;
Expand All @@ -530,7 +573,9 @@ void start_client(CVideo& video, const config& game_config, saved_game& state, c

preferences::admin_authentication_reset r;

wesnothd_connection_ptr connection = open_connection(video, host);
wesnothd_connection_ptr connection;
gui2::dialogs::loading_screen::display(video, [&]() { connection = open_connection(video, host); });

if(!connection) {
return;
}
Expand Down
18 changes: 0 additions & 18 deletions src/gui/dialogs/network_transmission.cpp
Expand Up @@ -137,23 +137,5 @@ bool network_transmission::wesnothd_receive_dialog(CVideo& video, const std::str
return connection.receive_data(cfg);
}

struct connect_wesnothd_connection_data : public network_transmission::connection_data
{
connect_wesnothd_connection_data(wesnothd_connection& conn) : conn_(conn) {}
virtual bool finished() override { return conn_.handshake_finished(); }
virtual void cancel() override { }
virtual void poll() override { conn_.poll(); }
wesnothd_connection& conn_;
};

wesnothd_connection_ptr network_transmission::wesnothd_connect_dialog(CVideo& video, const std::string& msg, const std::string& hostname, int port)
{
assert(!msg.empty());
wesnothd_connection_ptr res = wesnothd_connection::create(hostname, std::to_string(port));
connect_wesnothd_connection_data gui_data(*res);
wesnothd_dialog(video, gui_data, msg);
return res;
}

} // namespace dialogs
} // namespace gui2
1 change: 0 additions & 1 deletion src/gui/dialogs/network_transmission.hpp
Expand Up @@ -48,7 +48,6 @@ class network_transmission : public modal_dialog
};

static bool wesnothd_receive_dialog(CVideo& video, const std::string& msg, config& cfg, wesnothd_connection& connection);
static wesnothd_connection_ptr wesnothd_connect_dialog(CVideo& video, const std::string& msg, const std::string& hostname, int port);

private:
static void wesnothd_dialog(CVideo& video, connection_data& conn, const std::string& msg);
Expand Down

0 comments on commit 386336c

Please sign in to comment.