add a button to invite all friends playing same game

This commit is contained in:
otavepto 2024-01-22 01:31:02 +02:00
parent d73faeff38
commit 9eee49d6af
3 changed files with 176 additions and 44 deletions

View File

@ -127,6 +127,8 @@ class Steam_Overlay
std::vector<Notification> notifications; std::vector<Notification> notifications;
std::recursive_mutex notifications_mutex; std::recursive_mutex notifications_mutex;
std::atomic<bool> have_notifications; std::atomic<bool> have_notifications;
// used when the button "Invite all" is clicked
std::atomic<bool> invite_all_friends_clicked = false;
bool overlay_state_changed; bool overlay_state_changed;
@ -159,6 +161,9 @@ class Steam_Overlay
void BuildFriendWindow(Friend const& frd, friend_window_state &state); void BuildFriendWindow(Friend const& frd, friend_window_state &state);
// Notifications like achievements, chat and invitations // Notifications like achievements, chat and invitations
void BuildNotifications(int width, int height); void BuildNotifications(int width, int height);
// invite a single friend
void InviteFriend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking);
public: public:
Steam_Overlay(Settings* settings, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking *network); Steam_Overlay(Settings* settings, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking *network);

View File

@ -190,6 +190,98 @@ const char translationInvite[TRANSLATION_NUMBER_OF_LANGUAGES][TRANSLATION_BUFFER
"Pozovi u igru" "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 // C:\Program Files (x86)\Steam\tenfoot\resource\localization\tenfoot_*.txt
// friends_chat_accept_game_invite // friends_chat_accept_game_invite
const char translationJoin[TRANSLATION_NUMBER_OF_LANGUAGES][TRANSLATION_BUFFER_SIZE] = { const char translationJoin[TRANSLATION_NUMBER_OF_LANGUAGES][TRANSLATION_BUFFER_SIZE] = {

View File

@ -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.h"
#include "overlay/steam_overlay_translations.h" #include "overlay/steam_overlay_translations.h"
#ifdef EMU_OVERLAY
#include <thread> #include <thread>
#include <string> #include <string>
#include <sstream> #include <sstream>
@ -69,9 +74,9 @@ int find_free_id(std::vector<int> & ids, int base)
return id > (base+max_window_id) ? 0 : id; return id > (base+max_window_id) ? 0 : id;
} }
int find_free_friend_id(std::map<Friend, friend_window_state, Friend_Less> const& friend_windows) int find_free_friend_id(const std::map<Friend, friend_window_state, Friend_Less> &friend_windows)
{ {
std::vector<int> ids; std::vector<int> ids{};
ids.reserve(friend_windows.size()); ids.reserve(friend_windows.size());
std::for_each(friend_windows.begin(), friend_windows.end(), [&ids](std::pair<Friend const, friend_window_state> const& i) std::for_each(friend_windows.begin(), friend_windows.end(), [&ids](std::pair<Friend const, friend_window_state> const& i)
@ -84,7 +89,7 @@ int find_free_friend_id(std::map<Friend, friend_window_state, Friend_Less> const
int find_free_notification_id(std::vector<Notification> const& notifications) int find_free_notification_id(std::vector<Notification> const& notifications)
{ {
std::vector<int> ids; std::vector<int> ids{};
ids.reserve(notifications.size()); ids.reserve(notifications.size());
std::for_each(notifications.begin(), notifications.end(), [&ids](Notification const& i) 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)) 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; bool close_popup = false;
// user clicked on "chat"
if (ImGui::Button(translationChat[current_language])) if (ImGui::Button(translationChat[current_language]))
{ {
state.window_state |= window_state_show;
close_popup = true; close_popup = true;
state.window_state |= window_state_show;
} }
// If we have the same appid, activate the invite/join buttons // If we have the same appid, activate the invite/join buttons
if (settings->get_local_game_id().AppID() == frd.appid()) if (settings->get_local_game_id().AppID() == frd.appid())
{ {
std::string translationInvite_tmp; // user clicked on "invite to game"
std::string translationJoin_tmp; std::string translationInvite_tmp(translationInvite[current_language]);
translationInvite_tmp.append(translationInvite[current_language]); translationInvite_tmp.append("##PopupInviteToGame");
translationInvite_tmp.append("##PopupInvite");
translationJoin_tmp.append(translationJoin[current_language]);
translationJoin_tmp.append("##PopupInvite");
if (i_have_lobby && ImGui::Button(translationInvite_tmp.c_str())) if (i_have_lobby && ImGui::Button(translationInvite_tmp.c_str()))
{ {
close_popup = true;
state.window_state |= window_state_invite; state.window_state |= window_state_invite;
has_friend_action.push(frd); 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())) if (state.joinable && ImGui::Button(translationJoin_tmp.c_str()))
{ {
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; state.window_state |= window_state_join;
has_friend_action.push(frd); has_friend_action.push(frd);
close_popup = true;
} }
} }
if( close_popup) }
if (close_popup || invite_all_friends_clicked)
{ {
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
@ -1047,7 +1061,6 @@ void Steam_Overlay::OverlayProc()
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(translationSettings[current_language])) { if (ImGui::Button(translationSettings[current_language])) {
show_settings = true; show_settings = true;
} }
@ -1060,6 +1073,14 @@ void Steam_Overlay::OverlayProc()
std::lock_guard<std::recursive_mutex> lock(overlay_mutex); std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
if (!friends.empty()) 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())) if (ImGui::ListBoxHeader("##label", friends.size()))
{ {
std::for_each(friends.begin(), friends.end(), [this](std::pair<Friend const, friend_window_state> &i) std::for_each(friends.begin(), friends.end(), [this](std::pair<Friend const, friend_window_state> &i)
@ -1373,20 +1394,17 @@ void Steam_Overlay::RunCallbacks()
while (!has_friend_action.empty()) while (!has_friend_action.empty())
{ {
auto friend_info = friends.find(has_friend_action.front()); auto friend_info = friends.find(has_friend_action.front());
if (friend_info != friends.end()) if (friend_info != friends.end()) {
{ uint64 friend_id = (uint64)friend_info->first.id();
uint64 friend_id = friend_info->first.id();
// The user clicked on "Send" // 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* input = friend_info->second.chat_input;
char* end_input = input + strlen(input); char* end_input = input + strlen(input);
char* printable_char = std::find_if(input, end_input, [](char c) { char* printable_char = std::find_if(input, end_input, [](char c) {
return std::isgraph(c); return std::isgraph(c);
}); });
// Check if the message contains something else than blanks // Check if the message contains something else than blanks
if (printable_char != end_input) if (printable_char != end_input) {
{
// Handle chat send // Handle chat send
Common_Message msg; Common_Message msg;
Steam_Messages* steam_messages = new Steam_Messages; 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); 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 *input = 0; // Reset the input field
friend_info->second.window_state &= ~window_state_send_message; friend_info->second.window_state &= ~window_state_send_message;
} }
// The user clicked on "Invite" // The user clicked on "Invite" (but invite all wasn't clicked)
if (friend_info->second.window_state & window_state_invite) if (friend_info->second.window_state & window_state_invite) {
{ InviteFriend(friend_id, steamFriends, steamMatchmaking);
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);
friend_info->second.window_state &= ~window_state_invite; friend_info->second.window_state &= ~window_state_invite;
} }
// The user clicked on "Join" // 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"); std::string connect = steamFriends->GetFriendRichPresence(friend_id, "connect");
// The user got a lobby invite and accepted it // 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; GameLobbyJoinRequested_t data;
data.m_steamIDLobby.SetFromUint64(friend_info->second.lobbyId); data.m_steamIDLobby.SetFromUint64(friend_info->second.lobbyId);
data.m_steamIDFriend.SetFromUint64(friend_id); data.m_steamIDFriend.SetFromUint64(friend_id);
@ -1428,16 +1440,14 @@ void Steam_Overlay::RunCallbacks()
friend_info->second.window_state &= ~window_state_lobby_invite; friend_info->second.window_state &= ~window_state_lobby_invite;
} else { } else {
// The user got a rich presence invite and accepted it // 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 = {}; GameRichPresenceJoinRequested_t data = {};
data.m_steamIDFriend.SetFromUint64(friend_id); data.m_steamIDFriend.SetFromUint64(friend_id);
strncpy(data.m_rgchConnect, friend_info->second.connect, k_cchMaxRichPresenceValueLength - 1); strncpy(data.m_rgchConnect, friend_info->second.connect, k_cchMaxRichPresenceValueLength - 1);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
friend_info->second.window_state &= ~window_state_rich_invite; friend_info->second.window_state &= ~window_state_rich_invite;
} else if (connect.length() > 0) } else if (connect.length() > 0) {
{
GameRichPresenceJoinRequested_t data = {}; GameRichPresenceJoinRequested_t data = {};
data.m_steamIDFriend.SetFromUint64(friend_id); data.m_steamIDFriend.SetFromUint64(friend_id);
strncpy(data.m_rgchConnect, connect.c_str(), k_cchMaxRichPresenceValueLength - 1); strncpy(data.m_rgchConnect, connect.c_str(), k_cchMaxRichPresenceValueLength - 1);
@ -1461,6 +1471,31 @@ void Steam_Overlay::RunCallbacks()
} }
has_friend_action.pop(); 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 #endif