* allow setting the achievements notifications via config file

* fixed a mistake when discarding ut8 bom marker
This commit is contained in:
otavepto 2024-01-26 05:49:03 +02:00
parent 7e595ce1d9
commit 1bba2c09b0
8 changed files with 250 additions and 85 deletions

View File

@ -1,4 +1,12 @@
* **[Detanup01]** added a new command line option for the tool `generate_emu_config` to disable the generation of `disable_xxx.txt` files
* **[Detanup01]** added a new command line option for the tool `generate_emu_config` to disable the generation of `disable_xxx.txt` files,
suggested by **[Vlxst]**
* added new settings to the overlay which allow specifying the notifications positions, check the example file `overlay_appearance.EXAMPLE.txt`
* fixed a mistake when discarding the utf8 bom marker
---
## 2024/1/25
* added new options to the overlay to allow copying a friend's ID, plus current player ID, suggested by **[Vlxst]**
* added a new option to the overlay to invite all friends playing the same game, suggested by **[Vlxst]**
* added new `auto_accept_invite.txt` setting to automatically accept game/lobby invites from this list, each SteamID64 on a separate line

View File

@ -166,6 +166,8 @@ static inline void reset_LastError()
#include "utfcpp/utf8.h"
#include "controller/gamepad.h"
constexpr const char * const whitespaces = " \t\r\n";
// common includes
#include "common_helpers/common_helpers.hpp"
@ -247,12 +249,15 @@ static std::string uint8_vector_to_hex_string(std::vector<uint8_t>& v)
static inline void consume_bom(std::ifstream &input)
{
int bom[3];
if (!input.is_open()) return;
auto pos = input.tellg();
int bom[3]{};
bom[0] = input.get();
bom[1] = input.get();
bom[2] = input.get();
if (bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF) {
input.seekg(-3, std::ios::cur);
input.seekg(pos, std::ios::beg);
}
}
@ -281,6 +286,4 @@ static inline void consume_bom(std::ifstream &input)
#define LOBBY_CONNECT_APPID ((uint32)-2)
constexpr const char * const whitespaces = " \t\r\n";
#endif//__INCLUDED_COMMON_INCLUDES__

View File

@ -111,6 +111,13 @@ struct Group_Clans {
};
struct Overlay_Appearance {
enum NotificationPosition {
top_left, top_center, top_right,
bot_left, bot_center, bot_right,
};
constexpr const static NotificationPosition default_pos = NotificationPosition::top_right;
float font_size = 16.0;
float icon_size = 64.0;
float notification_r = 0.16;
@ -133,6 +140,23 @@ struct Overlay_Appearance {
float element_active_g = -1.0;
float element_active_b = -1.0;
float element_active_a = -1.0;
NotificationPosition ach_earned_pos = default_pos; // achievement earned
NotificationPosition invite_pos = default_pos; // lobby/game invitation
NotificationPosition chat_msg_pos = default_pos; // chat message from a friend
static NotificationPosition translate_notification_position(const std::string &str)
{
if (str == "top_left") return NotificationPosition::top_left;
else if (str == "top_center") return NotificationPosition::top_center;
else if (str == "top_right") return NotificationPosition::top_right;
else if (str == "bot_left") return NotificationPosition::bot_left;
else if (str == "bot_center") return NotificationPosition::bot_center;
else if (str == "bot_right") return NotificationPosition::bot_right;
PRINT_DEBUG("Invalid position '%s'\n", str.c_str());
return default_pos;
}
};
class Settings {
@ -261,7 +285,7 @@ public:
bool disable_overlay_warning_bad_appid = false;
// disable all overlay warnings
bool disable_overlay_warning_any = false;
Overlay_Appearance overlay_appearance;
Overlay_Appearance overlay_appearance{};
//app build id
int build_id = 10;

View File

@ -35,8 +35,8 @@ static void load_subscribed_groups_clans(std::string clans_filepath, Settings *s
{
PRINT_DEBUG("Group clans file path: %s\n", clans_filepath.c_str());
std::ifstream clans_file(utf8_decode(clans_filepath));
consume_bom(clans_file);
if (clans_file.is_open()) {
consume_bom(clans_file);
std::string line;
while (std::getline(clans_file, line)) {
if (line.length() < 0) continue;
@ -73,22 +73,35 @@ static void load_subscribed_groups_clans(std::string clans_filepath, Settings *s
static void load_overlay_appearance(std::string appearance_filepath, Settings *settings_client, Settings *settings_server)
{
PRINT_DEBUG("Overlay appearance file path: %s\n", appearance_filepath.c_str());
std::ifstream appearance_file(utf8_decode(appearance_filepath));
consume_bom(appearance_file);
if (appearance_file.is_open()) {
std::string line;
PRINT_DEBUG("Parsing overlay appearance file: '%s'\n", appearance_filepath.c_str());
consume_bom(appearance_file);
std::string line{};
while (std::getline(appearance_file, line)) {
if (line.length() < 0) continue;
if (line.length() <= 0) continue;
std::size_t seperator = line.find(" ");
std::string name;
std::string value;
std::string name{};
std::string value{};
if (seperator != std::string::npos) {
name = line.substr(0, seperator);
name.erase(0, name.find_first_not_of(whitespaces));
name.erase(name.find_last_not_of(whitespaces) + 1);
value = line.substr(seperator);
value.erase(0, value.find_first_not_of(whitespaces));
value.erase(value.find_last_not_of(whitespaces) + 1);
}
// comments
if (name.size() && (
name[0] == '#' || name[0] == ';' || name.compare(0, 2, "//") == 0
)) {
continue;
}
PRINT_DEBUG(" Overlay appearance line '%s' = '%s'\n", name.c_str(), value.c_str());
try {
if (name.compare("Font_Size") == 0) {
float nfont_size = std::stof(value, NULL);
@ -178,9 +191,21 @@ static void load_overlay_appearance(std::string appearance_filepath, Settings *s
float nelement_active_a = std::stof(value, NULL);
settings_client->overlay_appearance.element_active_a = nelement_active_a;
settings_server->overlay_appearance.element_active_a = nelement_active_a;
} else if (name.compare("PosAchievement") == 0) {
auto pos = Overlay_Appearance::translate_notification_position(value);
settings_client->overlay_appearance.ach_earned_pos = pos;
settings_server->overlay_appearance.ach_earned_pos = pos;
} else if (name.compare("PosInvitation") == 0) {
auto pos = Overlay_Appearance::translate_notification_position(value);
settings_client->overlay_appearance.invite_pos = pos;
settings_server->overlay_appearance.invite_pos = pos;
} else if (name.compare("PosChatMsg") == 0) {
auto pos = Overlay_Appearance::translate_notification_position(value);
settings_client->overlay_appearance.chat_msg_pos = pos;
settings_server->overlay_appearance.chat_msg_pos = pos;
}
PRINT_DEBUG("Overlay appearance %s %s\n", name.c_str(), value.c_str());
} catch (...) {}
} catch (...) { }
}
}
}

View File

@ -53,8 +53,8 @@ enum notification_type
struct Notification
{
static constexpr float width = 0.25;
static constexpr float height = 5.0;
static constexpr float width_percent = 0.25f; // percentage from total width
static constexpr float height = 5.0f;
static constexpr std::chrono::milliseconds fade_in = std::chrono::milliseconds(2000);
static constexpr std::chrono::milliseconds fade_out = std::chrono::milliseconds(2000);
static constexpr std::chrono::milliseconds show_time = std::chrono::milliseconds(6000) + fade_in + fade_out;
@ -85,6 +85,12 @@ struct Overlay_Achievement
#ifdef EMU_OVERLAY
#include <future>
#include "Renderer_Hook.h"
struct NotificationsIndexes {
int top_left = 0, top_center = 0, top_right = 0;
int bot_left = 0, bot_center = 0, bot_right = 0;
};
class Steam_Overlay
{
Settings* settings;
@ -153,8 +159,8 @@ class Steam_Overlay
bool FriendJoinable(std::pair<const Friend, friend_window_state> &f);
bool IHaveLobby();
void NotifyUser(friend_window_state& friend_state);
void NotifyUserAchievement();
void NotifySoundUserInvite(friend_window_state& friend_state);
void NotifySoundUserAchievement();
void NotifySoundAutoAcceptFriendInvite();
// Right click on friend
@ -162,6 +168,7 @@ class Steam_Overlay
// Double click on friend
void BuildFriendWindow(Friend const& frd, friend_window_state &state);
// Notifications like achievements, chat and invitations
void SetNextNotificationPos(float width, float height, float font_size, notification_type type, struct NotificationsIndexes &idx);
void BuildNotifications(int width, int height);
// invite a single friend
void InviteFriend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking);

View File

@ -336,7 +336,7 @@ void Steam_Overlay::ShowOverlay(bool state)
overlay_state_changed = true;
}
void Steam_Overlay::NotifyUser(friend_window_state& friend_state)
void Steam_Overlay::NotifySoundUserInvite(friend_window_state& friend_state)
{
if (settings->disable_overlay_friend_notification) return;
if (!(friend_state.window_state & window_state_show) || !show_overlay)
@ -352,7 +352,7 @@ void Steam_Overlay::NotifyUser(friend_window_state& friend_state)
}
}
void Steam_Overlay::NotifyUserAchievement()
void Steam_Overlay::NotifySoundUserAchievement()
{
if (settings->disable_overlay_achievement_notification) return;
if (!show_overlay)
@ -391,7 +391,7 @@ void Steam_Overlay::SetLobbyInvite(Friend friendId, uint64 lobbyId)
// Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows)
frd.window_state &= ~window_state_rich_invite;
AddInviteNotification(*i);
NotifyUser(i->second);
NotifySoundUserInvite(i->second);
}
}
@ -410,7 +410,7 @@ void Steam_Overlay::SetRichInvite(Friend friendId, const char* connect_str)
// Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows)
frd.window_state &= ~window_state_lobby_invite;
AddInviteNotification(*i);
NotifyUser(i->second);
NotifySoundUserInvite(i->second);
}
}
@ -487,7 +487,7 @@ void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach)
notif.message = ach["displayName"].get<std::string>() + "\n" + ach["description"].get<std::string>();
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
NotifyUserAchievement();
NotifySoundUserAchievement();
have_notifications = true;
}
else
@ -744,85 +744,142 @@ void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& st
ImFont *font_default;
ImFont *font_notif;
// set the position of the next notification
void Steam_Overlay::SetNextNotificationPos(float width, float height, float font_size, notification_type type, struct NotificationsIndexes &idx)
{
// 0 on the y-axis is top, 0 on the x-axis is left
// get the required position
Overlay_Appearance::NotificationPosition pos = Overlay_Appearance::default_pos;
switch (type) {
case notification_type::notification_type_achievement: pos = settings->overlay_appearance.ach_earned_pos; break;
case notification_type::notification_type_invite: pos = settings->overlay_appearance.invite_pos; break;
case notification_type::notification_type_message: pos = settings->overlay_appearance.chat_msg_pos; break;
default: /* satisfy compiler warning */ break;
}
float x = 0.0f;
float y = 0.0f;
const float noti_width = width * Notification::width_percent;
const float noti_height = Notification::height * font_size;
switch (pos) {
// top
case Overlay_Appearance::NotificationPosition::top_left:
x = 0.0f;
y = noti_height * idx.top_left;
++idx.top_left;
break;
case Overlay_Appearance::NotificationPosition::top_center:
x = (width / 2) - (noti_width / 2);
y = noti_height * idx.top_center;
++idx.top_center;
break;
case Overlay_Appearance::NotificationPosition::top_right:
x = width - noti_width;
y = noti_height * idx.top_right;
++idx.top_right;
break;
// bot
case Overlay_Appearance::NotificationPosition::bot_left:
x = 0.0f;
y = height - noti_height * (idx.bot_left + 1);
++idx.bot_left;
break;
case Overlay_Appearance::NotificationPosition::bot_center:
x = (width / 2) - (noti_width / 2);
y = height - noti_height * (idx.bot_center + 1);
++idx.bot_center;
break;
case Overlay_Appearance::NotificationPosition::bot_right:
x = width - noti_width;
y = height - noti_height * (idx.bot_right + 1);
++idx.bot_right;
break;
default: /* satisfy compiler warning */ break;
}
ImGui::SetNextWindowPos(ImVec2( x, y ));
}
void Steam_Overlay::BuildNotifications(int width, int height)
{
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
int i = 0;
int font_size = ImGui::GetFontSize();
float font_size = ImGui::GetFontSize();
std::queue<Friend> friend_actions_temp;
std::queue<Friend> friend_actions_temp{};
{
std::lock_guard<std::recursive_mutex> lock(notifications_mutex);
for (auto it = notifications.begin(); it != notifications.end(); ++it, ++i)
NotificationsIndexes idx{};
for (auto it = notifications.begin(); it != notifications.end(); ++it)
{
auto elapsed_notif = now - it->start_time;
if ( elapsed_notif < Notification::fade_in)
{
if ( elapsed_notif < Notification::fade_in) {
float alpha = settings->overlay_appearance.notification_a * (elapsed_notif.count() / static_cast<float>(Notification::fade_in.count()));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(settings->overlay_appearance.notification_r, settings->overlay_appearance.notification_g, settings->overlay_appearance.notification_b, alpha));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2));
}
else if ( elapsed_notif > Notification::fade_out_start)
{
else if ( elapsed_notif > Notification::fade_out_start) {
float alpha = settings->overlay_appearance.notification_a * ((Notification::show_time - elapsed_notif).count() / static_cast<float>(Notification::fade_out.count()));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(settings->overlay_appearance.notification_r, settings->overlay_appearance.notification_g, settings->overlay_appearance.notification_b, alpha));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2));
}
else
{
} else {
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, settings->overlay_appearance.notification_a));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(settings->overlay_appearance.notification_r, settings->overlay_appearance.notification_g, settings->overlay_appearance.notification_b, settings->overlay_appearance.notification_a));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, settings->overlay_appearance.notification_a*2));
}
ImGui::SetNextWindowPos(ImVec2((float)width - width * Notification::width, Notification::height * font_size * i ));
ImGui::SetNextWindowSize(ImVec2( width * Notification::width, Notification::height * font_size ));
ImGui::Begin(std::to_string(it->id).c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus |
SetNextNotificationPos(width, height, font_size, (notification_type)it->type, idx);
ImGui::SetNextWindowSize(ImVec2( width * Notification::width_percent, Notification::height * font_size ));
std::string wnd_name = "NotiPopupShow" + std::to_string(it->id);
ImGui::Begin(wnd_name.c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoDecoration);
switch (it->type)
{
switch (it->type) {
case notification_type_achievement:
{
if (!it->icon.expired()) {
ImGui::BeginTable("imgui_table", 2);
ImGui::TableSetupColumn("imgui_table_image", ImGuiTableColumnFlags_WidthFixed, settings->overlay_appearance.icon_size);
ImGui::TableSetupColumn("imgui_table_text");
ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size);
{
if (!it->icon.expired()) {
ImGui::BeginTable("imgui_table", 2);
ImGui::TableSetupColumn("imgui_table_image", ImGuiTableColumnFlags_WidthFixed, settings->overlay_appearance.icon_size);
ImGui::TableSetupColumn("imgui_table_text");
ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size);
ImGui::TableSetColumnIndex(0);
ImGui::Image((ImU64)*it->icon.lock().get(), ImVec2(settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size));
ImGui::TableSetColumnIndex(0);
ImGui::Image((ImU64)*it->icon.lock().get(), ImVec2(settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size));
ImGui::TableSetColumnIndex(1);
ImGui::TextWrapped("%s", it->message.c_str());
ImGui::EndTable();
} else {
ImGui::TextWrapped("%s", it->message.c_str());
}
}
break;
case notification_type_invite:
{
ImGui::TableSetColumnIndex(1);
ImGui::TextWrapped("%s", it->message.c_str());
ImGui::EndTable();
} else {
ImGui::TextWrapped("%s", it->message.c_str());
if (ImGui::Button(translationJoin[current_language]))
{
it->frd->second.window_state |= window_state_join;
friend_actions_temp.push(it->frd->first);
it->start_time = std::chrono::seconds(0);
}
}
break;
}
break;
case notification_type_invite:
{
ImGui::TextWrapped("%s", it->message.c_str());
if (ImGui::Button(translationJoin[current_language]))
{
it->frd->second.window_state |= window_state_join;
friend_actions_temp.push(it->frd->first);
it->start_time = std::chrono::seconds(0);
}
}
break;
case notification_type_message:
ImGui::TextWrapped("%s", it->message.c_str()); break;
ImGui::TextWrapped("%s", it->message.c_str());
break;
case notification_type_auto_accept_invite:
ImGui::TextWrapped("%s", it->message.c_str()); break;
ImGui::TextWrapped("%s", it->message.c_str());
break;
}
ImGui::End();
@ -1308,7 +1365,7 @@ void Steam_Overlay::Callback(Common_Message *msg)
}
AddMessageNotification(friend_info->first.name() + ": " + steam_message.message());
NotifyUser(friend_info->second);
NotifySoundUserInvite(friend_info->second);
}
}
}

View File

@ -422,6 +422,7 @@ Second, the project is not a malware, if your antivirus software complains, be s
---
## Overlay warnings:
**Note: at the moment this feature is only enabled in the windows experimental builds**
These configuration files allow disabling various overlay warnings:
* `disable_overlay_warning_forced_setting.txt`:
@ -435,6 +436,22 @@ Check the exaxmple files in the `steam_settings` folder
---
## Overlay appearance:
**Note: at the moment this feature is only enabled in the windows experimental builds**
These configuration file `overlay_appearance.txt` has various options to set for the overlay appearance.
The notifications positions could be set to one of these values:
* `top_left`
* `top_center`
* `top_right`
* `bot_left`
* `bot_center`
* `bot_right`
Check the exaxmple files in the `steam_settings` folder
---
## Auto accept game/lobby invites:
When the overlay is enabled and working, you can bypass it and auto-accept invites (lobby or game) from a list of Steam IDs (SteamID64 format).

View File

@ -1,22 +1,46 @@
Font_Size 16.0
Icon_Size 64.0
Notification_R 0.16
Notification_G 0.29
Notification_B 0.48
Notification_A 1.0
Background_R 0.0
Background_G 0.0
Background_B 0.1
Background_A 0.5
Element_R 0.0
Element_G 0.1
Element_B 0.0
Element_A 1.0
ElementHovered_R 0.0
ElementHovered_G 0.5
ElementHovered_B 0.0
ElementHovered_A 1.0
ElementActive_R 0.0
ElementActive_G 0.75
ElementActive_B 0.0
ElementActive_A 1.0
Background_R -1.0
Background_G -1.0
Background_B -1.0
Background_A -1.0
Element_R -1.0
Element_G -1.0
Element_B -1.0
Element_A -1.0
ElementHovered_R -1.0
ElementHovered_G -1.0
ElementHovered_B -1.0
ElementHovered_A -1.0
ElementActive_R -1.0
ElementActive_G -1.0
ElementActive_B -1.0
ElementActive_A -1.0
; available options:
; top_left
; top_center
; top_right
; bot_left
; bot_center
; bot_right
; position of achievements
PosAchievement top_right
; position of invitations
PosInvitation top_right
; position of chat messages
PosChatMsg top_right