Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/emu-build-all-win.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:

for %%P in (api_regular api_experimental steamclient_experimental_stub steamclient_experimental steamclient_experimental_loader steamclient_experimental_extra lib_game_overlay_renderer lib_steamnetworkingsockets tool_lobby_connect tool_generate_interfaces) do (
for %%A in (x64 Win32) do (
echo === Building %%P (%%A, release) ===
echo === Building %%P [%%A release] ===
msbuild /nologo /t:%%P /m /v:m /p:Configuration=release,Platform=%%A gbe.sln
if errorlevel 1 echo Warning: %%P %%A release build failed
)
Expand Down
6 changes: 3 additions & 3 deletions dll/base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ std::recursive_mutex global_mutex{};
extern const std::chrono::time_point<std::chrono::high_resolution_clock> startup_counter = std::chrono::high_resolution_clock::now();
extern const std::chrono::time_point<std::chrono::system_clock> startup_time = std::chrono::system_clock::now();

#ifndef EMU_RELEASE_BUILD
dbg_log dbg_logger(get_full_program_path() + "STEAM_LOG_" + std::to_string(common_helpers::rand_number(UINT32_MAX)) + ".log");
#endif
// NOTE: the global logger now lives behind the inline accessor dbg_logger_get() in
// dbg_log.hpp (so every project that uses PRINT_DEBUG gets it without linking this
// file). It is no longer defined here.


#ifdef __WINDOWS__
Expand Down
56 changes: 29 additions & 27 deletions dll/dll/common_includes.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,36 +166,38 @@ static inline void reset_LastError()
#endif

// PRINT_DEBUG definition
#ifndef EMU_RELEASE_BUILD
#include "dbg_log/dbg_log.hpp"
// we need this for printf specifiers for intptr_t such as PRIdPTR
#include <inttypes.h>

#if defined(__WINDOWS__)
#define PRINT_DEBUG_TID() (long long)GetCurrentThreadId()
#define PRINT_DEBUG_CLEANUP() WSASetLastError(0)
#elif defined(__LINUX__)
#include <sys/syscall.h> // syscall

#define PRINT_DEBUG_TID() (long long)syscall(SYS_gettid)
#define PRINT_DEBUG_CLEANUP() (void)0
#else
#warning "Unrecognized OS"

#define PRINT_DEBUG_TID() (long long)0
#define PRINT_DEBUG_CLEANUP() (void)0
#endif
// Logging is compiled into release builds too, but only ACTIVATES at runtime when
// the GSE_FORCE_LOG env var is set (see dbg_log::is_active). This lets us observe a
// shipped (release) emulator — e.g. LAN lobby matchmaking — without a debug build.
// When inactive, PRINT_DEBUG is a single cached bool check: no formatting and, in
// particular, NO PRINT_DEBUG_CLEANUP (WSASetLastError(0) would otherwise clobber the
// game's last socket error on every call). Debug builds: always active.
#include "dbg_log/dbg_log.hpp"
// we need this for printf specifiers for intptr_t such as PRIdPTR
#include <inttypes.h>

#if defined(__WINDOWS__)
#define PRINT_DEBUG_TID() (long long)GetCurrentThreadId()
#define PRINT_DEBUG_CLEANUP() WSASetLastError(0)
#elif defined(__LINUX__)
#include <sys/syscall.h> // syscall

extern dbg_log dbg_logger;
#define PRINT_DEBUG_TID() (long long)syscall(SYS_gettid)
#define PRINT_DEBUG_CLEANUP() (void)0
#else
#warning "Unrecognized OS"

#define PRINT_DEBUG(a, ...) do { \
dbg_logger.write("[tid %lld] %s " a, PRINT_DEBUG_TID(), EMU_FUNC_NAME, ##__VA_ARGS__); \
PRINT_DEBUG_CLEANUP(); \
} while (0)
#define PRINT_DEBUG_TID() (long long)0
#define PRINT_DEBUG_CLEANUP() (void)0
#endif

#else // EMU_RELEASE_BUILD
#define PRINT_DEBUG(...)
#endif // EMU_RELEASE_BUILD
#define PRINT_DEBUG(a, ...) do { \
dbg_log& _gse_dbg_log = dbg_logger_get(); \
if (_gse_dbg_log.is_active()) { \
_gse_dbg_log.write("[tid %lld] %s " a, PRINT_DEBUG_TID(), EMU_FUNC_NAME, ##__VA_ARGS__); \
PRINT_DEBUG_CLEANUP(); \
} \
} while (0)

// function entry
#define PRINT_DEBUG_ENTRY() PRINT_DEBUG("")
Expand Down
4 changes: 4 additions & 0 deletions dll/dll/steam_friends.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public ISteamFriends
std::map<std::string, std::string> reg{};
std::string reg_nullptr{};

// stable backing store for the auto-derived "connect" rich-presence string
// (see get_friend_rich_presence_silent)
std::string rich_presence_connect{};

Friend *find_friend(CSteamID id);

void persona_change(CSteamID id, EPersonaChange flags);
Expand Down
22 changes: 20 additions & 2 deletions dll/steam_friends.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1075,9 +1075,27 @@ const char* Steam_Friends::get_friend_rich_presence_silent( CSteamID steamIDFrie
f = find_friend(steamIDFriend);
}

if (f && is_appid_compatible(f)) {
if (f && is_appid_compatible(f) && pchKey) {
auto result = f->rich_presence().find(pchKey);
if (result != f->rich_presence().end()) value = result->second.c_str();
if (result != f->rich_presence().end()) {
value = result->second.c_str();
} else if (f->lobby_id()) {
// Real Steam auto-derives the "connect"/"Joinable" rich presence from a
// member's joinable lobby (it launches/joins the game with the param
// +connect_lobby <64-bit lobby id>). goldberg historically did not, so
// games that drive friends "Join Game" purely off rich presence (e.g.
// EOSPlus+Steam titles like StarRupture) saw GetFriendRichPresence(host,
// "connect") == "" and reported "No sessions available". Derive it here
// when the friend is in a (non-invisible) lobby and the game didn't set
// the key itself. settings->get_lobby() only tracks non-invisible lobbies,
// so a non-zero lobby_id is a good proxy for "joinable".
if (strcmp(pchKey, "connect") == 0) {
rich_presence_connect = "+connect_lobby " + std::to_string(f->lobby_id());
value = rich_presence_connect.c_str();
} else if (strcmp(pchKey, "Joinable") == 0) {
value = "true";
}
}
}

return value;
Expand Down
60 changes: 23 additions & 37 deletions helpers/dbg_log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <iterator>
#include <cwchar>
#include <cstdarg>
#include <cstdlib>
#include <filesystem>
#include <sstream>
#include <string>
Expand All @@ -13,10 +14,28 @@
#include "common_helpers/os_detector.h"


void dbg_log::open()
// Logging is compiled into release builds, but only ACTIVATES when GSE_FORCE_LOG is
// set in the environment (any non-empty value other than "0"). Debug builds always
// log. The single runtime gate lives here (and is mirrored by PRINT_DEBUG so an
// inactive logger does no work and has no side effects); every write path goes
// through open(), so when inactive out_file stays null and all writes no-op.
bool dbg_log::is_active()
{
#ifndef EMU_RELEASE_BUILD
if (!out_file && filepath.size()) {
return true;
#else
static int cached = -1;
if (cached < 0) {
const char* e = std::getenv("GSE_FORCE_LOG");
cached = (e && e[0] && e[0] != '0') ? 1 : 0;
}
return cached == 1;
#endif
}

void dbg_log::open()
{
if (is_active() && !out_file && filepath.size()) {

// https://en.cppreference.com/w/cpp/filesystem/path/u8path
const auto fsp = std::filesystem::u8path(filepath);
Expand All @@ -27,9 +46,6 @@ void dbg_log::open()
#endif

}

#endif

}

void dbg_log::write_stamp()
Expand All @@ -47,53 +63,33 @@ void dbg_log::write_stamp()

dbg_log::dbg_log(std::string_view path)
{

#ifndef EMU_RELEASE_BUILD
// Store the path unconditionally; open() decides whether to actually open it
// (release: only when GSE_FORCE_LOG is set). Storing a string is free.
filepath = path;
#endif

}

dbg_log::dbg_log(std::wstring_view path)
{

#ifndef EMU_RELEASE_BUILD
filepath = common_helpers::to_str(path);
#endif

}

dbg_log::~dbg_log()
{

#ifndef EMU_RELEASE_BUILD
close();
#endif

}

void dbg_log::write(const std::string &str)
{

#ifndef EMU_RELEASE_BUILD
write("%s", str.c_str());
#endif

}

void dbg_log::write(const std::wstring &str)
{

#ifndef EMU_RELEASE_BUILD
write("%s", str.c_str());
#endif

}

void dbg_log::write(const char *fmt, ...)
{

#ifndef EMU_RELEASE_BUILD
std::lock_guard lk(f_mtx);
open();
if (out_file) {
Expand All @@ -107,14 +103,10 @@ void dbg_log::write(const char *fmt, ...)
std::fprintf(out_file, "\n");
std::fflush(out_file);
}
#endif

}

void dbg_log::write(const wchar_t *fmt, ...)
{

#ifndef EMU_RELEASE_BUILD
std::lock_guard lk(f_mtx);
open();
if (out_file) {
Expand All @@ -128,20 +120,14 @@ void dbg_log::write(const wchar_t *fmt, ...)
std::fprintf(out_file, "\n");
std::fflush(out_file);
}
#endif

}

void dbg_log::close()
{

#ifndef EMU_RELEASE_BUILD
std::lock_guard lk(f_mtx);
if (out_file) {
std::fprintf(out_file, "\nLog file closed\n\n");
std::fclose(out_file);
out_file = nullptr;
}
#endif

}
23 changes: 23 additions & 0 deletions helpers/dbg_log/dbg_log.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string_view>
#include <mutex>
#include <cstdio>
#include <cstdlib>
#include <chrono>

class dbg_log
Expand All @@ -29,4 +30,26 @@ class dbg_log
void write(const wchar_t* fmt, ...);

void close();

// True when logging should actually happen: always in debug builds; in release
// builds only when the GSE_FORCE_LOG env var is set (cached on first call).
// PRINT_DEBUG gates its whole body on this so an inactive logger has ~zero cost
// and no side effects.
bool is_active();
};

// Single process-wide logger, provided as an inline accessor so every translation
// unit / project that uses PRINT_DEBUG gets it WITHOUT needing to link a specific
// .cpp that defines a global (e.g. lib_steam_old uses PRINT_DEBUG but does not link
// base.cpp, and has no get_full_program_path). Log path: GSE_LOG_PATH env if set,
// else a fixed name in the process CWD. The file is opened lazily and only if
// logging actually activates (see dbg_log::is_active / GSE_FORCE_LOG).
inline dbg_log& dbg_logger_get()
{
static dbg_log instance(
[]() -> std::string {
const char* p = std::getenv("GSE_LOG_PATH");
return (p && p[0]) ? std::string(p) : std::string("STEAM_LOG.log");
}());
return instance;
}
Loading