Skip to content

Commit

Permalink
MP Lobby: refactored game entry code
Browse files Browse the repository at this point in the history
This fixes #2121.

This code was an absolute mess. It relied entirely on finding games by-index, despite there also
being a simple function in lobby_data to get a game by id. I had even written code to take an id,
find a game_info ptr, then search the games vector for a match, get an index, and then use that
index to access the games vector later :| *facepalm*

Binding indices with callbacks meant  certain callbacks such as the list double click handler needed
to be constantly re-added. We also had no bounds checking in said callback. And for some reason, the
inline join/observe buttons were  handled by the same code as their global counterparts (#2121).

Just... a mess. Now we can directly handle joining by either index or game id directly. The former
is now only used for the global join/observe buttons' callback, but it could potentially be used
with the plugin context code, if applicable. It depends on whether the plugin data has an index key.
  • Loading branch information
Vultraz authored and GregoryLundberg committed Nov 30, 2017
1 parent f08ca94 commit ce89e25
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 139 deletions.
241 changes: 114 additions & 127 deletions src/gui/dialogs/multiplayer/lobby.cpp
Expand Up @@ -219,14 +219,6 @@ void modify_grid_with_data(grid* grid, const std::map<std::string, string_map>&
}
}

void set_visible_if_exists(grid* grid, const std::string& id, bool visible)
{
if(widget* w = grid->find(id, false)) {
//w->set_visible(visible ? widget::visibility::visible : widget::visibility::invisible);
w->set_visible(visible ? widget::visibility::visible : widget::visibility::hidden);
}
}

std::string colorize(const std::string& str, const std::string& color)
{
if(color.empty()) {
Expand All @@ -236,6 +228,54 @@ std::string colorize(const std::string& str, const std::string& color)
return (formatter() << "<span color=\"" << color << "\">" << str << "</span>").str();
}

bool handle_addon_requirements_gui(CVideo& v, const std::vector<mp::game_info::required_addon>& reqs, mp::game_info::ADDON_REQ addon_outcome)
{
if(addon_outcome == mp::game_info::CANNOT_SATISFY) {
std::string e_title = _("Incompatible User-made Content.");
std::string err_msg = _("This game cannot be joined because the host has out-of-date add-ons that are incompatible with your version. You might wish to suggest that the host's add-ons be updated.");

err_msg +="\n\n";
err_msg += _("Details:");
err_msg += "\n";

for(const mp::game_info::required_addon & a : reqs) {
if (a.outcome == mp::game_info::CANNOT_SATISFY) {
err_msg += font::unicode_bullet + " " + a.message + "\n";
}
}
gui2::show_message(v, e_title, err_msg, message::auto_close);

return false;
} else if(addon_outcome == mp::game_info::NEED_DOWNLOAD) {
std::string e_title = _("Missing User-made Content.");
std::string err_msg = _("This game requires one or more user-made addons to be installed or updated in order to join.\nDo you want to try to install them?");

err_msg +="\n\n";
err_msg += _("Details:");
err_msg += "\n";

std::vector<std::string> needs_download;
for(const mp::game_info::required_addon & a : reqs) {
if(a.outcome == mp::game_info::NEED_DOWNLOAD) {
err_msg += font::unicode_bullet + " " + a.message + "\n";

needs_download.push_back(a.addon_id);
}
}

assert(needs_download.size() > 0);

if(gui2::show_message(v, e_title, err_msg, message::yes_no_buttons, true) == gui2::window::OK) {
// Begin download session
ad_hoc_addon_fetch_session(v, needs_download);

return true;
}
}

return false;
}

} // end anonymous namespace

void mp_lobby::update_gamelist()
Expand Down Expand Up @@ -494,9 +534,6 @@ void mp_lobby::adjust_game_row_contents(const mp::game_info& game, int idx, grid
map.set_config(&game_config_);
map.set_map_data(game.map_data);

connect_signal_mouse_left_double_click(row_panel,
std::bind(&mp_lobby::join_or_observe, this, idx));

button& join_button = find_widget<button>(grid, "join", false);
button& observe_button = find_widget<button>(grid, "observe", false);

Expand All @@ -507,11 +544,14 @@ void mp_lobby::adjust_game_row_contents(const mp::game_info& game, int idx, grid
return;
}

connect_signal_mouse_left_double_click(row_panel,
std::bind(&mp_lobby::enter_game_by_id, this, game.id, DO_EITHER));

connect_signal_mouse_left_click(join_button,
std::bind(&mp_lobby::join_global_button_callback, this, std::ref(*get_window())));
std::bind(&mp_lobby::enter_game_by_id, this, game.id, DO_JOIN));

connect_signal_mouse_left_click(observe_button,
std::bind(&mp_lobby::observe_global_button_callback, this, std::ref(*get_window())));
std::bind(&mp_lobby::enter_game_by_id, this, game.id, DO_OBSERVE));
}

void mp_lobby::update_gamelist_filter()
Expand Down Expand Up @@ -720,13 +760,13 @@ void mp_lobby::pre_show(window& window)

connect_signal_mouse_left_click(
find_widget<button>(&window, "join_global", false),
std::bind(&mp_lobby::join_global_button_callback, this, std::ref(window)));
std::bind(&mp_lobby::enter_selected_game, this, DO_JOIN));

find_widget<button>(&window, "join_global", false).set_active(false);

connect_signal_mouse_left_click(
find_widget<button>(&window, "observe_global", false),
std::bind(&mp_lobby::observe_global_button_callback, this, std::ref(window)));
std::bind(&mp_lobby::enter_selected_game, this, DO_OBSERVE));

find_widget<button>(&window, "observe_global", false).set_active(false);

Expand Down Expand Up @@ -778,15 +818,11 @@ void mp_lobby::pre_show(window& window)
plugins_context_.reset(new plugins_context("Multiplayer Lobby"));

plugins_context_->set_callback("join", [&, this](const config&) {
if(do_game_join(get_game_index_from_id(selected_game_id_), false)) {
window.set_retval(JOIN);
}
enter_game_by_id(selected_game_id_, DO_JOIN);
}, true);

plugins_context_->set_callback("observe", [&, this](const config&) {
if(do_game_join(get_game_index_from_id(selected_game_id_), true)) {
window.set_retval(OBSERVE);
}
enter_game_by_id(selected_game_id_, DO_OBSERVE);
}, true);

plugins_context_->set_callback("create", [&window](const config&) { window.set_retval(CREATE); }, true);
Expand Down Expand Up @@ -876,129 +912,89 @@ void mp_lobby::process_gamelist_diff(const config& data)
}
}

void mp_lobby::observe_global_button_callback(window& window)
void mp_lobby::enter_game(const mp::game_info& game, JOIN_MODE mode)
{
if(do_game_join(gamelistbox_->get_selected_row(), true)) {
window.set_retval(OBSERVE);
}
}
const bool try_join = mode == DO_JOIN || (mode == DO_EITHER && game.can_join());
const bool try_obsv = mode == DO_OBSERVE;

void mp_lobby::join_global_button_callback(window& window)
{
if(do_game_join(gamelistbox_->get_selected_row(), false)) {
window.set_retval(JOIN);
if(try_obsv && !game.can_observe()) {
ERR_LB << "Attempted to observe a game with observers disabled" << std::endl;
return;
}
}

void mp_lobby::join_or_observe(int idx)
{
const mp::game_info& game = *lobby_info_.games()[idx];
if(do_game_join(idx, !game.can_join())) {
get_window()->set_retval(game.can_join() ? JOIN : OBSERVE);
if(try_join && !game.can_join()) {
ERR_LB << "Attempted to join a game with no vacant slots" << std::endl;
return;
}
}

static bool handle_addon_requirements_gui(CVideo& v, const std::vector<mp::game_info::required_addon>& reqs, mp::game_info::ADDON_REQ addon_outcome)
{
if(addon_outcome == mp::game_info::CANNOT_SATISFY) {
std::string e_title = _("Incompatible User-made Content.");
std::string err_msg = _("This game cannot be joined because the host has out-of-date add-ons that are incompatible with your version. You might wish to suggest that the host's add-ons be updated.");
window& window = *get_window();

err_msg +="\n\n";
err_msg += _("Details:");
err_msg += "\n";

for(const mp::game_info::required_addon & a : reqs) {
if (a.outcome == mp::game_info::CANNOT_SATISFY) {
err_msg += font::unicode_bullet + " " + a.message + "\n";
}
// Prompt user to download this game's required addons if its requirements have not been met
if(game.addons_outcome != mp::game_info::SATISFIED) {
if(game.required_addons.empty()) {
gui2::show_error_message(window.video(), _("Something is wrong with the addon version check database supporting the multiplayer lobby. Please report this at http://bugs.wesnoth.org."));
return;
}
gui2::show_message(v, e_title, err_msg, message::auto_close);

return false;
} else if(addon_outcome == mp::game_info::NEED_DOWNLOAD) {
std::string e_title = _("Missing User-made Content.");
std::string err_msg = _("This game requires one or more user-made addons to be installed or updated in order to join.\nDo you want to try to install them?");

err_msg +="\n\n";
err_msg += _("Details:");
err_msg += "\n";
if(!handle_addon_requirements_gui(window.video(), game.required_addons, game.addons_outcome)) {
return;
}

std::vector<std::string> needs_download;
for(const mp::game_info::required_addon & a : reqs) {
if(a.outcome == mp::game_info::NEED_DOWNLOAD) {
err_msg += font::unicode_bullet + " " + a.message + "\n";
// Addons have been downloaded, so the game_config and installed addons list need to be reloaded.
// The lobby is closed and reopened.
window.set_retval(RELOAD_CONFIG);
return;
}

needs_download.push_back(a.addon_id);
}
}
config response;

assert(needs_download.size() > 0);
config& join_data = response.add_child("join");
join_data["id"] = std::to_string(game.id);
join_data["observe"] = try_obsv;

if(gui2::show_message(v, e_title, err_msg, message::yes_no_buttons, true) == gui2::window::OK) {
// Begin download session
ad_hoc_addon_fetch_session(v, needs_download);
if(!join_data.empty() && try_join && game.password_required) {
std::string password;

return true;
if(!gui2::dialogs::mp_join_game_password_prompt::execute(password, window.video())) {
return;
}

join_data["password"] = password;
}

return false;
network_connection_.send_data(response);
joined_game_id_ = game.id;

// We're all good. Close lobby and proceed to game!
window.set_retval(try_join ? JOIN : OBSERVE);
}

bool mp_lobby::do_game_join(int idx, bool observe)
void mp_lobby::enter_game_by_index(const int index, JOIN_MODE mode)
{
if(idx < 0 || idx >= static_cast<int>(lobby_info_.games().size())) {
ERR_LB << "Requested join/observe of a game with index out of range: "
<< idx << ", games size is " << lobby_info_.games().size() << "\n";
return false;
}
const mp::game_info& game = *lobby_info_.games()[idx];
if(observe) {
if(!game.can_observe()) {
ERR_LB << "Requested observe of a game with observers disabled" << std::endl;
return false;
}
} else {
if(!game.can_join()) {
ERR_LB << "Requested join to a game with no vacant slots" << std::endl;
return false;
}
try {
enter_game(*lobby_info_.games().at(index), mode);
} catch(const std::out_of_range&) {
// Game index was invalid!
ERR_LB << "Attempted to join/observe a game with index out of range: " << index << ". "
<< "Games vector size is " << lobby_info_.games().size() << std::endl;
}
}

// Prompt user to download this game's required addons if its requirements have not been met
if(game.addons_outcome != mp::game_info::SATISFIED) {
if(game.required_addons.empty()) {
gui2::show_error_message(get_window()->video(), _("Something is wrong with the addon version check database supporting the multiplayer lobby. Please report this at http://bugs.wesnoth.org."));
return false;
}
void mp_lobby::enter_game_by_id(const int game_id, JOIN_MODE mode)
{
mp::game_info* game_ptr = lobby_info_.get_game_by_id(game_id);

if(!handle_addon_requirements_gui(get_window()->video(), game.required_addons, game.addons_outcome)) {
return false;
} else {
// Addons have been downloaded, so the game_config and installed addons list need to be reloaded.
// The lobby is closed and reopened.
get_window()->set_retval(RELOAD_CONFIG);
return false;
}
if(!game_ptr) {
ERR_LB << "Attempted to join/observe a game with an invalid id: " << game_id << std::endl;
return;
}

config response;
config& join = response.add_child("join");
join["id"] = std::to_string(game.id);
join["observe"] = observe;
if(join && !observe && game.password_required) {
std::string password;
if(!gui2::dialogs::mp_join_game_password_prompt::execute(password, get_window()->video())) {
return false;
}

join["password"] = password;
}
enter_game(*game_ptr, mode);
}

network_connection_.send_data(response);
joined_game_id_ = game.id;
return true;
void mp_lobby::enter_selected_game(JOIN_MODE mode)
{
enter_game_by_index(gamelistbox_->get_selected_row(), mode);
}

void mp_lobby::refresh_lobby()
Expand Down Expand Up @@ -1141,14 +1137,5 @@ void mp_lobby::skip_replay_changed_callback(window& window)
preferences::set_blindfold_replay(value == 2);
}

int mp_lobby::get_game_index_from_id(const int game_id) const
{
if(mp::game_info* game = lobby_info_.get_game_by_id(game_id)) {
return std::find(lobby_info_.games().begin(), lobby_info_.games().end(), game) - lobby_info_.games().begin();
}

return -1;
}

} // namespace dialogs
} // namespace gui2
34 changes: 22 additions & 12 deletions src/gui/dialogs/multiplayer/lobby.hpp
Expand Up @@ -118,19 +118,31 @@ class mp_lobby : public modal_dialog, public quit_confirmation, private plugin_e

void process_gamelist_diff(const config& data);

void join_global_button_callback(window& window);

void observe_global_button_callback(window& window);

void join_or_observe(int index);
enum JOIN_MODE { DO_JOIN, DO_OBSERVE, DO_EITHER };

/**
* Assemble and send a game join request. Ask for password if the game
* requires one.
* @return true iff the request was actually sent, false if not for some
* reason like user canceled the password dialog or idx was invalid.
* Exits the lobby and enters the given game.
*
* This assembles the game request for the server and handles any applicable
* pre-join actions, such as prompting the player for the game's password or
* informing them additonal content needs installing.
*
* The lobby window will be closed on completion, assuming an error wasn't
* encountered.
*
* @param game Info on the game we're attempting to join.
* @param mode
*/
bool do_game_join(int idx, bool observe);
void enter_game(const mp::game_info& game, JOIN_MODE mode);

/** Entry wrapper for @ref enter_game, where game is located by index. */
void enter_game_by_index(const int index, JOIN_MODE mode);

/** Entry wrapper for @ref enter_game, where game is located by game id. */
void enter_game_by_id(const int game_id, JOIN_MODE mode);

/** Enter game by index, where index is the selected game listbox row. */
void enter_selected_game(JOIN_MODE mode);

void show_preferences_button_callback(window& window);

Expand All @@ -154,8 +166,6 @@ class mp_lobby : public modal_dialog, public quit_confirmation, private plugin_e

static bool logout_prompt();

int get_game_index_from_id(const int game_id) const;

/** Inherited from modal_dialog, implemented by REGISTER_DIALOG. */
virtual const std::string& window_id() const override;

Expand Down

0 comments on commit ce89e25

Please sign in to comment.