Merge branch 'dev' into dev
This commit is contained in:
commit
d7f6576c65
228
dll/client_known_interfaces.cpp
Normal file
228
dll/client_known_interfaces.cpp
Normal file
@ -0,0 +1,228 @@
|
||||
#include "dll/client_known_interfaces.h"
|
||||
|
||||
/*
|
||||
the client function Steam_IsKnownInterface() accesses a structure which has this layout:
|
||||
|
||||
typedef struct struct_known_interfaces {
|
||||
void *unknown_function_ptr;
|
||||
const char *name; // ex: "STEAMAPPLIST_INTERFACE_VERSION001"
|
||||
const char *family; // ex: "AppList"
|
||||
struct_known_interfaces *previous_node;
|
||||
};
|
||||
|
||||
this is a dump of the `name` field when running this function from a debugger
|
||||
*/
|
||||
|
||||
extern const std::unordered_set<std::string> client_known_interfaces = {
|
||||
"SteamAppDisableUpdate001",
|
||||
"STEAMAPPLIST_INTERFACE_VERSION001",
|
||||
"SteamApps001",
|
||||
"STEAMAPPS_INTERFACE_VERSION001",
|
||||
"STEAMAPPS_INTERFACE_VERSION002",
|
||||
"STEAMAPPS_INTERFACE_VERSION003",
|
||||
"STEAMAPPS_INTERFACE_VERSION004",
|
||||
"STEAMAPPS_INTERFACE_VERSION005",
|
||||
"STEAMAPPS_INTERFACE_VERSION006",
|
||||
"STEAMAPPS_INTERFACE_VERSION007",
|
||||
"STEAMAPPS_INTERFACE_VERSION008",
|
||||
"STEAMAPPTICKET_INTERFACE_VERSION001",
|
||||
"SteamBilling002",
|
||||
"STEAMCHAT_INTERFACE_VERSION003",
|
||||
"SteamController003",
|
||||
"SteamController004",
|
||||
"SteamController005",
|
||||
"SteamController006",
|
||||
"SteamController007",
|
||||
"SteamController008",
|
||||
"STEAMCONTROLLER_INTERFACE_VERSION",
|
||||
"SteamFriends001",
|
||||
"SteamFriends002",
|
||||
"SteamFriends003",
|
||||
"SteamFriends004",
|
||||
"SteamFriends005",
|
||||
"SteamFriends006",
|
||||
"SteamFriends007",
|
||||
"SteamFriends008",
|
||||
"SteamFriends009",
|
||||
"SteamFriends010",
|
||||
"SteamFriends011",
|
||||
"SteamFriends012",
|
||||
"SteamFriends013",
|
||||
"SteamFriends014",
|
||||
"SteamFriends015",
|
||||
"SteamFriends016",
|
||||
"SteamFriends017",
|
||||
"SteamGameCoordinator001",
|
||||
"SteamGameServer002",
|
||||
"SteamGameServer003",
|
||||
"SteamGameServer004",
|
||||
"SteamGameServer005",
|
||||
"SteamGameServer006",
|
||||
"SteamGameServer007",
|
||||
"SteamGameServer008",
|
||||
"SteamGameServer009",
|
||||
"SteamGameServer010",
|
||||
"SteamGameServer011",
|
||||
"SteamGameServer012",
|
||||
"SteamGameServer013",
|
||||
"SteamGameServer014",
|
||||
"SteamGameServer015",
|
||||
"SteamGameServerStats001",
|
||||
"SteamGameStats001",
|
||||
"STEAMHTMLSURFACE_INTERFACE_VERSION_001",
|
||||
"STEAMHTMLSURFACE_INTERFACE_VERSION_002",
|
||||
"STEAMHTMLSURFACE_INTERFACE_VERSION_003",
|
||||
"STEAMHTMLSURFACE_INTERFACE_VERSION_004",
|
||||
"STEAMHTMLSURFACE_INTERFACE_VERSION_005",
|
||||
"STEAMHTTP_INTERFACE_VERSION001",
|
||||
"STEAMHTTP_INTERFACE_VERSION002",
|
||||
"STEAMHTTP_INTERFACE_VERSION003",
|
||||
"SteamInput001",
|
||||
"SteamInput002",
|
||||
"SteamInput003",
|
||||
"SteamInput004",
|
||||
"SteamInput005",
|
||||
"SteamInput006",
|
||||
"STEAMINVENTORY_INTERFACE_V001",
|
||||
"STEAMINVENTORY_INTERFACE_V002",
|
||||
"STEAMINVENTORY_INTERFACE_V003",
|
||||
"SteamMasterServerUpdater001",
|
||||
"SteamMatchGameSearch001",
|
||||
"SteamMatchMaking001",
|
||||
"SteamMatchMaking002",
|
||||
"SteamMatchMaking003",
|
||||
"SteamMatchMaking004",
|
||||
"SteamMatchMaking005",
|
||||
"SteamMatchMaking006",
|
||||
"SteamMatchMaking007",
|
||||
"SteamMatchMaking008",
|
||||
"SteamMatchMaking009",
|
||||
"SteamMatchMakingServers001",
|
||||
"SteamMatchMakingServers002",
|
||||
"STEAMMUSIC_INTERFACE_VERSION001",
|
||||
"STEAMMUSICREMOTE_INTERFACE_VERSION001",
|
||||
"SteamNetworking001",
|
||||
"SteamNetworking002",
|
||||
"SteamNetworking003",
|
||||
"SteamNetworking004",
|
||||
"SteamNetworking005",
|
||||
"SteamNetworking006",
|
||||
"SteamNetworkingMessages002",
|
||||
"SteamNetworkingSockets002",
|
||||
"SteamNetworkingSockets003",
|
||||
"SteamNetworkingSockets004",
|
||||
"SteamNetworkingSockets005",
|
||||
"SteamNetworkingSockets006",
|
||||
"SteamNetworkingSockets008",
|
||||
"SteamNetworkingSockets009",
|
||||
"SteamNetworkingSockets010",
|
||||
"SteamNetworkingSockets011",
|
||||
"SteamNetworkingSockets012",
|
||||
"SteamNetworkingSocketsSerialized001",
|
||||
"SteamNetworkingSocketsSerialized002",
|
||||
"SteamNetworkingSocketsSerialized003",
|
||||
"SteamNetworkingSocketsSerialized004",
|
||||
"SteamNetworkingSocketsSerialized005",
|
||||
"SteamNetworkingUtils001",
|
||||
"SteamNetworkingUtils002",
|
||||
"SteamNetworkingUtils003",
|
||||
"SteamNetworkingUtils004",
|
||||
"STEAMPARENTALSETTINGS_INTERFACE_VERSION001",
|
||||
"SteamParties001",
|
||||
"SteamParties002",
|
||||
"STEAMREMOTEPLAY_INTERFACE_VERSION001",
|
||||
"STEAMREMOTEPLAY_INTERFACE_VERSION002",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION001",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION002",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION003",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION004",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION005",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION006",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION007",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION008",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION009",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION010",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION011",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION012",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION013",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION014",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION015",
|
||||
"STEAMREMOTESTORAGE_INTERFACE_VERSION016",
|
||||
"STEAMSCREENSHOTS_INTERFACE_VERSION001",
|
||||
"STEAMSCREENSHOTS_INTERFACE_VERSION002",
|
||||
"STEAMSCREENSHOTS_INTERFACE_VERSION003",
|
||||
"SteamStreamLauncher001",
|
||||
"STEAMTIMELINE_INTERFACE_V001",
|
||||
"STEAMTV_INTERFACE_V001",
|
||||
"STEAMTV_INTERFACE_V002",
|
||||
"STEAMUGC_INTERFACE_VERSION001",
|
||||
"STEAMUGC_INTERFACE_VERSION002",
|
||||
"STEAMUGC_INTERFACE_VERSION003",
|
||||
"STEAMUGC_INTERFACE_VERSION004",
|
||||
"STEAMUGC_INTERFACE_VERSION005",
|
||||
"STEAMUGC_INTERFACE_VERSION006",
|
||||
"STEAMUGC_INTERFACE_VERSION007",
|
||||
"STEAMUGC_INTERFACE_VERSION008",
|
||||
"STEAMUGC_INTERFACE_VERSION009",
|
||||
"STEAMUGC_INTERFACE_VERSION010",
|
||||
"STEAMUGC_INTERFACE_VERSION011",
|
||||
"STEAMUGC_INTERFACE_VERSION012",
|
||||
"STEAMUGC_INTERFACE_VERSION013",
|
||||
"STEAMUGC_INTERFACE_VERSION014",
|
||||
"STEAMUGC_INTERFACE_VERSION015",
|
||||
"STEAMUGC_INTERFACE_VERSION016",
|
||||
"STEAMUGC_INTERFACE_VERSION017",
|
||||
"STEAMUGC_INTERFACE_VERSION018",
|
||||
"STEAMUGC_INTERFACE_VERSION019",
|
||||
"STEAMUGC_INTERFACE_VERSION020",
|
||||
"STEAMUNIFIEDMESSAGES_INTERFACE_VERSION001",
|
||||
"SteamUser004",
|
||||
"SteamUser005",
|
||||
"SteamUser006",
|
||||
"SteamUser007",
|
||||
"SteamUser008",
|
||||
"SteamUser009",
|
||||
"SteamUser010",
|
||||
"SteamUser011",
|
||||
"SteamUser012",
|
||||
"SteamUser013",
|
||||
"SteamUser014",
|
||||
"SteamUser015",
|
||||
"SteamUser016",
|
||||
"SteamUser017",
|
||||
"SteamUser018",
|
||||
"SteamUser019",
|
||||
"SteamUser020",
|
||||
"SteamUser021",
|
||||
"SteamUser022",
|
||||
"SteamUser023",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION001",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION002",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION003",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION004",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION005",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION006",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION007",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION008",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION009",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION010",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION011",
|
||||
"STEAMUSERSTATS_INTERFACE_VERSION012",
|
||||
"SteamUtils001",
|
||||
"SteamUtils002",
|
||||
"SteamUtils003",
|
||||
"SteamUtils004",
|
||||
"SteamUtils005",
|
||||
"SteamUtils006",
|
||||
"SteamUtils007",
|
||||
"SteamUtils008",
|
||||
"SteamUtils009",
|
||||
"SteamUtils010",
|
||||
"STEAMVIDEO_INTERFACE_V001",
|
||||
"STEAMVIDEO_INTERFACE_V002",
|
||||
"STEAMVIDEO_INTERFACE_V003",
|
||||
"STEAMVIDEO_INTERFACE_V004",
|
||||
"STEAMVIDEO_INTERFACE_V005",
|
||||
"STEAMVIDEO_INTERFACE_V006",
|
||||
"STEAMVIDEO_INTERFACE_V007",
|
||||
};
|
63
dll/dll.cpp
63
dll/dll.cpp
@ -18,6 +18,7 @@
|
||||
#define STEAM_API_FUNCTIONS_IMPL
|
||||
#include "dll/dll.h"
|
||||
#include "dll/settings_parser.h"
|
||||
#include "dll/client_known_interfaces.h"
|
||||
|
||||
|
||||
static char old_client[128] = STEAMCLIENT_INTERFACE_VERSION; //"SteamClient017";
|
||||
@ -1320,7 +1321,7 @@ STEAMCLIENT_API steam_bool Steam_BGetCallback( HSteamPipe hSteamPipe, CallbackMs
|
||||
|
||||
STEAMCLIENT_API void Steam_FreeLastCallback( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("Steam_FreeLastCallback %i", hSteamPipe);
|
||||
PRINT_DEBUG("%i", hSteamPipe);
|
||||
SteamAPI_ManualDispatch_FreeLastCallback( hSteamPipe );
|
||||
}
|
||||
|
||||
@ -1330,7 +1331,7 @@ STEAMCLIENT_API steam_bool Steam_GetAPICallResult( HSteamPipe hSteamPipe, SteamA
|
||||
return SteamAPI_ManualDispatch_GetAPICallResult(hSteamPipe, hSteamAPICall, pCallback, cubCallback, iCallbackExpected, pbFailed);
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void *CreateInterface( const char *pName, int *pReturnCode )
|
||||
STEAMCLIENT_API void* CreateInterface( const char *pName, int *pReturnCode )
|
||||
{
|
||||
PRINT_DEBUG("%s %p", pName, pReturnCode);
|
||||
auto ptr = create_client_interface(pName);
|
||||
@ -1347,6 +1348,11 @@ STEAMCLIENT_API void Breakpad_SteamMiniDumpInit( uint32 a, const char *b, const
|
||||
PRINT_DEBUG_TODO();
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Breakpad_SteamSendMiniDump( void *a, uint32 b )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Breakpad_SteamSetAppID( uint32 unAppID )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
@ -1369,19 +1375,19 @@ STEAMCLIENT_API void Breakpad_SteamWriteMiniDumpUsingExceptionInfoWithBuildId( i
|
||||
PRINT_DEBUG(" app is writing a crash dump! [XXXXXXXXXXXXXXXXXXXXXXXXXXX]");
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_BConnected( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
STEAMCLIENT_API steam_bool Steam_BConnected( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG_ENTRY();
|
||||
return true;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_BLoggedOn( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
STEAMCLIENT_API steam_bool Steam_BLoggedOn( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG_ENTRY();
|
||||
return true;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_BReleaseSteamPipe( HSteamPipe hSteamPipe )
|
||||
STEAMCLIENT_API steam_bool Steam_BReleaseSteamPipe( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
@ -1411,19 +1417,19 @@ STEAMCLIENT_API HSteamPipe Steam_CreateSteamPipe()
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSBLoggedOn( void *phSteamHandle )
|
||||
STEAMCLIENT_API steam_bool Steam_GSBLoggedOn( void *phSteamHandle )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSBSecure( void *phSteamHandle)
|
||||
STEAMCLIENT_API steam_bool Steam_GSBSecure( void *phSteamHandle)
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSGetSteam2GetEncryptionKeyToSendToNewClient( void *phSteamHandle, void *pvEncryptionKey, uint32 *pcbEncryptionKey, uint32 cbMaxEncryptionKey )
|
||||
STEAMCLIENT_API steam_bool Steam_GSGetSteam2GetEncryptionKeyToSendToNewClient( void *phSteamHandle, void *pvEncryptionKey, uint32 *pcbEncryptionKey, uint32 cbMaxEncryptionKey )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
@ -1445,37 +1451,37 @@ STEAMCLIENT_API void Steam_GSLogOn( void *phSteamHandle )
|
||||
PRINT_DEBUG_TODO();
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSRemoveUserConnect( void *phSteamHandle, uint32 unUserID )
|
||||
STEAMCLIENT_API steam_bool Steam_GSRemoveUserConnect( void *phSteamHandle, uint32 unUserID )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSendSteam2UserConnect( void *phSteamHandle, uint32 unUserID, const void *pvRawKey, uint32 unKeyLen, uint32 unIPPublic, uint16 usPort, const void *pvCookie, uint32 cubCookie )
|
||||
STEAMCLIENT_API steam_bool Steam_GSSendSteam2UserConnect( void *phSteamHandle, uint32 unUserID, const void *pvRawKey, uint32 unKeyLen, uint32 unIPPublic, uint16 usPort, const void *pvCookie, uint32 cubCookie )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSendSteam3UserConnect( void *phSteamHandle, uint64 steamID, uint32 unIPPublic, const void *pvCookie, uint32 cubCookie )
|
||||
STEAMCLIENT_API steam_bool Steam_GSSendSteam3UserConnect( void *phSteamHandle, uint64 steamID, uint32 unIPPublic, const void *pvCookie, uint32 cubCookie )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSendUserDisconnect( void *phSteamHandle, uint64 ulSteamID, uint32 unUserID )
|
||||
STEAMCLIENT_API steam_bool Steam_GSSendUserDisconnect( void *phSteamHandle, uint64 ulSteamID, uint32 unUserID )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSendUserStatusResponse( void *phSteamHandle, uint64 ulSteamID, int nSecondsConnected, int nSecondsSinceLast )
|
||||
STEAMCLIENT_API steam_bool Steam_GSSendUserStatusResponse( void *phSteamHandle, uint64 ulSteamID, int nSecondsConnected, int nSecondsSinceLast )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSetServerType( void *phSteamHandle, int32 nAppIdServed, uint32 unServerFlags, uint32 unGameIP, uint32 unGamePort, const char *pchGameDir, const char *pchVersion )
|
||||
STEAMCLIENT_API steam_bool Steam_GSSetServerType( void *phSteamHandle, int32 nAppIdServed, uint32 unServerFlags, uint32 unGameIP, uint32 unGamePort, const char *pchGameDir, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
@ -1486,7 +1492,7 @@ STEAMCLIENT_API void Steam_GSSetSpawnCount( void *phSteamHandle, uint32 ucSpawn
|
||||
PRINT_DEBUG_TODO();
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSUpdateStatus( void *phSteamHandle, int cPlayers, int cPlayersMax, int cBotPlayers, const char *pchServerName, const char *pchMapName )
|
||||
STEAMCLIENT_API steam_bool Steam_GSUpdateStatus( void *phSteamHandle, int cPlayers, int cPlayersMax, int cBotPlayers, const char *pchServerName, const char *pchMapName )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return false;
|
||||
@ -1495,7 +1501,7 @@ STEAMCLIENT_API bool Steam_GSUpdateStatus( void *phSteamHandle, int cPlayers, in
|
||||
STEAMCLIENT_API void* Steam_GetGSHandle( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API int Steam_InitiateGameConnection( HSteamUser hUser, HSteamPipe hSteamPipe, void *pBlob, int cbMaxBlob, uint64 steamID, int nGameAppID, uint32 unIPServer, uint16 usPortServer, bool bSecure )
|
||||
@ -1504,6 +1510,24 @@ STEAMCLIENT_API int Steam_InitiateGameConnection( HSteamUser hUser, HSteamPipe h
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://github.com/ValveSoftware/Proton/blob/962bbc4e74dde0643a6edab7c845bc628601f23f/lsteamclient/steamclient_main.c#L579-L586
|
||||
STEAMCLIENT_API steam_bool Steam_IsKnownInterface( const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("'%s'", pchVersion);
|
||||
|
||||
// real client doesn't validate if the arg was null
|
||||
if (!pchVersion) return false;
|
||||
|
||||
bool found = client_known_interfaces.count(pchVersion);
|
||||
|
||||
PRINT_DEBUG(" result=%i", (int)found);
|
||||
if (!found) {
|
||||
get_steam_client()->report_missing_impl(pchVersion, EMU_FUNC_NAME);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Steam_LogOff( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
@ -1514,6 +1538,13 @@ STEAMCLIENT_API void Steam_LogOn( HSteamUser hUser, HSteamPipe hSteamPipe, uint6
|
||||
PRINT_DEBUG_TODO();
|
||||
}
|
||||
|
||||
// https://github.com/ValveSoftware/Proton/blob/962bbc4e74dde0643a6edab7c845bc628601f23f/lsteamclient/steamclient_main.c#L588-L594
|
||||
STEAMCLIENT_API void Steam_NotifyMissingInterface(HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("XXXXXXXXXXXXX '%s' %i", pchVersion, hSteamPipe);
|
||||
get_steam_client()->report_missing_impl(pchVersion, EMU_FUNC_NAME);
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Steam_ReleaseThreadLocalMemory(bool thread_exit)
|
||||
{
|
||||
PRINT_DEBUG_TODO();
|
||||
|
9
dll/dll/client_known_interfaces.h
Normal file
9
dll/dll/client_known_interfaces.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef _CLIENT_KNOWN_INTERFACES_H_
|
||||
#define _CLIENT_KNOWN_INTERFACES_H_
|
||||
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
|
||||
extern const std::unordered_set<std::string> client_known_interfaces;
|
||||
|
||||
#endif // _CLIENT_KNOWN_INTERFACES_H_
|
@ -216,6 +216,7 @@ private:
|
||||
|
||||
public:
|
||||
constexpr const static int INVALID_IMAGE_HANDLE = 0;
|
||||
constexpr const static int UNLOADED_IMAGE_HANDLE = -1;
|
||||
|
||||
//Depots
|
||||
std::vector<DepotId_t> depots{};
|
||||
@ -258,8 +259,12 @@ public:
|
||||
// the stat itself is always saved regardless of that flag, only affects the achievement progress
|
||||
bool save_only_higher_stat_achievement_progress = true;
|
||||
// the emulator loads the achievements icons is memory mainly for `ISteamUserStats::GetAchievementIcon()`
|
||||
// true means load icons lazily when they are requested, otherwise load icons as soon as the interface ISteamUserStats is initialized
|
||||
bool lazy_load_achievements_icons = true;
|
||||
// this defines how many icons to load each iteration when the periodic callback in `Steam_User_Stats` is triggered
|
||||
// or when the app calls `SteamAPI_RunCallbacks()`
|
||||
// -1 == functionality disabled
|
||||
// 0 == load icons only when they're requested
|
||||
// >0 == load icons in the background as mentioned above
|
||||
int paginated_achievements_icons = 10;
|
||||
|
||||
// bypass to make SetAchievement() always return true, prevent some games from breaking
|
||||
bool achievement_bypass = false;
|
||||
@ -314,6 +319,8 @@ public:
|
||||
bool overlay_warn_local_save = false;
|
||||
//disable overlay warning for local save
|
||||
bool disable_overlay_warning_local_save = false;
|
||||
// should the overlay upload icons to the GPU and display them
|
||||
bool overlay_upload_achs_icons_to_gpu = true;
|
||||
//disable overlay warning for bad app ID (= 0)
|
||||
bool disable_overlay_warning_bad_appid = false;
|
||||
// disable all overlay warnings
|
||||
|
@ -338,6 +338,7 @@ public:
|
||||
|
||||
void DestroyAllInterfaces();
|
||||
|
||||
void report_missing_impl(std::string_view itf, std::string_view caller);
|
||||
[[noreturn]] void report_missing_impl_and_exit(std::string_view itf, std::string_view caller);
|
||||
|
||||
};
|
||||
|
@ -70,8 +70,7 @@ public ISteamUserStats011,
|
||||
public ISteamUserStats
|
||||
{
|
||||
public:
|
||||
static constexpr auto achievements_user_file = "achievements.json";
|
||||
static constexpr int UNLOADED_ACH_ICON = -1;
|
||||
static constexpr const auto achievements_user_file = "achievements.json";
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
@ -95,7 +94,7 @@ private:
|
||||
nlohmann::json defined_achievements{};
|
||||
nlohmann::json user_achievements{};
|
||||
std::vector<std::string> sorted_achievement_names{};
|
||||
bool achievements_icons_loaded = false;
|
||||
size_t last_loaded_ach_icon{};
|
||||
|
||||
std::map<std::string, int32> stats_cache_int{};
|
||||
std::map<std::string, float> stats_cache_float{};
|
||||
@ -213,7 +212,7 @@ public:
|
||||
// specified achievement.
|
||||
int GetAchievementIcon( const char *pchName );
|
||||
|
||||
int get_achievement_icon_handle( const std::string &ach_name, bool pbAchieved );
|
||||
int get_achievement_icon_handle( const std::string &ach_name, bool pbAchieved, bool force_load = false );
|
||||
|
||||
std::string get_achievement_icon_name( const char *pchName, bool achieved );
|
||||
|
||||
|
@ -375,7 +375,7 @@ int Settings::add_image(const std::string &data, uint32 width, uint32 height)
|
||||
|
||||
Image_Data* Settings::get_image(int handle)
|
||||
{
|
||||
if (INVALID_IMAGE_HANDLE == handle) {
|
||||
if (INVALID_IMAGE_HANDLE == handle || UNLOADED_IMAGE_HANDLE == handle) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1343,6 +1343,9 @@ static void parse_overlay_general_config(class Settings *settings_client, class
|
||||
settings_client->disable_overlay_warning_local_save = ini.GetBoolValue("overlay::general", "disable_warning_local_save", settings_client->disable_overlay_warning_local_save);
|
||||
settings_server->disable_overlay_warning_local_save = ini.GetBoolValue("overlay::general", "disable_warning_local_save", settings_server->disable_overlay_warning_local_save);
|
||||
|
||||
settings_client->overlay_upload_achs_icons_to_gpu = ini.GetBoolValue("overlay::general", "upload_achievements_icons_to_gpu", settings_client->overlay_upload_achs_icons_to_gpu);
|
||||
settings_server->overlay_upload_achs_icons_to_gpu = ini.GetBoolValue("overlay::general", "upload_achievements_icons_to_gpu", settings_server->overlay_upload_achs_icons_to_gpu);
|
||||
|
||||
}
|
||||
|
||||
// main::misc::steam_game_stats_reports_dir
|
||||
@ -1437,8 +1440,13 @@ static void parse_stats_features(class Settings *settings_client, class Settings
|
||||
settings_client->save_only_higher_stat_achievement_progress = ini.GetBoolValue("main::stats", "save_only_higher_stat_achievement_progress", settings_client->save_only_higher_stat_achievement_progress);
|
||||
settings_server->save_only_higher_stat_achievement_progress = ini.GetBoolValue("main::stats", "save_only_higher_stat_achievement_progress", settings_server->save_only_higher_stat_achievement_progress);
|
||||
|
||||
settings_client->lazy_load_achievements_icons = ini.GetBoolValue("main::stats", "lazy_load_achievements_icons", settings_client->lazy_load_achievements_icons);
|
||||
settings_server->lazy_load_achievements_icons = ini.GetBoolValue("main::stats", "lazy_load_achievements_icons", settings_server->lazy_load_achievements_icons);
|
||||
{
|
||||
long val_client = ini.GetLongValue("main::stats", "paginated_achievements_icons", settings_client->paginated_achievements_icons);
|
||||
settings_client->paginated_achievements_icons = static_cast<int>(val_client);
|
||||
|
||||
long val_server = ini.GetLongValue("main::stats", "paginated_achievements_icons", settings_server->paginated_achievements_icons);
|
||||
settings_server->paginated_achievements_icons = static_cast<int>(val_server);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
// https://developer.valvesoftware.com/wiki/Steam_Application_IDs
|
||||
// https://developer.valvesoftware.com/wiki/Dedicated_Servers_List
|
||||
// they're not really accurate
|
||||
const std::map<uint32, std::string> steam_preowned_app_ids = {
|
||||
extern const std::map<uint32, std::string> steam_preowned_app_ids = {
|
||||
|
||||
// { 0, "Base Goldsource Shared Binaries" },
|
||||
// { 1, "Base Goldsource Shared Content" },
|
||||
|
@ -938,7 +938,7 @@ ISteamAppTicket *Steam_Client::GetAppTicket( HSteamUser hSteamUser, HSteamPipe h
|
||||
report_missing_impl_and_exit(pchVersion, EMU_FUNC_NAME);
|
||||
}
|
||||
|
||||
void Steam_Client::report_missing_impl_and_exit(std::string_view itf, std::string_view caller)
|
||||
void Steam_Client::report_missing_impl(std::string_view itf, std::string_view caller)
|
||||
{
|
||||
PRINT_DEBUG("'%s' '%s'", itf.data(), caller.data());
|
||||
std::lock_guard lck(global_mutex);
|
||||
@ -947,18 +947,27 @@ void Steam_Client::report_missing_impl_and_exit(std::string_view itf, std::strin
|
||||
try {
|
||||
ss << "INTERFACE=" << itf << "\n";
|
||||
ss << "CALLER FN=" << caller << "\n";
|
||||
}
|
||||
catch(...) { }
|
||||
|
||||
try {
|
||||
if (settings_client) {
|
||||
ss << "APPID=" << settings_client->get_local_game_id().AppID() << "\n";
|
||||
}
|
||||
}
|
||||
catch(...) { }
|
||||
|
||||
try {
|
||||
std::string time(common_helpers::get_utc_time());
|
||||
if (time.size()) {
|
||||
ss << "TIME=" << time << "\n";
|
||||
}
|
||||
|
||||
ss << "--------------------\n" << std::endl;
|
||||
}
|
||||
catch(...) { }
|
||||
|
||||
try {
|
||||
std::ofstream report(std::filesystem::u8path(get_full_program_path() + "EMU_MISSING_INTERFACE.txt"), std::ios::out | std::ios::app);
|
||||
if (report.is_open()) {
|
||||
report << ss.str();
|
||||
@ -969,6 +978,10 @@ void Steam_Client::report_missing_impl_and_exit(std::string_view itf, std::strin
|
||||
#if defined(__WINDOWS__)
|
||||
MessageBoxA(nullptr, ss.str().c_str(), "Missing interface", MB_OK);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void Steam_Client::report_missing_impl_and_exit(std::string_view itf, std::string_view caller)
|
||||
{
|
||||
report_missing_impl(itf, caller);
|
||||
std::exit(0x4155149); // MISSING :)
|
||||
}
|
||||
|
@ -96,8 +96,8 @@ Steam_User_Stats::Steam_User_Stats(Settings *settings, class Networking *network
|
||||
it["displayName"] = get_value_for_language(it, "displayName", settings->get_language());
|
||||
it["description"] = get_value_for_language(it, "description", settings->get_language());
|
||||
|
||||
it["icon_handle"] = UNLOADED_ACH_ICON;
|
||||
it["icon_gray_handle"] = UNLOADED_ACH_ICON;
|
||||
it["icon_handle"] = Settings::UNLOADED_IMAGE_HANDLE;
|
||||
it["icon_gray_handle"] = Settings::UNLOADED_IMAGE_HANDLE;
|
||||
}
|
||||
|
||||
//TODO: not sure if the sort is actually case insensitive, ach names seem to be treated by steam as case insensitive so I assume they are.
|
||||
|
@ -80,8 +80,8 @@ void Steam_User_Stats::save_achievements()
|
||||
int Steam_User_Stats::load_ach_icon(nlohmann::json &defined_ach, bool achieved)
|
||||
{
|
||||
const char *icon_handle_key = achieved ? "icon_handle" : "icon_gray_handle";
|
||||
int current_handle = defined_ach.value(icon_handle_key, UNLOADED_ACH_ICON);
|
||||
if (UNLOADED_ACH_ICON != current_handle) { // already loaded
|
||||
int current_handle = defined_ach.value(icon_handle_key, Settings::UNLOADED_IMAGE_HANDLE);
|
||||
if (Settings::UNLOADED_IMAGE_HANDLE != current_handle) { // already loaded
|
||||
return current_handle;
|
||||
}
|
||||
|
||||
@ -394,7 +394,12 @@ int Steam_User_Stats::GetAchievementIcon( const char *pchName )
|
||||
GetAchievement(pchName, &achieved);
|
||||
|
||||
std::string ach_name(pchName);
|
||||
int handle = get_achievement_icon_handle(ach_name, achieved);
|
||||
// here we force load in case the game has a lot of achievements, because otherwise some games might timeout
|
||||
// this somewhat defeats the purpose of background loading but a timeout is worse
|
||||
int handle = get_achievement_icon_handle(ach_name, achieved, true);
|
||||
if (Settings::UNLOADED_IMAGE_HANDLE == handle) { // if the background callback didn't get a chance to load this one yet
|
||||
handle = Settings::INVALID_IMAGE_HANDLE;
|
||||
}
|
||||
|
||||
UserAchievementIconFetched_t data{};
|
||||
data.m_bAchieved = achieved ;
|
||||
@ -406,11 +411,10 @@ int Steam_User_Stats::GetAchievementIcon( const char *pchName )
|
||||
return handle;
|
||||
}
|
||||
|
||||
int Steam_User_Stats::get_achievement_icon_handle( const std::string &ach_name, bool achieved )
|
||||
int Steam_User_Stats::get_achievement_icon_handle( const std::string &ach_name, bool achieved, bool force_load )
|
||||
{
|
||||
PRINT_DEBUG("'%s', %i", ach_name.c_str(), (int)achieved);
|
||||
PRINT_DEBUG("'%s', achieved=%i, force=%i", ach_name.c_str(), (int)achieved, (int)force_load);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (ach_name.empty()) return Settings::INVALID_IMAGE_HANDLE;
|
||||
|
||||
nlohmann::detail::iter_impl<nlohmann::json> it = defined_achievements.end();
|
||||
try {
|
||||
@ -418,7 +422,20 @@ int Steam_User_Stats::get_achievement_icon_handle( const std::string &ach_name,
|
||||
} catch(...) { }
|
||||
if (defined_achievements.end() == it) return Settings::INVALID_IMAGE_HANDLE;
|
||||
|
||||
int handle = load_ach_icon(*it, achieved);
|
||||
int handle = Settings::INVALID_IMAGE_HANDLE;
|
||||
if (settings->paginated_achievements_icons < 0) { // disabled functionality
|
||||
handle = Settings::INVALID_IMAGE_HANDLE;
|
||||
} else if (settings->paginated_achievements_icons == 0) { // load the icon only when requested
|
||||
handle = load_ach_icon(*it, achieved);
|
||||
} else { // depend on the periodic callback to load the icon
|
||||
if (force_load) {
|
||||
handle = load_ach_icon(*it, achieved);
|
||||
} else {
|
||||
const char *icon_handle_key = achieved ? "icon_handle" : "icon_gray_handle";
|
||||
handle = it->value(icon_handle_key, Settings::UNLOADED_IMAGE_HANDLE);
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_DEBUG("returned handle = %i", handle);
|
||||
return handle;
|
||||
}
|
||||
@ -784,16 +801,25 @@ bool Steam_User_Stats::GetAchievementProgressLimits( const char *pchName, float
|
||||
|
||||
void Steam_User_Stats::load_achievements_icons()
|
||||
{
|
||||
if (achievements_icons_loaded) return;
|
||||
if (settings->lazy_load_achievements_icons) {
|
||||
achievements_icons_loaded = true;
|
||||
return;
|
||||
if (last_loaded_ach_icon >= defined_achievements.size() || settings->paginated_achievements_icons <= 0) return;
|
||||
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
auto now1 = std::chrono::high_resolution_clock::now();
|
||||
#endif
|
||||
|
||||
size_t idx = 0;
|
||||
for (;
|
||||
idx < settings->paginated_achievements_icons && last_loaded_ach_icon < defined_achievements.size();
|
||||
++idx, ++last_loaded_ach_icon) {
|
||||
auto &ach = defined_achievements.at(last_loaded_ach_icon);
|
||||
load_ach_icon(ach, true);
|
||||
load_ach_icon(ach, false);
|
||||
}
|
||||
|
||||
for (auto & defined_ach : defined_achievements) {
|
||||
load_ach_icon(defined_ach, true);
|
||||
load_ach_icon(defined_ach, false);
|
||||
}
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
auto now2 = std::chrono::high_resolution_clock::now();
|
||||
auto dd = (unsigned)std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
|
||||
PRINT_DEBUG("attempted to load %zu achievements icons in %u ms", idx * 2, dd);
|
||||
#endif
|
||||
|
||||
achievements_icons_loaded = true;
|
||||
}
|
||||
|
@ -71,8 +71,10 @@ struct Overlay_Achievement
|
||||
bool hidden{};
|
||||
bool achieved{};
|
||||
uint32 unlock_time{};
|
||||
std::pair< std::weak_ptr<uint64_t>, bool > icon{};
|
||||
std::pair< std::weak_ptr<uint64_t>, bool > icon_gray{};
|
||||
std::weak_ptr<uint64_t> icon{};
|
||||
std::weak_ptr<uint64_t> icon_gray{};
|
||||
int icon_handle = Settings::UNLOADED_IMAGE_HANDLE;
|
||||
int icon_gray_handle = Settings::UNLOADED_IMAGE_HANDLE;
|
||||
};
|
||||
|
||||
struct Notification
|
||||
@ -99,7 +101,6 @@ struct NotificationsCoords
|
||||
class Steam_Overlay
|
||||
{
|
||||
constexpr static const char ACH_SOUNDS_FOLDER[] = "sounds";
|
||||
|
||||
constexpr static const int renderer_detector_polling_ms = 100;
|
||||
|
||||
class Settings* settings;
|
||||
@ -120,6 +121,7 @@ class Steam_Overlay
|
||||
std::string show_url{};
|
||||
|
||||
std::vector<Overlay_Achievement> achievements{};
|
||||
size_t last_loaded_ach_icon{};
|
||||
|
||||
bool show_overlay = false;
|
||||
bool show_user_info = false;
|
||||
@ -184,12 +186,6 @@ class Steam_Overlay
|
||||
Steam_Overlay& operator=(Steam_Overlay const&) = delete;
|
||||
Steam_Overlay& operator=(Steam_Overlay&&) = delete;
|
||||
|
||||
static void overlay_run_callback(void* object);
|
||||
static void overlay_networking_callback(void* object, Common_Message* msg);
|
||||
|
||||
bool is_friend_joinable(std::pair<const Friend, friend_window_state> &f);
|
||||
bool got_lobby();
|
||||
|
||||
bool submit_notification(
|
||||
notification_type type,
|
||||
const std::string &msg,
|
||||
@ -213,9 +209,7 @@ class Steam_Overlay
|
||||
void add_ach_progressbar(const Overlay_Achievement &ach);
|
||||
ImVec4 get_notification_bg_rgba_safe();
|
||||
void build_notifications(float width, float height);
|
||||
// invite a single friend
|
||||
void invite_friend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking);
|
||||
|
||||
|
||||
void request_renderer_detector();
|
||||
void set_renderer_hook_timeout();
|
||||
void cleanup_renderer_hook();
|
||||
@ -238,14 +232,27 @@ class Steam_Overlay
|
||||
|
||||
bool open_overlay_hook(bool toggle);
|
||||
|
||||
bool try_load_ach_icon(Overlay_Achievement &ach, bool achieved);
|
||||
bool try_load_ach_icon(Overlay_Achievement &ach, bool achieved, bool upload_new_icon_to_gpu);
|
||||
|
||||
void overlay_render_proc();
|
||||
void load_next_ach_icon();
|
||||
uint32 apply_global_style_color();
|
||||
void render_main_window();
|
||||
void networking_msg_received(Common_Message* msg);
|
||||
|
||||
|
||||
void steam_run_callback_update_my_lobby();
|
||||
bool is_friend_joinable(std::pair<const Friend, friend_window_state> &f);
|
||||
// invite a single friend
|
||||
void invite_friend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking);
|
||||
void steam_run_callback_friends_actions();
|
||||
void steam_run_callback();
|
||||
|
||||
void networking_msg_received(Common_Message* msg);
|
||||
|
||||
|
||||
static void overlay_run_callback(void* object);
|
||||
static void overlay_networking_callback(void* object, Common_Message* msg);
|
||||
|
||||
public:
|
||||
Steam_Overlay(Settings* settings, Local_Storage *local_storage, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking *network);
|
||||
|
||||
|
@ -85,23 +85,7 @@ void Steam_Overlay::overlay_run_callback(void* object)
|
||||
{
|
||||
// PRINT_DEBUG_ENTRY();
|
||||
Steam_Overlay* _this = reinterpret_cast<Steam_Overlay*>(object);
|
||||
|
||||
// bail immediately if we can't lock the overlay mutex, deadlock scenario:
|
||||
// 1. ** the background thread locks the global mutex
|
||||
// 2. -- the user opens the overlay
|
||||
// 3. -- the overlay proc is triggered the next frame, locking the overlay mutex
|
||||
// 4. ** the background thread locks the global mutex and runs this callback
|
||||
// 5. ** this callback (already having the global mutex) attempts to lock the overlay mutex (already locked before)
|
||||
// 6. ** this callback, and the background thread, are now blocked, note that the global mutex is still locked
|
||||
// 7. -- in the same frame, some code in the overlay proc attempts to call a steam API which usually locks the global mutex
|
||||
// sice the global mutex is still locked, the overlay proc is also blocked,
|
||||
// and now both the background thread and the overlay proc and locked
|
||||
// even worse, the global mutex is locked forever now
|
||||
if (!_this->overlay_mutex.try_lock()) return;
|
||||
|
||||
_this->steam_run_callback();
|
||||
|
||||
_this->overlay_mutex.unlock();
|
||||
}
|
||||
|
||||
void Steam_Overlay::overlay_networking_callback(void* object, Common_Message* msg)
|
||||
@ -422,7 +406,6 @@ void Steam_Overlay::overlay_state_hook(bool ready)
|
||||
// called when the user presses SHIFT + TAB
|
||||
bool Steam_Overlay::open_overlay_hook(bool toggle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
|
||||
if (toggle) {
|
||||
ShowOverlay(!show_overlay);
|
||||
}
|
||||
@ -639,16 +622,19 @@ void Steam_Overlay::show_test_achievement()
|
||||
ach.description = "~~~ " + ach.title + " ~~~";
|
||||
ach.achieved = true;
|
||||
|
||||
// random add icon
|
||||
if (achievements.size()) {
|
||||
size_t rand_idx = common_helpers::rand_number(achievements.size() - 1);
|
||||
auto &rand_ach = achievements[rand_idx];
|
||||
bool achieved = rand_idx < (achievements.size() / 2);
|
||||
try_load_ach_icon(rand_ach, achieved);
|
||||
ach.icon = achieved ? rand_ach.icon : rand_ach.icon_gray;
|
||||
// force upload to GPU if the pagination is request-based
|
||||
try_load_ach_icon(rand_ach, achieved, settings->paginated_achievements_icons == 0);
|
||||
ach.icon = rand_ach.icon;
|
||||
ach.icon_gray = rand_ach.icon_gray;
|
||||
}
|
||||
|
||||
bool for_progress = false;
|
||||
// randomly add progress
|
||||
bool for_progress = false;
|
||||
if (common_helpers::rand_number(1000) % 2) {
|
||||
for_progress = true;
|
||||
uint32 progress = (uint32)(common_helpers::rand_number(500) / 10 + 50); // [50, 100]
|
||||
@ -662,41 +648,6 @@ void Steam_Overlay::show_test_achievement()
|
||||
notify_sound_user_achievement();
|
||||
}
|
||||
|
||||
bool Steam_Overlay::is_friend_joinable(std::pair<const Friend, friend_window_state> &f)
|
||||
{
|
||||
PRINT_DEBUG("%" PRIu64 "", f.first.id());
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
|
||||
|
||||
if (std::string(steamFriends->get_friend_rich_presence_silent((uint64)f.first.id(), "connect")).length() > 0 ) {
|
||||
PRINT_DEBUG("%" PRIu64 " true (connect string)", f.first.id());
|
||||
return true;
|
||||
}
|
||||
|
||||
FriendGameInfo_t friend_game_info{};
|
||||
steamFriends->GetFriendGamePlayed((uint64)f.first.id(), &friend_game_info);
|
||||
if (friend_game_info.m_steamIDLobby.IsValid() && (f.second.window_state & window_state_lobby_invite)) {
|
||||
PRINT_DEBUG("%" PRIu64 " true (friend in a game)", f.first.id());
|
||||
return true;
|
||||
}
|
||||
|
||||
PRINT_DEBUG("%" PRIu64 " false", f.first.id());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_Overlay::got_lobby()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
|
||||
if (std::string(steamFriends->get_friend_rich_presence_silent(settings->get_local_steam_id(), "connect")).length() > 0)
|
||||
return true;
|
||||
|
||||
if (settings->get_lobby().IsValid())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Steam_Overlay::build_friend_context_menu(Friend const& frd, friend_window_state& state)
|
||||
{
|
||||
if (ImGui::BeginPopupContextItem("Friends_ContextMenu", 1)) {
|
||||
@ -768,24 +719,21 @@ void Steam_Overlay::build_friend_window(Friend const& frd, friend_window_state&
|
||||
// Window id is after the ###, the window title is the friend name
|
||||
std::string friend_window_id = std::move("###" + std::to_string(state.id));
|
||||
if (ImGui::Begin((state.window_title + friend_window_id).c_str(), &show)) {
|
||||
if (state.window_state & window_state_need_attention && ImGui::IsWindowFocused())
|
||||
{
|
||||
if (state.window_state & window_state_need_attention && ImGui::IsWindowFocused()) {
|
||||
state.window_state &= ~window_state_need_attention;
|
||||
}
|
||||
|
||||
// Fill this with the chat box and maybe the invitation
|
||||
if (state.window_state & (window_state_lobby_invite | window_state_rich_invite))
|
||||
{
|
||||
if (state.window_state & (window_state_lobby_invite | window_state_rich_invite)) {
|
||||
ImGui::LabelText("##label", translationInvitedYouToJoinTheGame[current_language], frd.name().c_str(), frd.appid());
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(translationAccept[current_language]))
|
||||
{
|
||||
if (ImGui::Button(translationAccept[current_language])) {
|
||||
state.window_state |= window_state_join;
|
||||
this->has_friend_action.push(frd);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(translationRefuse[current_language]))
|
||||
{
|
||||
if (ImGui::Button(translationRefuse[current_language])) {
|
||||
state.window_state &= ~(window_state_lobby_invite | window_state_rich_invite);
|
||||
}
|
||||
}
|
||||
@ -829,15 +777,12 @@ void Steam_Overlay::build_friend_window(Friend const& frd, friend_window_state&
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(translationSend[current_language]))
|
||||
{
|
||||
if (ImGui::Button(translationSend[current_language])) {
|
||||
send_chat_msg = true;
|
||||
}
|
||||
|
||||
if (send_chat_msg)
|
||||
{
|
||||
if (!(state.window_state & window_state_send_message))
|
||||
{
|
||||
if (send_chat_msg) {
|
||||
if (!(state.window_state & window_state_send_message)) {
|
||||
has_friend_action.push(frd);
|
||||
state.window_state |= window_state_send_message;
|
||||
}
|
||||
@ -845,8 +790,9 @@ void Steam_Overlay::build_friend_window(Friend const& frd, friend_window_state&
|
||||
}
|
||||
|
||||
// User closed the friend window
|
||||
if (!show)
|
||||
if (!show) {
|
||||
state.window_state &= ~window_state_show;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
@ -1108,7 +1054,7 @@ void Steam_Overlay::build_notifications(float width, float height)
|
||||
case notification_type::achievement_progress:
|
||||
case notification_type::achievement: {
|
||||
const auto &ach = it->ach.value();
|
||||
auto& [icon_rsrc, _] = (notification_type)it->type == notification_type::achievement
|
||||
auto &icon_rsrc = (notification_type)it->type == notification_type::achievement
|
||||
? ach.icon
|
||||
: ach.icon_gray;
|
||||
if (!icon_rsrc.expired() && ImGui::BeginTable("imgui_table", 2)) {
|
||||
@ -1244,7 +1190,9 @@ void Steam_Overlay::post_achievement_notification(Overlay_Achievement &ach, bool
|
||||
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
|
||||
if (!Ready()) return;
|
||||
|
||||
try_load_ach_icon(ach, !for_progress); // for progress notifications we want to load the gray icon
|
||||
bool achieved = !for_progress; // for progress notifications we want to load the gray icon
|
||||
// force upload to GPU if the pagination is request-based
|
||||
try_load_ach_icon(ach, achieved, settings->paginated_achievements_icons == 0);
|
||||
submit_notification(
|
||||
for_progress ? notification_type::achievement_progress : notification_type::achievement,
|
||||
ach.title + "\n" + ach.description,
|
||||
@ -1253,26 +1201,22 @@ void Steam_Overlay::post_achievement_notification(Overlay_Achievement &ach, bool
|
||||
);
|
||||
}
|
||||
|
||||
void Steam_Overlay::invite_friend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking)
|
||||
{
|
||||
std::string connect_str = steamFriends->get_friend_rich_presence_silent(settings->get_local_steam_id(), "connect");
|
||||
if (connect_str.length() > 0) {
|
||||
steamFriends->InviteUserToGame(friend_id, connect_str.c_str());
|
||||
PRINT_DEBUG("sent game invitation to friend with id = %llu", friend_id);
|
||||
} else if (settings->get_lobby().IsValid()) {
|
||||
steamMatchmaking->InviteUserToLobby(settings->get_lobby(), friend_id);
|
||||
PRINT_DEBUG("sent lobby invitation to friend with id = %llu", friend_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool Steam_Overlay::try_load_ach_icon(Overlay_Achievement &ach, bool achieved)
|
||||
bool Steam_Overlay::try_load_ach_icon(Overlay_Achievement &ach, bool achieved, bool upload_new_icon_to_gpu)
|
||||
{
|
||||
if (!_renderer) return false;
|
||||
if (settings->paginated_achievements_icons < 0) return false; // no icons are loaded anyway
|
||||
if (!settings->overlay_upload_achs_icons_to_gpu) return false; // don't upload anything to the GPU
|
||||
|
||||
auto& [icon_rsrc, attempted] = achieved ? ach.icon : ach.icon_gray;
|
||||
if (attempted || !icon_rsrc.expired()) return true;
|
||||
auto &icon_rsrc = achieved ? ach.icon : ach.icon_gray;
|
||||
if (!icon_rsrc.expired()) return true;
|
||||
|
||||
const int icon_handle = get_steam_client()->steam_user_stats->get_achievement_icon_handle(ach.name, achieved);
|
||||
// icons needs to be loaded, but we're not allowed
|
||||
if (!upload_new_icon_to_gpu) return false;
|
||||
|
||||
int &icon_handle = achieved ? ach.icon_handle : ach.icon_gray_handle;
|
||||
if (Settings::UNLOADED_IMAGE_HANDLE == icon_handle) { // not loaded yet
|
||||
icon_handle = get_steam_client()->steam_user_stats->get_achievement_icon_handle(ach.name, achieved);
|
||||
}
|
||||
auto image_info = settings->get_image(icon_handle);
|
||||
if (image_info) {
|
||||
int icon_size = static_cast<int>(settings->overlay_appearance.icon_size);
|
||||
@ -1283,14 +1227,14 @@ bool Steam_Overlay::try_load_ach_icon(Overlay_Achievement &ach, bool achieved)
|
||||
PRINT_DEBUG("'%s' (result=%i)", ach.name.c_str(), (int)!icon_rsrc.expired());
|
||||
}
|
||||
|
||||
attempted = true;
|
||||
return !icon_rsrc.expired();
|
||||
}
|
||||
|
||||
// Try to make this function as short as possible or it might affect game's fps.
|
||||
void Steam_Overlay::overlay_render_proc()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
|
||||
std::lock_guard lock(overlay_mutex);
|
||||
|
||||
if (!Ready()) return;
|
||||
|
||||
if (show_overlay) {
|
||||
@ -1302,6 +1246,7 @@ void Steam_Overlay::overlay_render_proc()
|
||||
build_notifications(io.DisplaySize.x, io.DisplaySize.y);
|
||||
}
|
||||
|
||||
load_next_ach_icon();
|
||||
}
|
||||
|
||||
uint32 Steam_Overlay::apply_global_style_color()
|
||||
@ -1480,13 +1425,14 @@ void Steam_Overlay::render_main_window()
|
||||
bool achieved = x.achieved;
|
||||
bool hidden = x.hidden && !achieved;
|
||||
|
||||
try_load_ach_icon(x, true);
|
||||
try_load_ach_icon(x, false);
|
||||
// force upload to GPU if the pagination is request-based
|
||||
try_load_ach_icon(x, true, settings->paginated_achievements_icons == 0);
|
||||
try_load_ach_icon(x, false, settings->paginated_achievements_icons == 0);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
bool could_create_ach_table_entry = false;
|
||||
if (!x.icon.first.expired() || !x.icon_gray.first.expired()) {
|
||||
if (!x.icon.expired() || !x.icon_gray.expired()) {
|
||||
if (ImGui::BeginTable(x.title.c_str(), 2)) {
|
||||
could_create_ach_table_entry = true;
|
||||
|
||||
@ -1495,7 +1441,7 @@ void Steam_Overlay::render_main_window()
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size);
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
auto& [icon_rsrc, _] = achieved ? x.icon : x.icon_gray;
|
||||
auto &icon_rsrc = achieved ? x.icon : x.icon_gray;
|
||||
if (!icon_rsrc.expired()) {
|
||||
ImGui::Image(
|
||||
(ImTextureID)*icon_rsrc.lock().get(),
|
||||
@ -1633,162 +1579,45 @@ void Steam_Overlay::render_main_window()
|
||||
if (style_color_stack) ImGui::PopStyleColor(style_color_stack);
|
||||
ImGui::PopFont();
|
||||
|
||||
if (!show) ShowOverlay(false);
|
||||
if (!show) {
|
||||
ShowOverlay(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Steam_Overlay::networking_msg_received(Common_Message *msg)
|
||||
void Steam_Overlay::load_next_ach_icon()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
|
||||
// this function only works when icons pagination is active, request-based loading is not supported too (pagination=0)
|
||||
if (!settings->overlay_upload_achs_icons_to_gpu || settings->paginated_achievements_icons <= 0 || achievements.empty()) return;
|
||||
|
||||
size_t linear_idx = last_loaded_ach_icon / 2; // 2 icons per achievement, 1 achieved, 1 unachieved
|
||||
if (linear_idx >= achievements.size()) {
|
||||
last_loaded_ach_icon = 0;
|
||||
linear_idx = 0;
|
||||
}
|
||||
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
auto now1 = std::chrono::high_resolution_clock::now();
|
||||
#endif
|
||||
|
||||
auto &ach = achievements.at(linear_idx);
|
||||
++last_loaded_ach_icon;
|
||||
|
||||
bool achieved = last_loaded_ach_icon % 2 != 0;
|
||||
auto &icon_rsrc = achieved ? ach.icon : ach.icon_gray;
|
||||
// always force upload to GPU in background-loading mode (pagination > 0)
|
||||
bool loaded = try_load_ach_icon(ach, achieved, true);
|
||||
|
||||
if (msg->has_steam_messages()) {
|
||||
Friend frd;
|
||||
frd.set_id(msg->source_id());
|
||||
auto friend_info = friends.find(frd);
|
||||
if (friend_info != friends.end()) {
|
||||
Steam_Messages const& steam_message = msg->steam_messages();
|
||||
// Change color to cyan for friend
|
||||
friend_info->second.chat_history.append(friend_info->first.name() + ": " + steam_message.message()).append("\n", 1);
|
||||
if (!(friend_info->second.window_state & window_state_show)) {
|
||||
friend_info->second.window_state |= window_state_need_attention;
|
||||
}
|
||||
|
||||
add_chat_message_notification(friend_info->first.name() + ": " + steam_message.message());
|
||||
notify_sound_user_invite(friend_info->second);
|
||||
}
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
if (loaded) {
|
||||
auto now2 = std::chrono::high_resolution_clock::now();
|
||||
auto dd = (unsigned)std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
|
||||
PRINT_DEBUG("uploaded an achievement icon to GPU in %u ms", dd);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void Steam_Overlay::steam_run_callback()
|
||||
{
|
||||
if (!Ready()) return;
|
||||
|
||||
if (overlay_state_changed) {
|
||||
overlay_state_changed = false;
|
||||
|
||||
GameOverlayActivated_t data{};
|
||||
data.m_bActive = show_overlay;
|
||||
data.m_bUserInitiated = true;
|
||||
data.m_dwOverlayPID = 123;
|
||||
data.m_nAppID = settings->get_local_game_id().AppID();
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
|
||||
Steam_Matchmaking* steamMatchmaking = get_steam_client()->steam_matchmaking;
|
||||
|
||||
if (save_settings) {
|
||||
save_settings = false;
|
||||
|
||||
const char *language_text = valid_languages[current_language];
|
||||
save_global_settings(get_steam_client()->local_storage, username_text, language_text);
|
||||
get_steam_client()->settings_client->set_local_name(username_text);
|
||||
get_steam_client()->settings_server->set_local_name(username_text);
|
||||
get_steam_client()->settings_client->set_language(language_text);
|
||||
get_steam_client()->settings_server->set_language(language_text);
|
||||
steamFriends->resend_friend_data();
|
||||
}
|
||||
|
||||
i_have_lobby = got_lobby();
|
||||
std::for_each(friends.begin(), friends.end(), [this](std::pair<Friend const, friend_window_state> &i)
|
||||
{
|
||||
i.second.joinable = is_friend_joinable(i);
|
||||
});
|
||||
|
||||
while (!has_friend_action.empty()) {
|
||||
auto friend_info = friends.find(has_friend_action.front());
|
||||
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) {
|
||||
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) {
|
||||
// Handle chat send
|
||||
Common_Message msg;
|
||||
Steam_Messages* steam_messages = new Steam_Messages;
|
||||
steam_messages->set_type(Steam_Messages::FRIEND_CHAT);
|
||||
steam_messages->set_message(friend_info->second.chat_input);
|
||||
msg.set_allocated_steam_messages(steam_messages);
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_dest_id(friend_id);
|
||||
network->sendTo(&msg, true);
|
||||
|
||||
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" (but invite all wasn't clicked)
|
||||
if (friend_info->second.window_state & window_state_invite) {
|
||||
invite_friend(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) {
|
||||
std::string connect = steamFriends->get_friend_rich_presence_silent(friend_id, "connect");
|
||||
// The user got a lobby invite and accepted it
|
||||
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);
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
|
||||
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) {
|
||||
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) {
|
||||
GameRichPresenceJoinRequested_t data = {};
|
||||
data.m_steamIDFriend.SetFromUint64(friend_id);
|
||||
strncpy(data.m_rgchConnect, connect.c_str(), k_cchMaxRichPresenceValueLength - 1);
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
//Not sure about this but it fixes sonic racing transformed invites
|
||||
FriendGameInfo_t friend_game_info = {};
|
||||
steamFriends->GetFriendGamePlayed(friend_id, &friend_game_info);
|
||||
uint64 lobby_id = friend_game_info.m_steamIDLobby.ConvertToUint64();
|
||||
if (lobby_id) {
|
||||
GameLobbyJoinRequested_t data;
|
||||
data.m_steamIDLobby.SetFromUint64(lobby_id);
|
||||
data.m_steamIDFriend.SetFromUint64(friend_id);
|
||||
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 yes_clicked = true;
|
||||
if (invite_all_friends_clicked.compare_exchange_weak(yes_clicked, false)) {
|
||||
PRINT_DEBUG("Steam_Overlay will send invitations to [%zu] friends if they're using the same app", 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();
|
||||
invite_friend(friend_id, steamFriends, steamMatchmaking);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Steam_Overlay::SetupOverlay()
|
||||
{
|
||||
if (settings->disable_overlay) return;
|
||||
@ -1838,14 +1667,14 @@ void Steam_Overlay::UnSetupOverlay()
|
||||
|
||||
PRINT_DEBUG("releasing any images resources");
|
||||
for (auto &ach : achievements) {
|
||||
if (!ach.icon.first.expired()) {
|
||||
_renderer->ReleaseImageResource(ach.icon.first);
|
||||
ach.icon.first.reset();
|
||||
if (!ach.icon.expired()) {
|
||||
_renderer->ReleaseImageResource(ach.icon);
|
||||
ach.icon.reset();
|
||||
}
|
||||
|
||||
if (!ach.icon_gray.first.expired()) {
|
||||
_renderer->ReleaseImageResource(ach.icon_gray.first);
|
||||
ach.icon_gray.first.reset();
|
||||
if (!ach.icon_gray.expired()) {
|
||||
_renderer->ReleaseImageResource(ach.icon_gray);
|
||||
ach.icon_gray.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2062,4 +1891,232 @@ void Steam_Overlay::AddAchievementNotification(const std::string &ach_name, nloh
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -- steam run callbacks --
|
||||
void Steam_Overlay::steam_run_callback_update_my_lobby()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
|
||||
if (std::string(steamFriends->get_friend_rich_presence_silent(settings->get_local_steam_id(), "connect")).length() > 0) {
|
||||
i_have_lobby = true;
|
||||
} else if (settings->get_lobby().IsValid()) {
|
||||
i_have_lobby = true;
|
||||
} else {
|
||||
i_have_lobby = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Steam_Overlay::is_friend_joinable(std::pair<const Friend, friend_window_state> &f)
|
||||
{
|
||||
PRINT_DEBUG("%" PRIu64 "", f.first.id());
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
|
||||
|
||||
if (std::string(steamFriends->get_friend_rich_presence_silent((uint64)f.first.id(), "connect")).length() > 0 ) {
|
||||
PRINT_DEBUG("%" PRIu64 " true (connect string)", f.first.id());
|
||||
return true;
|
||||
}
|
||||
|
||||
FriendGameInfo_t friend_game_info{};
|
||||
steamFriends->GetFriendGamePlayed((uint64)f.first.id(), &friend_game_info);
|
||||
if (friend_game_info.m_steamIDLobby.IsValid() && (f.second.window_state & window_state_lobby_invite)) {
|
||||
PRINT_DEBUG("%" PRIu64 " true (friend in a game)", f.first.id());
|
||||
return true;
|
||||
}
|
||||
|
||||
PRINT_DEBUG("%" PRIu64 " false", f.first.id());
|
||||
return false;
|
||||
}
|
||||
|
||||
void Steam_Overlay::invite_friend(uint64 friend_id, class Steam_Friends* steamFriends, class Steam_Matchmaking* steamMatchmaking)
|
||||
{
|
||||
std::string connect_str = steamFriends->get_friend_rich_presence_silent(settings->get_local_steam_id(), "connect");
|
||||
if (connect_str.length() > 0) {
|
||||
steamFriends->InviteUserToGame(friend_id, connect_str.c_str());
|
||||
PRINT_DEBUG("sent game invitation to friend with id = %llu", friend_id);
|
||||
} else if (settings->get_lobby().IsValid()) {
|
||||
steamMatchmaking->InviteUserToLobby(settings->get_lobby(), friend_id);
|
||||
PRINT_DEBUG("sent lobby invitation to friend with id = %llu", friend_id);
|
||||
}
|
||||
}
|
||||
|
||||
void Steam_Overlay::steam_run_callback_friends_actions()
|
||||
{
|
||||
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
|
||||
Steam_Matchmaking* steamMatchmaking = get_steam_client()->steam_matchmaking;
|
||||
|
||||
std::for_each(friends.begin(), friends.end(), [this](std::pair<Friend const, friend_window_state> &i) {
|
||||
i.second.joinable = is_friend_joinable(i);
|
||||
});
|
||||
|
||||
while (!has_friend_action.empty()) {
|
||||
auto friend_info = friends.find(has_friend_action.front());
|
||||
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) {
|
||||
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) {
|
||||
// Handle chat send
|
||||
Common_Message msg;
|
||||
Steam_Messages* steam_messages = new Steam_Messages;
|
||||
steam_messages->set_type(Steam_Messages::FRIEND_CHAT);
|
||||
steam_messages->set_message(friend_info->second.chat_input);
|
||||
msg.set_allocated_steam_messages(steam_messages);
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_dest_id(friend_id);
|
||||
network->sendTo(&msg, true);
|
||||
|
||||
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" (but invite all wasn't clicked)
|
||||
if (friend_info->second.window_state & window_state_invite) {
|
||||
invite_friend(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) {
|
||||
std::string connect = steamFriends->get_friend_rich_presence_silent(friend_id, "connect");
|
||||
// The user got a lobby invite and accepted it
|
||||
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);
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
|
||||
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) {
|
||||
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) {
|
||||
GameRichPresenceJoinRequested_t data = {};
|
||||
data.m_steamIDFriend.SetFromUint64(friend_id);
|
||||
strncpy(data.m_rgchConnect, connect.c_str(), k_cchMaxRichPresenceValueLength - 1);
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
//Not sure about this but it fixes sonic racing transformed invites
|
||||
FriendGameInfo_t friend_game_info = {};
|
||||
steamFriends->GetFriendGamePlayed(friend_id, &friend_game_info);
|
||||
uint64 lobby_id = friend_game_info.m_steamIDLobby.ConvertToUint64();
|
||||
if (lobby_id) {
|
||||
GameLobbyJoinRequested_t data;
|
||||
data.m_steamIDLobby.SetFromUint64(lobby_id);
|
||||
data.m_steamIDFriend.SetFromUint64(friend_id);
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
}
|
||||
|
||||
friend_info->second.window_state &= ~window_state_join;
|
||||
}
|
||||
}
|
||||
has_friend_action.pop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Steam_Overlay::steam_run_callback()
|
||||
{
|
||||
if (!Ready()) return;
|
||||
|
||||
if (overlay_state_changed) {
|
||||
overlay_state_changed = false;
|
||||
|
||||
GameOverlayActivated_t data{};
|
||||
data.m_bActive = show_overlay;
|
||||
data.m_bUserInitiated = true;
|
||||
data.m_dwOverlayPID = 123;
|
||||
data.m_nAppID = settings->get_local_game_id().AppID();
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
Steam_Friends* steamFriends = get_steam_client()->steam_friends;
|
||||
Steam_Matchmaking* steamMatchmaking = get_steam_client()->steam_matchmaking;
|
||||
|
||||
if (save_settings) {
|
||||
save_settings = false;
|
||||
|
||||
const char *language_text = valid_languages[current_language];
|
||||
save_global_settings(get_steam_client()->local_storage, username_text, language_text);
|
||||
get_steam_client()->settings_client->set_local_name(username_text);
|
||||
get_steam_client()->settings_server->set_local_name(username_text);
|
||||
get_steam_client()->settings_client->set_language(language_text);
|
||||
get_steam_client()->settings_server->set_language(language_text);
|
||||
steamFriends->resend_friend_data();
|
||||
}
|
||||
|
||||
steam_run_callback_update_my_lobby();
|
||||
|
||||
// if variable == true, then set it to false and return true (because state was changed) in that case
|
||||
bool yes_clicked = true;
|
||||
if (invite_all_friends_clicked.compare_exchange_weak(yes_clicked, false)) {
|
||||
PRINT_DEBUG("Steam_Overlay will send invitations to [%zu] friends if they're using the same app", 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();
|
||||
invite_friend(friend_id, steamFriends, steamMatchmaking);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't wait to lock the overlay mutex
|
||||
// * the overlay proc might be active and holding the overlay mutex
|
||||
// * this steam callback will be blocked, but it has the global mutex locked
|
||||
// * the overlay proc tries to lock the global mutex, but since we have it, it will be blocked forever
|
||||
if (overlay_mutex.try_lock()) {
|
||||
if (Ready()) {
|
||||
// ==============================================================
|
||||
// call steam callbacks that has to change the overlay state here
|
||||
// ==============================================================
|
||||
|
||||
steam_run_callback_friends_actions();
|
||||
}
|
||||
|
||||
overlay_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -- steam networking callbacks --
|
||||
void Steam_Overlay::networking_msg_received(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_steam_messages()) {
|
||||
std::lock_guard<std::recursive_mutex> lock(overlay_mutex);
|
||||
|
||||
Friend frd;
|
||||
frd.set_id(msg->source_id());
|
||||
auto friend_info = friends.find(frd);
|
||||
if (friend_info != friends.end()) {
|
||||
Steam_Messages const& steam_message = msg->steam_messages();
|
||||
// Change color to cyan for friend
|
||||
friend_info->second.chat_history.append(friend_info->first.name() + ": " + steam_message.message()).append("\n", 1);
|
||||
if (!(friend_info->second.window_state & window_state_show)) {
|
||||
friend_info->second.window_state |= window_state_need_attention;
|
||||
}
|
||||
|
||||
add_chat_message_notification(friend_info->first.name() + ": " + steam_message.message());
|
||||
notify_sound_user_invite(friend_info->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -59,12 +59,16 @@ stat_achievement_progress_functionality=1
|
||||
# also has no impact on the functions which directly change stats, achievements, or achievements progress
|
||||
# default=1
|
||||
save_only_higher_stat_achievement_progress=1
|
||||
# the emulator loads the achievements icons is memory, this is needed for APIs like `ISteamUserStats::GetAchievementIcon()`
|
||||
# the loaded icon size is controlled by [overlay::appearance] -> Icon_Size, in configs.overlay.ini
|
||||
# 1=load icons lazily when they are requested
|
||||
# 0=load icons as soon as the interface 'ISteamUserStats' is initialized
|
||||
# default=1
|
||||
lazy_load_achievements_icons=1
|
||||
# the emulator loads the achievements icons is memory, this is needed for APIs like `ISteamUserStats::GetAchievementIcon()` and the overlay
|
||||
# the loaded icon size is controlled by [overlay::appearance] -> Icon_Size, in the file configs.overlay.ini
|
||||
# this value controls how many icons to load each iteration when the periodic callback of the emu is triggered
|
||||
# or when the app/game calls `SteamAPI_RunCallbacks()`
|
||||
# each achievement has 2 icons, one when it's locked and another when it's unlocked, so a value of 10 means loading 20 icons
|
||||
# increasing this value will cause a huge startup delay
|
||||
# -1=disable this functionality (`ISteamUserStats::GetAchievementIcon()` and the overlay won't be able to use icons)
|
||||
# 0=load the icon in memory only when it's requested
|
||||
# default=10
|
||||
paginated_achievements_icons=10
|
||||
|
||||
[main::connectivity]
|
||||
# 1=prevent hooking OS networking APIs and allow any external requests
|
||||
|
@ -33,6 +33,12 @@ disable_warning_bad_appid=0
|
||||
# 1=disable the local_save warning in the overlay
|
||||
# default=0
|
||||
disable_warning_local_save=0
|
||||
# by default the overlay will attempt to upload the achievements icons to the GPU
|
||||
# so that they are displayed, in rare cases this might keep failing and cause FPS drop
|
||||
# 0=prevent the overlay from attempting to upload the icons periodically,
|
||||
# in that case achievements icons win't be displayed
|
||||
# default=1
|
||||
upload_achievements_icons_to_gpu=1
|
||||
|
||||
[overlay::appearance]
|
||||
# load custom TrueType font from a path, it could be absolute, or relative
|
||||
|
Loading…
x
Reference in New Issue
Block a user