diff --git a/overlay_experimental/overlay/steam_overlay.h b/overlay_experimental/overlay/steam_overlay.h index eff2d4b8..97aa3696 100644 --- a/overlay_experimental/overlay/steam_overlay.h +++ b/overlay_experimental/overlay/steam_overlay.h @@ -127,6 +127,8 @@ class Steam_Overlay std::vector notifications; std::recursive_mutex notifications_mutex; std::atomic have_notifications; + // used when the button "Invite all" is clicked + std::atomic invite_all_friends_clicked = false; bool overlay_state_changed; @@ -159,6 +161,9 @@ class Steam_Overlay void BuildFriendWindow(Friend const& frd, friend_window_state &state); // Notifications like achievements, chat and invitations void BuildNotifications(int width, int height); + // invite a single friend + void InviteFriend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking); + public: Steam_Overlay(Settings* settings, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking *network); diff --git a/overlay_experimental/overlay/steam_overlay_translations.h b/overlay_experimental/overlay/steam_overlay_translations.h index 97b8411e..3766bfe2 100644 --- a/overlay_experimental/overlay/steam_overlay_translations.h +++ b/overlay_experimental/overlay/steam_overlay_translations.h @@ -190,6 +190,98 @@ const char translationInvite[TRANSLATION_NUMBER_OF_LANGUAGES][TRANSLATION_BUFFER "Pozovi u igru" }; +const char translationInviteAll[TRANSLATION_NUMBER_OF_LANGUAGES][TRANSLATION_BUFFER_SIZE] = { + // 0 - English + "Invite all", + + // 1 - Arabic + "Invite all", + + // 2 - Bulgarian + "Invite all", + + // 3 - Simplified Chinese + "Invite all", + + // 4 - Traditional Chinese + "Invite all", + + // 5 - Czech + "Invite all", + + // 6 - Danish + "Invite all", + + // 7 - Dutch + "Invite all", + + // 8 - Finnish + "Invite all", + + // 9 - French + "Invite all", + + // 10 - German + "Invite all", + + // 11 - Greek + "Invite all", + + // 12 - Hungarian + "Invite all", + + // 13 - Italian + "Invite all", + + // 14 - Japanese + "Invite all", + + // 15 - Korean + "Invite all", + + // 16 - Norwegian + "Invite all", + + // 17 - Polish + "Invite all", + + // 18 - Portuguese + "Invite all", + + // 19 - Brazilian Portuguese + "Invite all", + + // 20 - Romanian + "Invite all", + + // 21 - Russian + "Invite all", + + // 22 - Spanish + "Invite all", + + // 23 - Latin American + "Invite all", + + // 24 - Swedish + "Invite all", + + // 25 - Thai + "Invite all", + + // 26 - Turkish + "Invite all", + + // 27 - Ukrainian + "Invite all", + + // 28 - Vietnamese + "Invite all", + + // 29 - Croatian + "Invite all", +}; + // C:\Program Files (x86)\Steam\tenfoot\resource\localization\tenfoot_*.txt // friends_chat_accept_game_invite const char translationJoin[TRANSLATION_NUMBER_OF_LANGUAGES][TRANSLATION_BUFFER_SIZE] = { diff --git a/overlay_experimental/steam_overlay.cpp b/overlay_experimental/steam_overlay.cpp index b56b7ad0..a31dc82d 100644 --- a/overlay_experimental/steam_overlay.cpp +++ b/overlay_experimental/steam_overlay.cpp @@ -1,8 +1,13 @@ +#ifdef EMU_OVERLAY + +// if you're wondering about text like: ##PopupAcceptInvite +// these are unique labels (keys) for each button/label/text,etc... +// ImGui uses the labels as keys, adding a suffic like "My Text##SomeKey" +// avoids confusing ImGui when another label has the same text "MyText" + #include "overlay/steam_overlay.h" #include "overlay/steam_overlay_translations.h" -#ifdef EMU_OVERLAY - #include #include #include @@ -69,9 +74,9 @@ int find_free_id(std::vector & ids, int base) return id > (base+max_window_id) ? 0 : id; } -int find_free_friend_id(std::map const& friend_windows) +int find_free_friend_id(const std::map &friend_windows) { - std::vector ids; + std::vector ids{}; ids.reserve(friend_windows.size()); std::for_each(friend_windows.begin(), friend_windows.end(), [&ids](std::pair const& i) @@ -84,7 +89,7 @@ int find_free_friend_id(std::map const int find_free_notification_id(std::vector const& notifications) { - std::vector ids; + std::vector ids{}; ids.reserve(notifications.size()); std::for_each(notifications.begin(), notifications.end(), [&ids](Notification const& i) @@ -584,37 +589,46 @@ void Steam_Overlay::BuildContextMenu(Friend const& frd, friend_window_state& sta { if (ImGui::BeginPopupContextItem("Friends_ContextMenu", 1)) { + // this is set to true if any button was clicked + // otherwise, after clicking any button, the menu will be persistent bool close_popup = false; + // user clicked on "chat" if (ImGui::Button(translationChat[current_language])) { - state.window_state |= window_state_show; close_popup = true; + state.window_state |= window_state_show; } // If we have the same appid, activate the invite/join buttons if (settings->get_local_game_id().AppID() == frd.appid()) { - std::string translationInvite_tmp; - std::string translationJoin_tmp; - translationInvite_tmp.append(translationInvite[current_language]); - translationInvite_tmp.append("##PopupInvite"); - translationJoin_tmp.append(translationJoin[current_language]); - translationJoin_tmp.append("##PopupInvite"); - + // user clicked on "invite to game" + std::string translationInvite_tmp(translationInvite[current_language]); + translationInvite_tmp.append("##PopupInviteToGame"); if (i_have_lobby && ImGui::Button(translationInvite_tmp.c_str())) { + close_popup = true; state.window_state |= window_state_invite; has_friend_action.push(frd); - close_popup = true; } + + // user clicked on "accept game invite" + std::string translationJoin_tmp(translationJoin[current_language]); + translationJoin_tmp.append("##PopupAcceptInvite"); + if (state.joinable && ImGui::Button(translationJoin_tmp.c_str())) { - state.window_state |= window_state_join; - has_friend_action.push(frd); close_popup = true; + // don't bother adding this friend if the button "invite all" was clicked + // we will send them the invitation later in Steam_Overlay::RunCallbacks() + if (!invite_all_friends_clicked) { + state.window_state |= window_state_join; + has_friend_action.push(frd); + } } } - if( close_popup) + + if (close_popup || invite_all_friends_clicked) { ImGui::CloseCurrentPopup(); } @@ -1047,7 +1061,6 @@ void Steam_Overlay::OverlayProc() } ImGui::SameLine(); - if (ImGui::Button(translationSettings[current_language])) { show_settings = true; } @@ -1060,6 +1073,14 @@ void Steam_Overlay::OverlayProc() std::lock_guard lock(overlay_mutex); if (!friends.empty()) { + if (i_have_lobby) { + std::string inviteAll(translationInviteAll[current_language]); + inviteAll.append("##PopupInviteAllFriends"); + if (ImGui::Button(inviteAll.c_str())) { // if btn clicked + invite_all_friends_clicked = true; + } + } + if (ImGui::ListBoxHeader("##label", friends.size())) { std::for_each(friends.begin(), friends.end(), [this](std::pair &i) @@ -1373,20 +1394,17 @@ void Steam_Overlay::RunCallbacks() while (!has_friend_action.empty()) { auto friend_info = friends.find(has_friend_action.front()); - if (friend_info != friends.end()) - { - uint64 friend_id = friend_info->first.id(); + if (friend_info != friends.end()) { + uint64 friend_id = (uint64)friend_info->first.id(); // The user clicked on "Send" - if (friend_info->second.window_state & window_state_send_message) - { + if (friend_info->second.window_state & window_state_send_message) { char* input = friend_info->second.chat_input; char* end_input = input + strlen(input); char* printable_char = std::find_if(input, end_input, [](char c) { return std::isgraph(c); }); // Check if the message contains something else than blanks - if (printable_char != end_input) - { + if (printable_char != end_input) { // Handle chat send Common_Message msg; Steam_Messages* steam_messages = new Steam_Messages; @@ -1400,26 +1418,20 @@ void Steam_Overlay::RunCallbacks() friend_info->second.chat_history.append(get_steam_client()->settings_client->get_local_name()).append(": ").append(input).append("\n", 1); } *input = 0; // Reset the input field + friend_info->second.window_state &= ~window_state_send_message; } - // The user clicked on "Invite" - if (friend_info->second.window_state & window_state_invite) - { - std::string connect = steamFriends->GetFriendRichPresence(settings->get_local_steam_id(), "connect"); - if (connect.length() > 0) - steamFriends->InviteUserToGame(friend_id, connect.c_str()); - else if (settings->get_lobby().IsValid()) - steamMatchmaking->InviteUserToLobby(settings->get_lobby(), friend_id); - + // The user clicked on "Invite" (but invite all wasn't clicked) + if (friend_info->second.window_state & window_state_invite) { + InviteFriend(friend_id, steamFriends, steamMatchmaking); + friend_info->second.window_state &= ~window_state_invite; } // The user clicked on "Join" - if (friend_info->second.window_state & window_state_join) - { + if (friend_info->second.window_state & window_state_join) { std::string connect = steamFriends->GetFriendRichPresence(friend_id, "connect"); // The user got a lobby invite and accepted it - if (friend_info->second.window_state & window_state_lobby_invite) - { + if (friend_info->second.window_state & window_state_lobby_invite) { GameLobbyJoinRequested_t data; data.m_steamIDLobby.SetFromUint64(friend_info->second.lobbyId); data.m_steamIDFriend.SetFromUint64(friend_id); @@ -1428,16 +1440,14 @@ void Steam_Overlay::RunCallbacks() friend_info->second.window_state &= ~window_state_lobby_invite; } else { // The user got a rich presence invite and accepted it - if (friend_info->second.window_state & window_state_rich_invite) - { + if (friend_info->second.window_state & window_state_rich_invite) { GameRichPresenceJoinRequested_t data = {}; data.m_steamIDFriend.SetFromUint64(friend_id); strncpy(data.m_rgchConnect, friend_info->second.connect, k_cchMaxRichPresenceValueLength - 1); callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); - + friend_info->second.window_state &= ~window_state_rich_invite; - } else if (connect.length() > 0) - { + } else if (connect.length() > 0) { GameRichPresenceJoinRequested_t data = {}; data.m_steamIDFriend.SetFromUint64(friend_id); strncpy(data.m_rgchConnect, connect.c_str(), k_cchMaxRichPresenceValueLength - 1); @@ -1455,12 +1465,37 @@ void Steam_Overlay::RunCallbacks() callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); } } - + friend_info->second.window_state &= ~window_state_join; } } has_friend_action.pop(); } + + // if variable == true, then set it to false and return true (because state was changed) in that case + bool true_tmp = true; + if (invite_all_friends_clicked.compare_exchange_weak(true_tmp, false)) { + PRINT_DEBUG("Overlay will send invitations to [%zu] friends if they're using the same app\n", friends.size()); + uint32 current_appid = settings->get_local_game_id().AppID(); + for (auto &fr : friends) { + if (fr.first.appid() == current_appid) { // friend is playing the same game + uint64 friend_id = (uint64)fr.first.id(); + InviteFriend(friend_id, steamFriends, steamMatchmaking); + } + } + } +} + +void Steam_Overlay::InviteFriend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking) +{ + std::string connect_str = steamFriends->GetFriendRichPresence(settings->get_local_steam_id(), "connect"); + if (connect_str.length() > 0) { + steamFriends->InviteUserToGame(friend_id, connect_str.c_str()); + PRINT_DEBUG("Overlay sent game invitation to friend with id = %llu\n", friend_id); + } else if (settings->get_lobby().IsValid()) { + steamMatchmaking->InviteUserToLobby(settings->get_lobby(), friend_id); + PRINT_DEBUG("Overlay sent lobby invitation to friend with id = %llu\n", friend_id); + } } #endif