16 #define GETTEXT_DOMAIN "wesnoth-lib"
54 #define DBG_LB LOG_STREAM(debug, log_lobby)
55 #define LOG_LB LOG_STREAM(info, log_lobby)
56 #define ERR_LB LOG_STREAM(err, log_lobby)
57 #define SCOPE_LB log_scope2(log_lobby, __func__)
65 return show_prompt(
_(
"Do you really want to log out?"));
71 , gamelistbox_(nullptr)
74 , filter_friends_(register_bool(
"filter_with_friends",
76 []() {
return prefs::get().fi_friends_in_game();},
77 [](
bool v) {
prefs::get().set_fi_friends_in_game(v);},
79 , filter_ignored_(register_bool(
"filter_with_ignored",
81 []() {
return prefs::get().fi_blocked_in_game();},
82 [](
bool v) {
prefs::get().set_fi_blocked_in_game(v);},
84 , filter_slots_(register_bool(
"filter_vacant_slots",
87 [](
bool v) {
prefs::get().set_fi_vacant_slots(v);},
89 , filter_invert_(register_bool(
"filter_invert",
94 , filter_auto_hosted_(
false)
95 , filter_text_(
nullptr)
98 , player_list_dirty_(
true)
99 , gamelist_dirty_(
true)
100 , last_lobby_update_()
101 , gamelist_diff_update_(
true)
102 , network_connection_(connection)
103 , lobby_update_timer_(0)
104 , gamelist_id_at_row_()
105 , delay_playerlist_update_(
false)
106 , delay_gamelist_update_(
false)
107 , joined_game_id_(joined_game)
109 set_show_even_without_video(
true);
110 set_allow_plugin_skip(
false);
111 set_always_save_fields(
true);
145 for(
const auto& v : map) {
146 const std::string& key = v.first;
159 for(
const auto & vv : strmap) {
160 if(vv.first ==
"label") {
161 c->set_label(vv.second);
162 }
else if(vv.first ==
"tooltip") {
163 c->set_tooltip(vv.second);
169 bool handle_addon_requirements_gui(
const std::vector<mp::game_info::required_addon>& reqs,
mp::game_info::addon_req addon_outcome)
172 std::string e_title =
_(
"Incompatible User-made Content");
173 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.");
176 err_msg +=
_(
"Details:");
188 std::string e_title =
_(
"Missing User-made Content");
189 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?");
192 err_msg +=
_(
"Details:");
195 std::vector<std::string> needs_download;
200 needs_download.push_back(a.addon_id);
204 assert(needs_download.size() > 0);
240 LOG_LB <<
"Adding game to listbox (1)" <<
game.id;
264 int list_rows_deleted = 0;
268 std::vector<int> next_gamelist_id_at_row;
279 LOG_LB <<
"Adding game to listbox " <<
game.id;
283 DBG_LB <<
"Added a game listbox row not at the end" << list_i
294 next_gamelist_id_at_row.push_back(
game.id);
297 ERR_LB <<
"Ran out of listbox items -- triggering a full refresh";
303 ERR_LB <<
"gamelist_id_at_row_ overflow! " << list_i <<
" + "
306 <<
" -- triggering a full refresh";
312 if(
game.id != listbox_game_id) {
313 ERR_LB <<
"Listbox game id does not match expected id "
314 << listbox_game_id <<
" " <<
game.id <<
" (row " << list_i <<
")";
320 LOG_LB <<
"Modifying game in listbox " <<
game.id <<
" (row " << list_i <<
")";
325 next_gamelist_id_at_row.push_back(
game.id);
327 LOG_LB <<
"Deleting game from listbox " <<
game.id <<
" (row "
333 LOG_LB <<
"Clean game in listbox " <<
game.id <<
" (row " << list_i <<
")";
334 next_gamelist_id_at_row.push_back(
game.id);
340 for(
unsigned i = 0;
i < next_gamelist_id_at_row.size(); ++
i) {
348 ERR_LB <<
"Would select a row beyond the listbox" << select_row <<
" "
366 const std::string games_string =
VGETTEXT(
"Games: showing $num_shown out of $num_total", {
381 item[
"use_markup"] =
"true";
384 if(
game.vacant_slots > 0) {
388 const std::string scenario_text =
VGETTEXT(
"$game_name (Era: $era_name)", {
389 {
"game_name",
game.scenario},
390 {
"era_name",
game.era}
394 data.emplace(
"name", item);
397 data.emplace(
"scenario", item);
400 data.emplace(
"status", item);
415 std::ostringstream ss;
417 const auto mark_missing = [&ss]() {
430 auto mods =
game.mod_info;
433 ss <<
_(
"active_modifications^None") <<
"\n";
435 for(
const auto& mod : mods) {
447 const auto yes_or_no = [](
bool val) {
return val ?
_(
"yes") :
_(
"no"); };
450 ss <<
_(
"Experience modifier:") <<
" " <<
game.xp <<
"\n";
451 ss <<
_(
"Gold per village:") <<
" " <<
game.gold <<
"\n";
452 ss <<
_(
"Map size:") <<
" " <<
game.map_size_info <<
"\n";
453 ss <<
_(
"Reloaded:") <<
" " << yes_or_no(
game.reloaded) <<
"\n";
454 ss <<
_(
"Shared vision:") <<
" " <<
game.vision <<
"\n";
455 ss <<
_(
"Shuffle sides:") <<
" " << yes_or_no(
game.shuffle_sides) <<
"\n";
456 ss <<
_(
"Time limit:") <<
" " <<
game.time_limit <<
"\n";
457 ss <<
_(
"Use map settings:") <<
" " << yes_or_no(
game.use_map_settings);
461 if(!
game.have_era || !
game.have_all_mods || !
game.required_addons.empty()) {
462 info_icon.
set_label(
"icons/icon-info-error.png");
465 ss <<
_(
"One or more add-ons need to be installed\nin order to join this game.");
467 info_icon.
set_label(
"icons/icon-info.png");
477 if(
game.password_required) {
492 observer_icon.
set_label(
"misc/no_observer.png");
513 DBG_LB <<
"mp_lobby::update_gamelist_filter";
538 bool can_join =
false, can_observe =
false;
542 can_observe =
game.can_observe();
543 can_join =
game.can_join();
549 find_widget<button>(
"observe_global").set_active(can_observe);
550 find_widget<button>(
"join_global").set_active(can_join);
564 gamelistbox_ = find_widget<listbox>(
"game_list",
false,
true);
576 chatbox_ = find_widget<chatbox>(
"chat",
false,
true);
583 find_widget<button>(
"create").set_retval(
CREATE);
586 find_widget<button>(
"show_preferences"),
590 find_widget<button>(
"join_global"),
593 find_widget<button>(
"join_global").set_active(
false);
596 find_widget<button>(
"observe_global"),
600 find_widget<button>(
"server_info"),
603 find_widget<button>(
"observe_global").set_active(
false);
605 menu_button& replay_options = find_widget<menu_button>(
"replay_options");
618 filter_text_ = find_widget<text_box>(
"filter_text",
false,
true);
643 if(
panel* profile_panel = find_widget<panel>(
"profile",
false,
false)) {
645 [](
const auto& u) { return u.get_relation() == mp::user_info::user_relation::ME; });
648 profile_panel->find_widget<
label>(
"username").
set_label(your_info->name);
650 auto& profile_button = profile_panel->find_widget<
button>(
"view_profile");
653 auto& history_button = profile_panel->find_widget<
button>(
"view_match_history");
658 listbox& tab_bar = find_widget<listbox>(
"games_list_tab_bar");
720 LOG_LB <<
"caught wesnothd_error in network_handler: " <<
e.message;
750 if(
auto error =
data.optional_child(
"error")) {
752 }
else if(
data.has_child(
"gamelist")) {
754 }
else if(
auto gamelist_diff =
data.optional_child(
"gamelist_diff")) {
756 }
else if(
auto info =
data.optional_child(
"message")) {
757 if(
info[
"type"] ==
"server_info") {
760 }
else if(
info[
"type"] ==
"announcements") {
774 DBG_LB <<
"Received gamelist";
784 DBG_LB <<
"Received gamelist diff";
787 ERR_LB <<
"process_gamelist_diff failed!";
790 const int joined =
data.child_count(
"insert_child");
791 const int left =
data.child_count(
"remove_child");
792 if(joined > 0 || left > 0) {
805 if(!
game.can_join()) {
806 ERR_LB <<
"Attempted to join a game with no vacant slots";
812 if(!
game.can_observe()) {
813 ERR_LB <<
"Attempted to observe a game with observers disabled";
819 if(
game.can_join()) {
821 }
else if(
game.can_observe()) {
824 DBG_LB <<
"Cannot join or observe a game.";
843 if(
game.required_addons.empty()) {
844 gui2::show_error_message(
_(
"Something is wrong with the addon version check database supporting the multiplayer lobby. Please report this at https://bugs.wesnoth.org."));
848 if(!handle_addon_requirements_gui(
game.required_addons,
game.addons_outcome)) {
861 join_data[
"id"] = std::to_string(
game.id);
862 join_data[
"observe"] = try_obsv;
868 }
else if(!join_data.
empty() &&
game.password_required) {
869 std::string password;
871 if(!gui2::dialogs::mp_join_game_password_prompt::execute(password)) {
875 join_data[
"password"] = password;
889 }
catch(
const std::out_of_range&) {
891 ERR_LB <<
"Attempted to join/observe a game with index out of range: " <<
index <<
". "
901 ERR_LB <<
"Attempted to join/observe a game with an invalid id: " << game_id;
925 gui2::dialogs::preferences_dialog::display();
941 if(!info.match_string_filter(s)) {
950 return filter_friends_->get_widget_value() ?
info.has_friends ==
true :
true;
957 return filter_ignored_->get_widget_value() ==
false ?
info.has_ignored ==
false :
true;
961 return filter_slots_->get_widget_value() ?
info.vacant_slots > 0 :
true;
965 return info.auto_hosted == filter_auto_hosted_;
968 lobby_info_.set_game_filter_invert(
969 [
this](
bool val) {
return filter_invert_->get_widget_value() ? !val : val; });
974 if(key == SDLK_RETURN || key == SDLK_KP_ENTER) {
1005 const int value = find_widget<menu_button>(
"replay_options").get_value();
1007 prefs::get().set_blindfold_replay(value == 2);
std::map< std::string, chatroom_log > default_chat_log
A config object defines a single node in a WML file, with access to child nodes.
bool has_attribute(config_key_type key) const
config & add_child(config_key_type key)
lobby_chat_window * room_window_open(const std::string &room, const bool open_new, const bool allow_close=true)
Check if a room window for "room" is open, if open_new is true then it will be created if not found.
virtual void send_chat_message(const std::string &message, bool allies_only) override
Inherited form chat_handler.
lobby_chat_window * whisper_window_open(const std::string &name, bool open_new)
Check if a whisper window for user "name" is open, if open_new is true then it will be created if not...
void switch_to_window(lobby_chat_window *t)
Switch to the window given by a valid pointer (e.g.
void process_network_data(const ::config &data)
void set_active_window_changed_callback(const std::function< void(void)> &f)
void load_log(std::map< std::string, chatroom_log > &log, bool show_lobby)
void active_window_changed()
bool result_open_whisper() const
@ yes_no_buttons
Shows a yes and no button.
@ auto_close
Enables auto close.
Abstract base class for all modal dialogs.
bool show(const unsigned auto_close_time=0)
Shows the window.
void enter_selected_game(JOIN_MODE mode)
Enter game by index, where index is the selected game listbox row.
void show_help_callback()
void process_gamelist(const config &data)
void update_visible_games()
void update_selected_game()
virtual void post_show() override
Actions to be taken after the window has been shown.
mp::lobby_info & lobby_info_
lobby_player_list_helper player_list_
void skip_replay_changed_callback()
static std::string announcements_
bool delay_gamelist_update_
wesnothd_connection & network_connection_
void open_match_history()
void update_gamelist_filter()
bool gamelist_diff_update_
bool delay_playerlist_update_
void process_network_data(const config &data)
widget_data make_game_row_data(const mp::game_info &game)
void game_filter_keypress_callback(const SDL_Keycode key)
void enter_game_by_id(const int game_id, JOIN_MODE mode)
Entry wrapper for enter_game, where game is located by game id.
void update_gamelist_diff()
void show_preferences_button_callback()
static std::string server_information_
void enter_game(const mp::game_info &game, JOIN_MODE mode)
Exits the lobby and enters the given game.
void user_dialog_callback(const mp::user_info *info)
void tab_switch_callback()
mp_lobby(mp::lobby_info &info, wesnothd_connection &connection, int &joined_game)
void process_gamelist_diff(const config &data)
std::size_t lobby_update_timer_
Timer for updating the lobby.
virtual void pre_show() override
Actions to be taken before showing the window.
void adjust_game_row_contents(const mp::game_info &game, grid *grid, bool add_callbacks=true)
std::vector< int > gamelist_id_at_row_
std::chrono::steady_clock::time_point last_lobby_update_
void network_handler()
Network polling callback.
void enter_game_by_index(const int index, JOIN_MODE mode)
Entry wrapper for enter_game, where game is located by index.
static void display(const std::string &player_name, wesnothd_connection &connection, bool wait_for_response=true)
The display function.
std::unique_ptr< plugins_context > plugins_context_
static void display(const std::string &info, const std::string &announcements)
The display function.
void register_hotkey(const hotkey::HOTKEY_COMMAND id, const hotkey_function &function)
Registers a hotkey.
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
grid & add_row(const widget_item &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
bool select_row(const unsigned row, const bool select=true)
Selects a row.
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
void clear()
Removes all the rows in the listbox, clearing it.
int get_selected_row() const
Returns the first selected row.
unsigned get_item_count() const
Returns the number of items in the listbox.
const mp::user_info * get_selected_info() const
void update(const std::vector< mp::user_info > &user_info, int focused_game)
Updates the tree contents based on the given user data.
void set_map_data(const std::string &map_data)
std::string get_value() const
base class of top level items, the only item which needs to store the final canvases to draw on.
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
show_mode mode() const
Returns the dialog mode for this window.
void keyboard_capture(widget *widget)
void set_exit_hook(exit_hook mode, std::function< bool(window &)> func)
Sets the window's exit hook.
This class represents the collective information the client has about the players and games on the se...
void add_game_filter(game_filter_func func)
Adds a new filter function to be considered when apply_game_filter is called.
bool is_game_visible(const game_info &)
Returns whether the game would be visible after the game filters are applied.
void clear_game_filters()
Clears all game filter functions.
const config & gamelist() const
Returns the raw game list config data.
void process_gamelist(const config &data)
Process a full game list.
bool process_gamelist_diff(const config &data)
Process a gamelist diff.
std::function< void()> begin_state_sync()
Updates the game pointer list and returns a second stage cleanup function to be called after any acti...
game_info * get_game_by_id(int id)
Returns info on a game with the given game ID.
bool gamelist_initialized() const
const std::vector< game_info * > & games() const
void apply_game_filter()
Generates a new list of games that match the current filter functions and inversion setting.
const boost::dynamic_bitset & games_visibility() const
const std::vector< user_info > & users() const
Implements a quit confirmation dialog.
static bool quit()
Shows the quit confirmation if needed.
A class that represents a TCP/IP connection to the wesnothd server.
bool receive_data(config &result)
Receives the next pending data pack from the server, if available.
Networked add-ons (campaignd) client interface.
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed.
static std::string _(const char *str)
static lg::log_domain log_lobby("lobby")
bool ad_hoc_addon_fetch_session(const std::vector< std::string > &addon_ids)
Conducts an ad-hoc add-ons server connection to download an add-on with a particular id and all it's ...
bool open_object([[maybe_unused]] const std::string &path_or_url)
const color_t YELLOW_COLOR
const color_t TITLE_COLOR
const std::string unicode_bullet
std::chrono::milliseconds lobby_refresh
std::chrono::milliseconds lobby_network_timer
REGISTER_DIALOG(editor_edit_unit)
void connect_signal_pre_key_press(dispatcher &dispatcher, const signal_keyboard &signal)
Connects the signal for 'snooping' on the keypress.
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
void connect_signal_mouse_left_double_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button double click.
std::size_t add_timer(const std::chrono::milliseconds &interval, const std::function< void(std::size_t id)> &callback, const bool repeat)
Adds a new timer.
std::map< std::string, widget_item > widget_data
std::map< std::string, t_string > widget_item
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
bool remove_timer(const std::size_t id)
Removes a timer.
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.
void show_help(const std::string &show_topic)
Open the help browser, show topic with id show_topic.
Functions to load and save images from/to disk.
std::string italic(Args &&... data)
std::string span_size(const std::string &size, Args &&... data)
std::string tag(const std::string &tag_name, Args &&... contents)
std::string span_color(const color_t &color, Args &&... data)
void do_notify(notify_mode mode, const std::string &sender, const std::string &message)
void send_to_server(const config &data)
Attempts to send given data to server if a connection is open.
std::string get_profile_link(int user_id)
Gets the forum profile link for the given user.
bool logged_in_as_moderator()
Gets whether the currently logged-in user is a moderator.
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
std::vector< std::string > split(const config_attribute_value &val)
Desktop environment interaction functions.
The basic class for representing 8-bit RGB or RGBA colour values.
lobby_delay_gamelist_update_guard(mp_lobby &l)
~lobby_delay_gamelist_update_guard()
This class represents the info a client has about a game on the server.
This class represents the information a client has about another player.
An error occurred during when trying to communicate with the wesnothd server.
static map_location::direction s
Contains the gui2 timer routines.