Merge pull request #3 from otavepto/patch-1

Fixes for some crashes + behavior enhancements
This commit is contained in:
Detanup01 2024-08-03 20:53:45 +02:00 committed by GitHub
commit 39d14cd4e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 690 additions and 340 deletions

View File

@ -154,6 +154,8 @@ public:
Steam_Networking_Messages *steam_gameserver_networking_messages{}; Steam_Networking_Messages *steam_gameserver_networking_messages{};
Steam_Game_Coordinator *steam_gameserver_game_coordinator{}; Steam_Game_Coordinator *steam_gameserver_game_coordinator{};
Steam_Masterserver_Updater *steam_masterserver_updater{}; Steam_Masterserver_Updater *steam_masterserver_updater{};
Steam_GameStats *steam_gameserver_gamestats{};
Steam_AppTicket *steam_app_ticket{}; Steam_AppTicket *steam_app_ticket{};
Steam_Overlay* steam_overlay{}; Steam_Overlay* steam_overlay{};

View File

@ -26,6 +26,10 @@
class Steam_GameStats : class Steam_GameStats :
public ISteamGameStats public ISteamGameStats
{ {
private:
// how much time to wait before removing ended sessions
constexpr const static int MAX_DEAD_SESSION_SECONDS = 15; // TODO not sure what would be sensible in this case
enum class AttributeType_t enum class AttributeType_t
{ {
Int, Str, Float, Int64, Int, Str, Float, Int64,
@ -33,15 +37,15 @@ public ISteamGameStats
struct Attribute_t struct Attribute_t
{ {
AttributeType_t type{}; const AttributeType_t type;
union { union {
int32 n_data; int32 n_data;
std::string s_data; std::string s_data;
float f_data; float f_data;
int64 ll_data{}; int64 ll_data;
}; };
Attribute_t(); Attribute_t(AttributeType_t type);
Attribute_t(const Attribute_t &other); Attribute_t(const Attribute_t &other);
Attribute_t(Attribute_t &&other); Attribute_t(Attribute_t &&other);
~Attribute_t(); ~Attribute_t();
@ -76,13 +80,15 @@ public ISteamGameStats
class SteamCallBacks *callbacks{}; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb{}; class RunEveryRunCB *run_every_runcb{};
std::vector<Session_t> sessions{}; std::map<uint64, Session_t> sessions{};
uint64 create_session_id() const;
bool valid_stats_account_type(int8 nAccountType); bool valid_stats_account_type(int8 nAccountType);
Table_t *get_or_create_session_table(Session_t &session, const char *table_name); Table_t *get_or_create_session_table(Session_t &session, const char *table_name);
Attribute_t *get_or_create_session_att(const char *att_name, Session_t &session, AttributeType_t type_if_create); Attribute_t *get_or_create_session_att(const char *att_name, Session_t &session, AttributeType_t type_if_create);
Attribute_t *get_or_create_row_att(uint64 ulRowID, const char *att_name, Table_t &table, AttributeType_t type_if_create); Attribute_t *get_or_create_row_att(uint64 ulRowID, const char *att_name, Table_t &table, AttributeType_t type_if_create);
Session_t* get_last_active_session();
void steam_run_callback(); void steam_run_callback();
@ -92,6 +98,7 @@ public ISteamGameStats
static void steam_gamestats_network_low_level(void *object, Common_Message *msg); static void steam_gamestats_network_low_level(void *object, Common_Message *msg);
static void steam_gamestats_run_every_runcb(void *object); static void steam_gamestats_run_every_runcb(void *object);
public: public:
Steam_GameStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb); Steam_GameStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
~Steam_GameStats(); ~Steam_GameStats();

View File

@ -36,30 +36,29 @@ struct Stream_Write {
}; };
struct Downloaded_File { struct Downloaded_File {
// --- these are needed due to the usage of union enum class DownloadSource {
Downloaded_File();
~Downloaded_File();
// ---
enum DownloadSource {
AfterFileShare, // attempted download after a call to Steam_Remote_Storage::FileShare() AfterFileShare, // attempted download after a call to Steam_Remote_Storage::FileShare()
AfterSendQueryUGCRequest, // attempted download after a call to Steam_UGC::SendQueryUGCRequest() AfterSendQueryUGCRequest, // attempted download after a call to Steam_UGC::SendQueryUGCRequest()
FromUGCDownloadToLocation, // attempted download via Steam_Remote_Storage::UGCDownloadToLocation() FromUGCDownloadToLocation, // attempted download via Steam_Remote_Storage::UGCDownloadToLocation()
} source{}; };
private:
DownloadSource source;
public:
Downloaded_File(DownloadSource src);
DownloadSource get_source() const;
// *** used in any case // *** used in any case
std::string file{}; std::string file{};
uint64 total_size{}; uint64 total_size{};
// put any additional data needed by other sources here // *** used when source = AfterSendQueryUGCRequest and FromUGCDownloadToLocation
Ugc_Remote_Storage_Bridge::QueryInfo mod_query_info{};
union {
// *** used when source = SendQueryUGCRequest only
Ugc_Remote_Storage_Bridge::QueryInfo mod_query_info;
// *** used when source = FromUGCDownloadToLocation only // *** used when source = FromUGCDownloadToLocation only
std::string download_to_location_fullpath; std::string download_to_location_fullpath{};
};
}; };
@ -85,6 +84,7 @@ private:
class Ugc_Remote_Storage_Bridge *ugc_bridge{}; class Ugc_Remote_Storage_Bridge *ugc_bridge{};
class Local_Storage *local_storage{}; class Local_Storage *local_storage{};
class SteamCallResults *callback_results{}; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{};
std::vector<struct Async_Read> async_reads{}; std::vector<struct Async_Read> async_reads{};
std::vector<struct Stream_Write> stream_writes{}; std::vector<struct Stream_Write> stream_writes{};
@ -95,7 +95,7 @@ private:
public: public:
Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results); Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks);
// NOTE // NOTE
// //

View File

@ -24,6 +24,8 @@ class Steam_Timeline :
public ISteamTimeline public ISteamTimeline
{ {
private: private:
constexpr const static float PRIORITY_CLIP_MIN_SEC = 8.0f;
struct TimelineEvent_t struct TimelineEvent_t
{ {
// emu specific: time when this event was added to the list via 'Steam_Timeline::AddTimelineEvent()' // emu specific: time when this event was added to the list via 'Steam_Timeline::AddTimelineEvent()'
@ -68,7 +70,7 @@ private:
class RunEveryRunCB *run_every_runcb{}; class RunEveryRunCB *run_every_runcb{};
std::vector<TimelineEvent_t> timeline_events{}; std::vector<TimelineEvent_t> timeline_events{};
std::vector<TimelineState_t> timeline_states{TimelineState_t{}}; // it seems to always start with a default event std::vector<TimelineState_t> timeline_states{};
// unconditional periodic callback // unconditional periodic callback
void RunCallbacks(); void RunCallbacks();

View File

@ -1114,7 +1114,7 @@ static void try_detect_mods_folder(class Settings *settings_client, Settings *se
PRINT_DEBUG(" preview_filename: '%s'", newMod.previewFileName.c_str()); PRINT_DEBUG(" preview_filename: '%s'", newMod.previewFileName.c_str());
PRINT_DEBUG(" preview_filesize: %i bytes", newMod.previewFileSize); PRINT_DEBUG(" preview_filesize: %i bytes", newMod.previewFileSize);
PRINT_DEBUG(" preview file handle: %llu", settings_client->getMod(newMod.id).handlePreviewFile); PRINT_DEBUG(" preview file handle: %llu", settings_client->getMod(newMod.id).handlePreviewFile);
PRINT_DEBUG(" total_files_sizes: '%s'", newMod.total_files_sizes); PRINT_DEBUG(" total_files_sizes: '%llu'", newMod.total_files_sizes);
PRINT_DEBUG(" min_game_branch: '%s'", newMod.min_game_branch.c_str()); PRINT_DEBUG(" min_game_branch: '%s'", newMod.min_game_branch.c_str());
PRINT_DEBUG(" max_game_branch: '%s'", newMod.max_game_branch.c_str()); PRINT_DEBUG(" max_game_branch: '%s'", newMod.max_game_branch.c_str());
PRINT_DEBUG(" workshop_item_url: '%s'", newMod.workshopItemURL.c_str()); PRINT_DEBUG(" workshop_item_url: '%s'", newMod.workshopItemURL.c_str());
@ -1379,8 +1379,8 @@ static void parse_simple_features(class Settings *settings_client, class Setting
settings_client->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_client->matchmaking_server_details_via_source_query); settings_client->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_client->matchmaking_server_details_via_source_query);
settings_server->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_server->matchmaking_server_details_via_source_query); settings_server->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_server->matchmaking_server_details_via_source_query);
settings_client->matchmaking_server_list_always_lan_type = ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", settings_client->matchmaking_server_list_always_lan_type); settings_client->matchmaking_server_list_always_lan_type = !ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", !settings_client->matchmaking_server_list_always_lan_type);
settings_server->matchmaking_server_list_always_lan_type = ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", settings_server->matchmaking_server_list_always_lan_type); settings_server->matchmaking_server_list_always_lan_type = !ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", !settings_server->matchmaking_server_list_always_lan_type);
// [main::connectivity] // [main::connectivity]

View File

@ -100,7 +100,7 @@ Steam_Client::Steam_Client()
steam_user_stats = new Steam_User_Stats(settings_client, network, local_storage, callback_results_client, callbacks_client, run_every_runcb, steam_overlay); steam_user_stats = new Steam_User_Stats(settings_client, network, local_storage, callback_results_client, callbacks_client, run_every_runcb, steam_overlay);
steam_apps = new Steam_Apps(settings_client, callback_results_client, callbacks_client); steam_apps = new Steam_Apps(settings_client, callback_results_client, callbacks_client);
steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb); steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb);
steam_remote_storage = new Steam_Remote_Storage(settings_client, ugc_bridge, local_storage, callback_results_client); steam_remote_storage = new Steam_Remote_Storage(settings_client, ugc_bridge, local_storage, callback_results_client, callbacks_client);
steam_screenshots = new Steam_Screenshots(local_storage, callbacks_client); steam_screenshots = new Steam_Screenshots(local_storage, callbacks_client);
steam_http = new Steam_HTTP(settings_client, network, callback_results_client, callbacks_client); steam_http = new Steam_HTTP(settings_client, network, callback_results_client, callbacks_client);
steam_controller = new Steam_Controller(settings_client, callback_results_client, callbacks_client, run_every_runcb); steam_controller = new Steam_Controller(settings_client, callback_results_client, callbacks_client, run_every_runcb);
@ -143,6 +143,7 @@ Steam_Client::Steam_Client()
steam_gameserver_networking_messages = new Steam_Networking_Messages(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); steam_gameserver_networking_messages = new Steam_Networking_Messages(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
steam_gameserver_game_coordinator = new Steam_Game_Coordinator(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); steam_gameserver_game_coordinator = new Steam_Game_Coordinator(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
steam_masterserver_updater = new Steam_Masterserver_Updater(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); steam_masterserver_updater = new Steam_Masterserver_Updater(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
steam_gameserver_gamestats = new Steam_GameStats(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
PRINT_DEBUG("init AppTicket"); PRINT_DEBUG("init AppTicket");
steam_app_ticket = new Steam_AppTicket(settings_client); steam_app_ticket = new Steam_AppTicket(settings_client);

View File

@ -37,8 +37,16 @@ ISteamGameStats *Steam_Client::GetISteamGameStats( HSteamUser hSteamUser, HSteam
PRINT_DEBUG("%s", pchVersion); PRINT_DEBUG("%s", pchVersion);
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return nullptr; if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return nullptr;
Steam_GameStats *steam_gamestats_tmp{};
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
steam_gamestats_tmp = steam_gameserver_gamestats;
} else {
steam_gamestats_tmp = steam_gamestats;
}
if (strcmp(pchVersion, STEAMGAMESTATS_INTERFACE_VERSION) == 0) { if (strcmp(pchVersion, STEAMGAMESTATS_INTERFACE_VERSION) == 0) {
return reinterpret_cast<ISteamGameStats *>(static_cast<ISteamGameStats *>(steam_gamestats)); return reinterpret_cast<ISteamGameStats *>(static_cast<ISteamGameStats *>(steam_gamestats_tmp));
} }
report_missing_impl_and_exit(pchVersion, EMU_FUNC_NAME); report_missing_impl_and_exit(pchVersion, EMU_FUNC_NAME);
@ -249,13 +257,15 @@ ISteamMatchmakingServers *Steam_Client::GetISteamMatchmakingServers( HSteamUser
// returns the a generic interface // returns the a generic interface
void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{ {
PRINT_DEBUG("%s", pchVersion); PRINT_DEBUG("'%s' %i %i", pchVersion, hSteamUser, hSteamPipe);
if (!steam_pipes.count(hSteamPipe)) return NULL; if (!steam_pipes.count(hSteamPipe)) return NULL;
bool server = false; bool server = false;
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) { if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
// PRINT_DEBUG("requesting interface with server pipe");
server = true; server = true;
} else { } else {
// PRINT_DEBUG("requesting interface with client pipe");
// if this is a user pipe, and version != "SteamNetworkingUtils", and version != "SteamUtils" // if this is a user pipe, and version != "SteamNetworkingUtils", and version != "SteamUtils"
if ((strstr(pchVersion, "SteamNetworkingUtils") != pchVersion) && (strstr(pchVersion, "SteamUtils") != pchVersion)) { if ((strstr(pchVersion, "SteamNetworkingUtils") != pchVersion) && (strstr(pchVersion, "SteamUtils") != pchVersion)) {
if (!hSteamUser) return NULL; if (!hSteamUser) return NULL;

View File

@ -209,21 +209,16 @@ SteamAPICall_t Steam_Friends::SetPersonaName( const char *pchPersonaName )
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
// send PersonaStateChange_t callbacks
persona_change(settings->get_local_steam_id(), EPersonaChange::k_EPersonaChangeName);
SetPersonaNameResponse_t data{}; SetPersonaNameResponse_t data{};
data.m_bSuccess = true; data.m_bSuccess = true;
data.m_bLocalSuccess = false; data.m_bLocalSuccess = false;
data.m_result = k_EResultOK; data.m_result = k_EResultOK;
persona_change(settings->get_local_steam_id(), k_EPersonaChangeName);
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
{
PersonaStateChange_t data2{};
data2.m_nChangeFlags = EPersonaChange::k_EPersonaChangeName;
data2.m_ulSteamID = settings->get_local_steam_id().ConvertToUint64();
callbacks->addCBResult(data2.k_iCallback, &data2, sizeof(data2));
}
return ret; return ret;
} }
@ -1032,7 +1027,10 @@ SteamAPICall_t Steam_Friends::JoinClanChatRoom( CSteamID steamIDClan )
JoinClanChatRoomCompletionResult_t data; JoinClanChatRoomCompletionResult_t data;
data.m_steamIDClanChat = steamIDClan; data.m_steamIDClanChat = steamIDClan;
data.m_eChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess; data.m_eChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
bool Steam_Friends::LeaveClanChatRoom( CSteamID steamIDClan ) bool Steam_Friends::LeaveClanChatRoom( CSteamID steamIDClan )

View File

@ -24,39 +24,54 @@
#include "dll/steam_gamestats.h" #include "dll/steam_gamestats.h"
Steam_GameStats::Attribute_t::Attribute_t() Steam_GameStats::Attribute_t::Attribute_t(AttributeType_t type)
{ } :type(type)
{
switch (type)
{
case AttributeType_t::Float: f_data = 0; break;
case AttributeType_t::Int64: ll_data = 0; break;
case AttributeType_t::Int: n_data = 0; break;
case AttributeType_t::Str: new (&s_data) std::string{}; break;
default: PRINT_DEBUG("[X] invalid type %i", (int)type); break;
}
}
Steam_GameStats::Attribute_t::Attribute_t(const Attribute_t &other) Steam_GameStats::Attribute_t::Attribute_t(const Attribute_t &other)
:type(type)
{ {
type = other.type;
switch (other.type) switch (other.type)
{ {
case AttributeType_t::Int: n_data = other.n_data; break; case AttributeType_t::Int: n_data = other.n_data; break;
case AttributeType_t::Str: s_data = other.s_data; break; case AttributeType_t::Str: new (&s_data) std::string(other.s_data); break;
case AttributeType_t::Float: f_data = other.f_data; break; case AttributeType_t::Float: f_data = other.f_data; break;
case AttributeType_t::Int64: ll_data = other.ll_data; break; case AttributeType_t::Int64: ll_data = other.ll_data; break;
default: break; default: PRINT_DEBUG("[X] invalid type %i", (int)other.type); break;
} }
} }
Steam_GameStats::Attribute_t::Attribute_t(Attribute_t &&other) Steam_GameStats::Attribute_t::Attribute_t(Attribute_t &&other)
:type(type)
{ {
type = other.type;
switch (other.type) switch (other.type)
{ {
case AttributeType_t::Int: n_data = other.n_data; break; case AttributeType_t::Int: n_data = other.n_data; break;
case AttributeType_t::Str: s_data = std::move(other.s_data); break; case AttributeType_t::Str: new (&s_data) std::string(std::move(other.s_data)); break;
case AttributeType_t::Float: f_data = other.f_data; break; case AttributeType_t::Float: f_data = other.f_data; break;
case AttributeType_t::Int64: ll_data = other.ll_data; break; case AttributeType_t::Int64: ll_data = other.ll_data; break;
default: break; default: PRINT_DEBUG("[X] invalid type %i", (int)other.type); break;
} }
} }
Steam_GameStats::Attribute_t::~Attribute_t() Steam_GameStats::Attribute_t::~Attribute_t()
{ } {
if (type == AttributeType_t::Str) {
s_data.~basic_string();
}
}
void Steam_GameStats::steam_gamestats_network_low_level(void *object, Common_Message *msg) void Steam_GameStats::steam_gamestats_network_low_level(void *object, Common_Message *msg)
@ -83,18 +98,26 @@ Steam_GameStats::Steam_GameStats(class Settings *settings, class Networking *net
this->callbacks = callbacks; this->callbacks = callbacks;
this->run_every_runcb = run_every_runcb; this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_GameStats::steam_gamestats_network_low_level, this); // this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_GameStats::steam_gamestats_network_low_level, this);
this->run_every_runcb->add(&Steam_GameStats::steam_gamestats_run_every_runcb, this); this->run_every_runcb->add(&Steam_GameStats::steam_gamestats_run_every_runcb, this);
} }
Steam_GameStats::~Steam_GameStats() Steam_GameStats::~Steam_GameStats()
{ {
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_GameStats::steam_gamestats_network_low_level, this); // this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_GameStats::steam_gamestats_network_low_level, this);
this->run_every_runcb->remove(&Steam_GameStats::steam_gamestats_run_every_runcb, this); this->run_every_runcb->remove(&Steam_GameStats::steam_gamestats_run_every_runcb, this);
} }
uint64 Steam_GameStats::create_session_id() const
{
static uint64 session_id = 0;
session_id++;
if (!session_id) session_id = 1; // not sure if 0 is a good idea
return session_id;
}
bool Steam_GameStats::valid_stats_account_type(int8 nAccountType) bool Steam_GameStats::valid_stats_account_type(int8 nAccountType)
{ {
switch ((EGameStatsAccountType)nAccountType) { switch ((EGameStatsAccountType)nAccountType) {
@ -113,8 +136,8 @@ Steam_GameStats::Table_t *Steam_GameStats::get_or_create_session_table(Session_t
{ {
auto table_it = std::find_if(session.tables.rbegin(), session.tables.rend(), [=](const std::pair<std::string, Table_t> &item){ return item.first == table_name; }); auto table_it = std::find_if(session.tables.rbegin(), session.tables.rend(), [=](const std::pair<std::string, Table_t> &item){ return item.first == table_name; });
if (session.tables.rend() == table_it) { if (session.tables.rend() == table_it) {
session.tables.emplace_back(std::pair<std::string, Table_t>{}); auto& [key, val] = session.tables.emplace_back(std::pair<std::string, Table_t>{});
table = &session.tables.back().second; table = &val;
} else { } else {
table = &table_it->second; table = &table_it->second;
} }
@ -125,40 +148,29 @@ Steam_GameStats::Table_t *Steam_GameStats::get_or_create_session_table(Session_t
Steam_GameStats::Attribute_t *Steam_GameStats::get_or_create_session_att(const char *att_name, Session_t &session, AttributeType_t type_if_create) Steam_GameStats::Attribute_t *Steam_GameStats::get_or_create_session_att(const char *att_name, Session_t &session, AttributeType_t type_if_create)
{ {
Attribute_t *att{}; auto [ele_it, _] = session.attributes.emplace(att_name, type_if_create);
{ return &ele_it->second;
auto att_itr = session.attributes.find(att_name);
if (att_itr != session.attributes.end()) {
att = &att_itr->second;
} else {
att = &session.attributes[att_name];
att->type = type_if_create;
}
}
return att;
} }
Steam_GameStats::Attribute_t *Steam_GameStats::get_or_create_row_att(uint64 ulRowID, const char *att_name, Table_t &table, AttributeType_t type_if_create) Steam_GameStats::Attribute_t *Steam_GameStats::get_or_create_row_att(uint64 ulRowID, const char *att_name, Table_t &table, AttributeType_t type_if_create)
{
Attribute_t *att{};
{ {
auto &row = table.rows[static_cast<unsigned>(ulRowID)]; auto &row = table.rows[static_cast<unsigned>(ulRowID)];
auto att_itr = row.attributes.find(att_name); auto [ele_it, _] = row.attributes.emplace(att_name, type_if_create);
if (att_itr != row.attributes.end()) { return &ele_it->second;
att = &att_itr->second;
} else {
att = &row.attributes[att_name];
att->type = type_if_create;
}
} }
return att; Steam_GameStats::Session_t* Steam_GameStats::get_last_active_session()
{
auto active_session_it = std::find_if(sessions.rbegin(), sessions.rend(), [](std::pair<const uint64, Steam_GameStats::Session_t> item){ return !item.second.ended; });
if (sessions.rend() == active_session_it) return nullptr; // TODO is this correct?
return &active_session_it->second;
} }
SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted ) SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted )
{ {
// appid 550 calls this function once with client account id, and another time with server account id
PRINT_DEBUG("%i, %llu, %i, %u", (int)nAccountType, ulAccountID, nAppID, rtTimeStarted); PRINT_DEBUG("%i, %llu, %i, %u", (int)nAccountType, ulAccountID, nAppID, rtTimeStarted);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
@ -166,11 +178,13 @@ SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccou
(nAppID < 0) || (nAppID < 0) ||
(settings->get_local_game_id().AppID() != (uint32)nAppID) || (settings->get_local_game_id().AppID() != (uint32)nAppID) ||
!valid_stats_account_type(nAccountType)) { !valid_stats_account_type(nAccountType)) {
GameStatsSessionIssued_t data_invalid{}; GameStatsSessionIssued_t data_invalid{};
data_invalid.m_bCollectingAny = false; data_invalid.m_bCollectingAny = false;
data_invalid.m_bCollectingDetails = false; data_invalid.m_bCollectingDetails = false;
data_invalid.m_eResult = EResult::k_EResultInvalidParam; data_invalid.m_eResult = EResult::k_EResultInvalidParam;
data_invalid.m_ulSessionID = 0; data_invalid.m_ulSessionID = 0;
PRINT_DEBUG("[X] invalid param");
auto ret = callback_results->addCallResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid)); auto ret = callback_results->addCallResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid));
// the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected
@ -178,16 +192,18 @@ SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccou
return ret; return ret;
} }
auto session_id = create_session_id();
Session_t new_session{}; Session_t new_session{};
new_session.nAccountType = (EGameStatsAccountType)nAccountType; new_session.nAccountType = (EGameStatsAccountType)nAccountType;
new_session.rtTimeStarted = rtTimeStarted; new_session.rtTimeStarted = rtTimeStarted;
sessions.emplace_back(new_session); sessions.insert_or_assign(session_id, new_session);
GameStatsSessionIssued_t data{}; GameStatsSessionIssued_t data{};
data.m_bCollectingAny = true; // TODO is this correct? data.m_bCollectingAny = true; // TODO is this correct?
data.m_bCollectingDetails = true; // TODO is this correct? data.m_bCollectingDetails = true; // TODO is this correct?
data.m_eResult = EResult::k_EResultOK; data.m_eResult = EResult::k_EResultOK;
data.m_ulSessionID = (uint64)sessions.size(); // I don't know if 0 is a bad value, so always send count (index + 1) data.m_ulSessionID = session_id;
PRINT_DEBUG("new session id = %llu", session_id);
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
// the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected
@ -200,10 +216,12 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn
PRINT_DEBUG("%llu, %u, %i", ulSessionID, rtTimeEnded, nReasonCode); PRINT_DEBUG("%llu, %u, %i", ulSessionID, rtTimeEnded, nReasonCode);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (ulSessionID == 0 || ulSessionID > sessions.size()) { auto session_it = sessions.find(ulSessionID);
if (sessions.end() == session_it) {
GameStatsSessionClosed_t data_invalid{}; GameStatsSessionClosed_t data_invalid{};
data_invalid.m_eResult = EResult::k_EResultInvalidParam; data_invalid.m_eResult = EResult::k_EResultInvalidParam;
data_invalid.m_ulSessionID = ulSessionID; data_invalid.m_ulSessionID = ulSessionID;
PRINT_DEBUG("[X] session doesn't exist");
auto ret = callback_results->addCallResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid)); auto ret = callback_results->addCallResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid));
// the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected
@ -211,7 +229,7 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn
return ret; return ret;
} }
auto& session = sessions[static_cast<unsigned>(ulSessionID - 1)]; auto& session = session_it->second;
if (session.ended) { if (session.ended) {
GameStatsSessionClosed_t data_invalid{}; GameStatsSessionClosed_t data_invalid{};
data_invalid.m_eResult = EResult::k_EResultExpired; // TODO is this correct? data_invalid.m_eResult = EResult::k_EResultExpired; // TODO is this correct?
@ -230,6 +248,7 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn
GameStatsSessionClosed_t data{}; GameStatsSessionClosed_t data{};
data.m_eResult = EResult::k_EResultOK; data.m_eResult = EResult::k_EResultOK;
data.m_ulSessionID = ulSessionID; data.m_ulSessionID = ulSessionID;
PRINT_DEBUG("ended session");
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
// the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected
@ -239,69 +258,77 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn
EResult Steam_GameStats::AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData ) EResult Steam_GameStats::AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData )
{ {
PRINT_DEBUG("%llu, '%s', %i", ulSessionID, pstrName, nData); PRINT_DEBUG("%llu, '%s'=%i", ulSessionID, pstrName, nData);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? auto session_it = sessions.find(ulSessionID);
if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto& session = sessions[static_cast<unsigned>(ulSessionID - 1)]; auto& session = session_it->second;
if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? if (session.ended) return EResult::k_EResultExpired; // TODO is this correct?
auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Int); auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Int);
if (att->type != AttributeType_t::Int) return EResult::k_EResultFail; // TODO is this correct? if (att->type != AttributeType_t::Int) return EResult::k_EResultFail; // TODO is this correct?
att->n_data = nData; att->n_data = nData;
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
EResult Steam_GameStats::AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData ) EResult Steam_GameStats::AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData )
{ {
PRINT_DEBUG("%llu, '%s', '%s'", ulSessionID, pstrName, pstrData); PRINT_DEBUG("%llu, '%s'='%s'", ulSessionID, pstrName, pstrData);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrName || !pstrData) return EResult::k_EResultInvalidParam; // TODO is this correct? auto session_it = sessions.find(ulSessionID);
if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto& session = sessions[static_cast<unsigned>(ulSessionID - 1)]; auto& session = session_it->second;
if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? if (session.ended) return EResult::k_EResultExpired; // TODO is this correct?
auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Str); auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Str);
if (att->type != AttributeType_t::Str) return EResult::k_EResultFail; // TODO is this correct? if (att->type != AttributeType_t::Str) return EResult::k_EResultFail; // TODO is this correct?
att->s_data = pstrData; att->s_data = pstrData;
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
EResult Steam_GameStats::AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData ) EResult Steam_GameStats::AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData )
{ {
PRINT_DEBUG("%llu, '%s', %f", ulSessionID, pstrName, fData); PRINT_DEBUG("%llu, '%s'=%f", ulSessionID, pstrName, fData);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? auto session_it = sessions.find(ulSessionID);
if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto& session = sessions[static_cast<unsigned>(ulSessionID - 1)]; auto& session = session_it->second;
if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? if (session.ended) return EResult::k_EResultExpired; // TODO is this correct?
auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Float); auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Float);
if (att->type != AttributeType_t::Float) return EResult::k_EResultFail; // TODO is this correct? if (att->type != AttributeType_t::Float) return EResult::k_EResultFail; // TODO is this correct?
att->f_data = fData; att->f_data = fData;
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
EResult Steam_GameStats::AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName ) EResult Steam_GameStats::AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName )
{ {
PRINT_DEBUG("%p, %llu, '%s'", pulRowID, ulSessionID, pstrTableName); PRINT_DEBUG("%p, %llu, ['%s']", pulRowID, ulSessionID, pstrTableName);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrTableName) return EResult::k_EResultInvalidParam; // TODO is this correct? auto session_it = sessions.find(ulSessionID);
if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto& session = sessions[static_cast<unsigned>(ulSessionID - 1)]; auto& session = session_it->second;
if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? if (session.ended) return EResult::k_EResultExpired; // TODO is this correct?
auto table = get_or_create_session_table(session, pstrTableName); auto table = get_or_create_session_table(session, pstrTableName);
table->rows.emplace_back(Row_t{}); table->rows.emplace_back(Row_t{});
if (pulRowID) *pulRowID = (uint64)(table->rows.size() - 1); if (pulRowID) *pulRowID = (uint64)(table->rows.size() - 1);
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
@ -310,8 +337,8 @@ EResult Steam_GameStats::CommitRow( uint64 ulRowID )
PRINT_DEBUG("%llu", ulRowID); PRINT_DEBUG("%llu", ulRowID);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); auto active_session = get_last_active_session();
if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? if (!active_session) return EResult::k_EResultFail; // TODO is this correct?
if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct? if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct?
auto &table = active_session->tables.back().second; auto &table = active_session->tables.back().second;
@ -321,6 +348,7 @@ EResult Steam_GameStats::CommitRow( uint64 ulRowID )
auto& row = table.rows[static_cast<unsigned>(ulRowID)]; auto& row = table.rows[static_cast<unsigned>(ulRowID)];
row.committed = true; row.committed = true;
PRINT_DEBUG("committed successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
@ -329,9 +357,10 @@ EResult Steam_GameStats::CommitOutstandingRows( uint64 ulSessionID )
PRINT_DEBUG("%llu", ulSessionID); PRINT_DEBUG("%llu", ulSessionID);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (ulSessionID == 0 || ulSessionID > sessions.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? auto session_it = sessions.find(ulSessionID);
if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto& session = sessions[static_cast<unsigned>(ulSessionID - 1)]; auto& session = session_it->second;
if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? if (session.ended) return EResult::k_EResultExpired; // TODO is this correct?
if (session.tables.size()) { if (session.tables.size()) {
@ -339,18 +368,19 @@ EResult Steam_GameStats::CommitOutstandingRows( uint64 ulSessionID )
row.committed = true; row.committed = true;
} }
} }
PRINT_DEBUG("committed all successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
EResult Steam_GameStats::AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData ) EResult Steam_GameStats::AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData )
{ {
PRINT_DEBUG("%llu, '%s', %i", ulRowID, pstrName, nData); PRINT_DEBUG("%llu, '%s'=%i", ulRowID, pstrName, nData);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); auto active_session = get_last_active_session();
if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? if (!active_session) return EResult::k_EResultFail; // TODO is this correct?
if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct? if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct?
auto &table = active_session->tables.back().second; auto &table = active_session->tables.back().second;
@ -360,18 +390,20 @@ EResult Steam_GameStats::AddRowAttributeInt( uint64 ulRowID, const char *pstrNam
if (att->type != AttributeType_t::Int) return EResult::k_EResultFail; // TODO is this correct? if (att->type != AttributeType_t::Int) return EResult::k_EResultFail; // TODO is this correct?
att->n_data = nData; att->n_data = nData;
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
EResult Steam_GameStats::AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData ) EResult Steam_GameStats::AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData )
{ {
PRINT_DEBUG("%llu, '%s', '%s'", ulRowID, pstrName, pstrData); PRINT_DEBUG("%llu, '%s'='%s'", ulRowID, pstrName, pstrData);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (!pstrName || !pstrData) return EResult::k_EResultInvalidParam; // TODO is this correct? if (!pstrName || !pstrData) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); auto active_session = get_last_active_session();
if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? if (!active_session) return EResult::k_EResultFail; // TODO is this correct?
if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct?
auto &table = active_session->tables.back().second; auto &table = active_session->tables.back().second;
if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct?
@ -380,18 +412,20 @@ EResult Steam_GameStats::AddRowAtributeString( uint64 ulRowID, const char *pstrN
if (att->type != AttributeType_t::Str) return EResult::k_EResultFail; // TODO is this correct? if (att->type != AttributeType_t::Str) return EResult::k_EResultFail; // TODO is this correct?
att->s_data = pstrData; att->s_data = pstrData;
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
EResult Steam_GameStats::AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData ) EResult Steam_GameStats::AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData )
{ {
PRINT_DEBUG("%llu, '%s', %f", ulRowID, pstrName, fData); PRINT_DEBUG("%llu, '%s'=%f", ulRowID, pstrName, fData);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); auto active_session = get_last_active_session();
if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? if (!active_session) return EResult::k_EResultFail; // TODO is this correct?
if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct?
auto &table = active_session->tables.back().second; auto &table = active_session->tables.back().second;
if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct?
@ -400,36 +434,40 @@ EResult Steam_GameStats::AddRowAttributeFloat( uint64 ulRowID, const char *pstrN
if (att->type != AttributeType_t::Float) return EResult::k_EResultFail; // TODO is this correct? if (att->type != AttributeType_t::Float) return EResult::k_EResultFail; // TODO is this correct?
att->f_data = fData; att->f_data = fData;
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
EResult Steam_GameStats::AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData ) EResult Steam_GameStats::AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData )
{ {
PRINT_DEBUG("%llu, '%s', %lli", ulSessionID, pstrName, llData); PRINT_DEBUG("%llu, '%s'=%lli", ulSessionID, pstrName, llData);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? auto session_it = sessions.find(ulSessionID);
if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto& session = sessions[static_cast<unsigned>(ulSessionID - 1)]; auto& session = session_it->second;
if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? if (session.ended) return EResult::k_EResultExpired; // TODO is this correct?
auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Int64); auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Int64);
if (att->type != AttributeType_t::Int64) return EResult::k_EResultFail; // TODO is this correct? if (att->type != AttributeType_t::Int64) return EResult::k_EResultFail; // TODO is this correct?
att->ll_data = llData; att->ll_data = llData;
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
EResult Steam_GameStats::AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData ) EResult Steam_GameStats::AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData )
{ {
PRINT_DEBUG("%llu, '%s', %lli", ulRowID, pstrName, llData); PRINT_DEBUG("%llu, '%s'=%lli", ulRowID, pstrName, llData);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct?
auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); auto active_session = get_last_active_session();
if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? if (!active_session) return EResult::k_EResultFail; // TODO is this correct?
if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct?
auto &table = active_session->tables.back().second; auto &table = active_session->tables.back().second;
if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct?
@ -438,6 +476,7 @@ EResult Steam_GameStats::AddRowAttributeInt64( uint64 ulRowID, const char *pstrN
if (att->type != AttributeType_t::Int64) return EResult::k_EResultFail; // TODO is this correct? if (att->type != AttributeType_t::Int64) return EResult::k_EResultFail; // TODO is this correct?
att->ll_data = llData; att->ll_data = llData;
PRINT_DEBUG("added successfully");
return EResult::k_EResultOK; return EResult::k_EResultOK;
} }
@ -447,7 +486,28 @@ EResult Steam_GameStats::AddRowAttributeInt64( uint64 ulRowID, const char *pstrN
void Steam_GameStats::steam_run_callback() void Steam_GameStats::steam_run_callback()
{ {
// nothing // remove ended sessions that are inactive
auto session_it = sessions.begin();
auto now_epoch_sec = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()
);
while (sessions.end() != session_it) {
bool should_remove = false;
auto &session = session_it->second;
if (session.ended) {
if ( (now_epoch_sec.count() - (long long)session.rtTimeEnded) >= MAX_DEAD_SESSION_SECONDS ) {
should_remove = true;
PRINT_DEBUG("removing outdated session id=%llu", session_it->first);
}
}
if (should_remove) {
session_it = sessions.erase(session_it);
} else {
++session_it;
}
}
} }

View File

@ -180,7 +180,9 @@ void Steam_HTTP::online_http_request(Steam_Http_Request *request, SteamAPICall_t
data.m_eStatusCode = k_EHTTPStatusCode200OK; data.m_eStatusCode = k_EHTTPStatusCode200OK;
} }
if (pCallHandle) *pCallHandle = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1); auto callres = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
if (pCallHandle) *pCallHandle = callres;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1); callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
}; };
@ -356,7 +358,9 @@ bool Steam_HTTP::SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pC
data.m_eStatusCode = k_EHTTPStatusCode200OK; data.m_eStatusCode = k_EHTTPStatusCode200OK;
} }
if (pCallHandle) *pCallHandle = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1); auto callres = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
if (pCallHandle) *pCallHandle = callres;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1); callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
} }

View File

@ -72,7 +72,9 @@ SteamAPICall_t Steam_Networking_Sockets_Serialized::GetCertAsync()
struct SteamNetworkingSocketsCert_t data = {}; struct SteamNetworkingSocketsCert_t data = {};
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
int Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON( void *buf, uint32 cbBuf, const char *pszLauncherPartner ) int Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON( void *buf, uint32 cbBuf, const char *pszLauncherPartner )

View File

@ -18,14 +18,13 @@
#include "dll/steam_remote_storage.h" #include "dll/steam_remote_storage.h"
Downloaded_File::Downloaded_File() Downloaded_File::Downloaded_File(DownloadSource src)
:source(src)
{ }
Downloaded_File::DownloadSource Downloaded_File::get_source() const
{ {
return source;
}
Downloaded_File::~Downloaded_File()
{
} }
static void copy_file(const std::string &src_filepath, const std::string &dst_filepath) static void copy_file(const std::string &src_filepath, const std::string &dst_filepath)
@ -43,12 +42,13 @@ static void copy_file(const std::string &src_filepath, const std::string &dst_fi
} catch(...) {} } catch(...) {}
} }
Steam_Remote_Storage::Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results) Steam_Remote_Storage::Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{ {
this->settings = settings; this->settings = settings;
this->ugc_bridge = ugc_bridge; this->ugc_bridge = ugc_bridge;
this->local_storage = local_storage; this->local_storage = local_storage;
this->callback_results = callback_results; this->callback_results = callback_results;
this->callbacks = callbacks;
steam_cloud_enabled = true; steam_cloud_enabled = true;
} }
@ -101,7 +101,9 @@ SteamAPICall_t Steam_Remote_Storage::FileWriteAsync( const char *pchFile, const
RemoteStorageFileWriteAsyncComplete_t data; RemoteStorageFileWriteAsyncComplete_t data;
data.m_eResult = success ? k_EResultOK : k_EResultFail; data.m_eResult = success ? k_EResultOK : k_EResultFail;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.01); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.01);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.01);
return ret;
} }
@ -130,7 +132,9 @@ SteamAPICall_t Steam_Remote_Storage::FileReadAsync( const char *pchFile, uint32
a_read.size = size; a_read.size = size;
async_reads.push_back(a_read); async_reads.push_back(a_read);
callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data), 0.0); callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data), 0.0);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.0);
return data.m_hFileReadAsync; return data.m_hFileReadAsync;
} }
@ -161,7 +165,7 @@ bool Steam_Remote_Storage::FileReadAsyncComplete( SteamAPICall_t hReadCall, void
bool Steam_Remote_Storage::FileForget( const char *pchFile ) bool Steam_Remote_Storage::FileForget( const char *pchFile )
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG("'%s'", pchFile);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!pchFile || !pchFile[0]) return false; if (!pchFile || !pchFile[0]) return false;
@ -170,7 +174,7 @@ bool Steam_Remote_Storage::FileForget( const char *pchFile )
bool Steam_Remote_Storage::FileDelete( const char *pchFile ) bool Steam_Remote_Storage::FileDelete( const char *pchFile )
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG("'%s'", pchFile);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!pchFile || !pchFile[0]) return false; if (!pchFile || !pchFile[0]) return false;
@ -180,7 +184,7 @@ bool Steam_Remote_Storage::FileDelete( const char *pchFile )
STEAM_CALL_RESULT( RemoteStorageFileShareResult_t ) STEAM_CALL_RESULT( RemoteStorageFileShareResult_t )
SteamAPICall_t Steam_Remote_Storage::FileShare( const char *pchFile ) SteamAPICall_t Steam_Remote_Storage::FileShare( const char *pchFile )
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG("'%s'", pchFile);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!pchFile || !pchFile[0]) return k_uAPICallInvalid; if (!pchFile || !pchFile[0]) return k_uAPICallInvalid;
@ -194,12 +198,14 @@ SteamAPICall_t Steam_Remote_Storage::FileShare( const char *pchFile )
data.m_eResult = k_EResultFileNotFound; data.m_eResult = k_EResultFileNotFound;
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
bool Steam_Remote_Storage::SetSyncPlatforms( const char *pchFile, ERemoteStoragePlatform eRemoteStoragePlatform ) bool Steam_Remote_Storage::SetSyncPlatforms( const char *pchFile, ERemoteStoragePlatform eRemoteStoragePlatform )
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG("'%s' %i", pchFile, (int)eRemoteStoragePlatform);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!pchFile || !pchFile[0]) return false; if (!pchFile || !pchFile[0]) return false;
@ -210,13 +216,15 @@ bool Steam_Remote_Storage::SetSyncPlatforms( const char *pchFile, ERemoteStorage
// file operations that cause network IO // file operations that cause network IO
UGCFileWriteStreamHandle_t Steam_Remote_Storage::FileWriteStreamOpen( const char *pchFile ) UGCFileWriteStreamHandle_t Steam_Remote_Storage::FileWriteStreamOpen( const char *pchFile )
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG("'%s'", pchFile);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!pchFile || !pchFile[0]) return k_UGCFileStreamHandleInvalid; if (!pchFile || !pchFile[0]) return k_UGCFileStreamHandleInvalid;
static UGCFileWriteStreamHandle_t handle; static UGCFileWriteStreamHandle_t handle = 0;
++handle; ++handle;
struct Stream_Write stream_write; if (!handle) handle = 1;
struct Stream_Write stream_write{};
stream_write.file_name = std::string(pchFile); stream_write.file_name = std::string(pchFile);
stream_write.write_stream_handle = handle; stream_write.write_stream_handle = handle;
stream_writes.push_back(stream_write); stream_writes.push_back(stream_write);
@ -412,18 +420,20 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownload( UGCHandle_t hContent, uint32 u
RemoteStorageDownloadUGCResult_t data{}; RemoteStorageDownloadUGCResult_t data{};
data.m_hFile = hContent; data.m_hFile = hContent;
data.m_nAppID = settings->get_local_game_id().AppID();
if (shared_files.count(hContent)) { if (shared_files.count(hContent)) {
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
data.m_nAppID = settings->get_local_game_id().AppID();
data.m_ulSteamIDOwner = settings->get_local_steam_id().ConvertToUint64(); data.m_ulSteamIDOwner = settings->get_local_steam_id().ConvertToUint64();
data.m_nSizeInBytes = local_storage->file_size(Local_Storage::remote_storage_folder, shared_files[hContent]); data.m_nSizeInBytes = local_storage->file_size(Local_Storage::remote_storage_folder, shared_files[hContent]);
shared_files[hContent].copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1); shared_files[hContent].copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1);
PRINT_DEBUG(" FileShare data.m_pchFileName = '%s'", data.m_pchFileName);
downloaded_files[hContent].source = Downloaded_File::DownloadSource::AfterFileShare; auto [ele_itr, _] = downloaded_files.insert_or_assign(hContent, Downloaded_File::DownloadSource::AfterFileShare);
downloaded_files[hContent].file = shared_files[hContent]; auto &ele = ele_itr->second;
downloaded_files[hContent].total_size = data.m_nSizeInBytes; ele.file = shared_files[hContent];
ele.total_size = data.m_nSizeInBytes;
} else if (auto query_res = ugc_bridge->get_ugc_query_result(hContent)) { } else if (auto query_res = ugc_bridge->get_ugc_query_result(hContent)) {
auto mod = settings->getMod(query_res.value().mod_id); auto mod = settings->getMod(query_res.value().mod_id);
auto &mod_name = query_res.value().is_primary_file auto &mod_name = query_res.value().is_primary_file
@ -434,24 +444,25 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownload( UGCHandle_t hContent, uint32 u
: mod.previewFileSize; : mod.previewFileSize;
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
data.m_nAppID = settings->get_local_game_id().AppID();
data.m_ulSteamIDOwner = mod.steamIDOwner; data.m_ulSteamIDOwner = mod.steamIDOwner;
data.m_nSizeInBytes = mod_size; data.m_nSizeInBytes = mod_size;
data.m_ulSteamIDOwner = mod.steamIDOwner; data.m_ulSteamIDOwner = mod.steamIDOwner;
mod_name.copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1); mod_name.copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1);
PRINT_DEBUG(" QueryUGCRequest data.m_pchFileName = '%s'", data.m_pchFileName);
downloaded_files[hContent].source = Downloaded_File::DownloadSource::AfterSendQueryUGCRequest; auto [ele_itr, _] = downloaded_files.insert_or_assign(hContent, Downloaded_File::DownloadSource::AfterSendQueryUGCRequest);
downloaded_files[hContent].file = mod_name; auto &ele = ele_itr->second;
downloaded_files[hContent].total_size = mod_size; ele.file = mod_name;
ele.total_size = mod_size;
downloaded_files[hContent].mod_query_info = query_res.value(); ele.mod_query_info = query_res.value();
} else { } else {
data.m_eResult = k_EResultFileNotFound; //TODO: not sure if this is the right result data.m_eResult = k_EResultFileNotFound; //TODO: not sure if this is the right result
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t ) STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
@ -512,7 +523,7 @@ int32 Steam_Remote_Storage::UGCRead( UGCHandle_t hContent, void *pvData, int32 c
Downloaded_File &dwf = f_itr->second; Downloaded_File &dwf = f_itr->second;
// depending on the download source, we have to decide where to grab the content/data // depending on the download source, we have to decide where to grab the content/data
switch (dwf.source) switch (dwf.get_source())
{ {
case Downloaded_File::DownloadSource::AfterFileShare: { case Downloaded_File::DownloadSource::AfterFileShare: {
PRINT_DEBUG(" source = AfterFileShare '%s'", dwf.file.c_str()); PRINT_DEBUG(" source = AfterFileShare '%s'", dwf.file.c_str());
@ -523,14 +534,14 @@ int32 Steam_Remote_Storage::UGCRead( UGCHandle_t hContent, void *pvData, int32 c
case Downloaded_File::DownloadSource::AfterSendQueryUGCRequest: case Downloaded_File::DownloadSource::AfterSendQueryUGCRequest:
case Downloaded_File::DownloadSource::FromUGCDownloadToLocation: { case Downloaded_File::DownloadSource::FromUGCDownloadToLocation: {
PRINT_DEBUG(" source = AfterSendQueryUGCRequest || FromUGCDownloadToLocation [%i]", (int)dwf.source); PRINT_DEBUG(" source = AfterSendQueryUGCRequest || FromUGCDownloadToLocation [%i]", (int)dwf.get_source());
auto mod = settings->getMod(dwf.mod_query_info.mod_id); auto mod = settings->getMod(dwf.mod_query_info.mod_id);
auto &mod_name = dwf.mod_query_info.is_primary_file auto &mod_name = dwf.mod_query_info.is_primary_file
? mod.primaryFileName ? mod.primaryFileName
: mod.previewFileName; : mod.previewFileName;
std::string mod_fullpath{}; std::string mod_fullpath{};
if (dwf.source == Downloaded_File::DownloadSource::AfterSendQueryUGCRequest) { if (dwf.get_source() == Downloaded_File::DownloadSource::AfterSendQueryUGCRequest) {
std::string mod_base_path = dwf.mod_query_info.is_primary_file std::string mod_base_path = dwf.mod_query_info.is_primary_file
? mod.path ? mod.path
: Local_Storage::get_game_settings_path() + "mod_images" + PATH_SEPARATOR + std::to_string(mod.id); : Local_Storage::get_game_settings_path() + "mod_images" + PATH_SEPARATOR + std::to_string(mod.id);
@ -547,7 +558,7 @@ int32 Steam_Remote_Storage::UGCRead( UGCHandle_t hContent, void *pvData, int32 c
break; break;
default: default:
PRINT_DEBUG(" unhandled download source %i", (int)dwf.source); PRINT_DEBUG(" unhandled download source %i", (int)dwf.get_source());
return -1; //TODO: is this the right return value? return -1; //TODO: is this the right return value?
break; break;
} }
@ -790,12 +801,13 @@ SteamAPICall_t Steam_Remote_Storage::GetPublishedFileDetails( PublishedFileId_t
mod.tags.copy(data.m_rgchTags, sizeof(data.m_rgchTags) - 1); mod.tags.copy(data.m_rgchTags, sizeof(data.m_rgchTags) - 1);
mod.title.copy(data.m_rgchTitle, sizeof(data.m_rgchTitle) - 1); mod.title.copy(data.m_rgchTitle, sizeof(data.m_rgchTitle) - 1);
mod.workshopItemURL.copy(data.m_rgchURL, sizeof(data.m_rgchURL) - 1); mod.workshopItemURL.copy(data.m_rgchURL, sizeof(data.m_rgchURL) - 1);
} else { } else {
data.m_eResult = EResult::k_EResultFail; // TODO is this correct? data.m_eResult = EResult::k_EResultFail; // TODO is this correct?
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
// return 0; // return 0;
/* /*
@ -830,6 +842,7 @@ SteamAPICall_t Steam_Remote_Storage::EnumerateUserPublishedFiles( uint32 unStart
PRINT_DEBUG("TODO %u", unStartIndex); PRINT_DEBUG("TODO %u", unStartIndex);
// TODO is this implementation correct? // TODO is this implementation correct?
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageEnumerateUserPublishedFilesResult_t data{}; RemoteStorageEnumerateUserPublishedFilesResult_t data{};
// collect all published mods by this user // collect all published mods by this user
@ -861,7 +874,9 @@ SteamAPICall_t Steam_Remote_Storage::EnumerateUserPublishedFiles( uint32 unStart
data.m_nResultsReturned = iterated; data.m_nResultsReturned = iterated;
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t ) STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t )
@ -882,7 +897,9 @@ SteamAPICall_t Steam_Remote_Storage::SubscribePublishedFile( PublishedFileId_t u
data.m_eResult = EResult::k_EResultFail; // TODO is this correct? data.m_eResult = EResult::k_EResultFail; // TODO is this correct?
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( RemoteStorageEnumerateUserSubscribedFilesResult_t ) STEAM_CALL_RESULT( RemoteStorageEnumerateUserSubscribedFilesResult_t )
@ -914,7 +931,9 @@ SteamAPICall_t Steam_Remote_Storage::EnumerateUserSubscribedFiles( uint32 unStar
data.m_nResultsReturned = iterated; data.m_nResultsReturned = iterated;
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t ) STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t )
@ -935,7 +954,9 @@ SteamAPICall_t Steam_Remote_Storage::UnsubscribePublishedFile( PublishedFileId_t
data.m_eResult = k_EResultFail; data.m_eResult = k_EResultFail;
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
bool Steam_Remote_Storage::UpdatePublishedFileSetChangeDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchChangeDescription ) bool Steam_Remote_Storage::UpdatePublishedFileSetChangeDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchChangeDescription )
@ -967,7 +988,9 @@ SteamAPICall_t Steam_Remote_Storage::GetPublishedItemVoteDetails( PublishedFileI
data.m_eResult = EResult::k_EResultFail; // TODO is this correct? data.m_eResult = EResult::k_EResultFail; // TODO is this correct?
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( RemoteStorageUpdateUserPublishedItemVoteResult_t ) STEAM_CALL_RESULT( RemoteStorageUpdateUserPublishedItemVoteResult_t )
@ -993,7 +1016,9 @@ SteamAPICall_t Steam_Remote_Storage::UpdateUserPublishedItemVote( PublishedFileI
data.m_eResult = EResult::k_EResultFail; // TODO is this correct? data.m_eResult = EResult::k_EResultFail; // TODO is this correct?
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t ) STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
@ -1022,22 +1047,26 @@ SteamAPICall_t Steam_Remote_Storage::GetUserPublishedItemVoteDetails( PublishedF
data.m_eResult = EResult::k_EResultFail; // TODO is this correct? data.m_eResult = EResult::k_EResultFail; // TODO is this correct?
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return 0; return ret;
} }
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t ) STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
SteamAPICall_t Steam_Remote_Storage::EnumerateUserSharedWorkshopFiles( CSteamID steamId, uint32 unStartIndex, SteamParamStringArray_t *pRequiredTags, SteamParamStringArray_t *pExcludedTags ) SteamAPICall_t Steam_Remote_Storage::EnumerateUserSharedWorkshopFiles( CSteamID steamId, uint32 unStartIndex, SteamParamStringArray_t *pRequiredTags, SteamParamStringArray_t *pExcludedTags )
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageEnumerateUserPublishedFilesResult_t data{}; RemoteStorageEnumerateUserPublishedFilesResult_t data{};
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
data.m_nResultsReturned = 0; data.m_nResultsReturned = 0;
data.m_nTotalResultCount = 0; data.m_nTotalResultCount = 0;
//data.m_rgPublishedFileId; //data.m_rgPublishedFileId;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t ) STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
@ -1086,12 +1115,15 @@ SteamAPICall_t Steam_Remote_Storage::EnumeratePublishedWorkshopFiles( EWorkshopE
PRINT_DEBUG_TODO(); PRINT_DEBUG_TODO();
// TODO is this implementation correct? // TODO is this implementation correct?
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageEnumerateWorkshopFilesResult_t data{}; RemoteStorageEnumerateWorkshopFilesResult_t data{};
data.m_eResult = EResult::k_EResultOK; data.m_eResult = EResult::k_EResultOK;
data.m_nResultsReturned = 0; data.m_nResultsReturned = 0;
data.m_nTotalResultCount = 0; data.m_nTotalResultCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
@ -1134,18 +1166,20 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownloadToLocation( UGCHandle_t hContent
copy_file(mod_fullpath, pchLocation); copy_file(mod_fullpath, pchLocation);
// TODO not sure about this though // TODO not sure about this though
downloaded_files[hContent].source = Downloaded_File::DownloadSource::FromUGCDownloadToLocation; auto [ele_itr, _] = downloaded_files.insert_or_assign(hContent, Downloaded_File::DownloadSource::FromUGCDownloadToLocation);
downloaded_files[hContent].file = mod_name; auto &ele = ele_itr->second;
downloaded_files[hContent].total_size = mod_size; ele.file = mod_name;
ele.total_size = mod_size;
downloaded_files[hContent].mod_query_info = query_res.value(); ele.mod_query_info = query_res.value();
downloaded_files[hContent].download_to_location_fullpath = pchLocation; ele.download_to_location_fullpath = pchLocation;
} else { } else {
data.m_eResult = k_EResultFileNotFound; //TODO: not sure if this is the right result data.m_eResult = k_EResultFileNotFound; //TODO: not sure if this is the right result
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
// Cloud dynamic state change notification // Cloud dynamic state change notification

View File

@ -47,6 +47,9 @@ Steam_Timeline::Steam_Timeline(class Settings *settings, class Networking *netwo
// this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Timeline::steam_callback, this); // this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Timeline::steam_callback, this);
this->run_every_runcb->add(&Steam_Timeline::steam_run_every_runcb, this); this->run_every_runcb->add(&Steam_Timeline::steam_run_every_runcb, this);
// timeline starts with a default event as seen here: https://www.youtube.com/watch?v=YwBD0E4-EsI
SetTimelineGameMode(ETimelineGameMode::k_ETimelineGameMode_Invalid);
} }
Steam_Timeline::~Steam_Timeline() Steam_Timeline::~Steam_Timeline()
@ -55,6 +58,20 @@ Steam_Timeline::~Steam_Timeline()
this->run_every_runcb->remove(&Steam_Timeline::steam_run_every_runcb, this); this->run_every_runcb->remove(&Steam_Timeline::steam_run_every_runcb, this);
} }
// Sets a description for the current game state in the timeline. These help the user to find specific
// moments in the timeline when saving clips. Setting a new state description replaces any previous
// description.
//
// Examples could include:
// * Where the user is in the world in a single player game
// * Which round is happening in a multiplayer game
// * The current score for a sports game
//
// Parameters:
// - pchDescription: provide a localized string in the language returned by SteamUtils()->GetSteamUILanguage()
// - flTimeDelta: The time offset in seconds to apply to this event. Negative times indicate an
// event that happened in the past.
void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, float flTimeDelta ) void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, float flTimeDelta )
{ {
PRINT_DEBUG("'%s' %f", pchDescription, flTimeDelta); PRINT_DEBUG("'%s' %f", pchDescription, flTimeDelta);
@ -78,7 +95,6 @@ void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, fl
} }
void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta ) void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta )
{ {
PRINT_DEBUG("%f", flTimeDelta); PRINT_DEBUG("%f", flTimeDelta);
@ -98,10 +114,35 @@ void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta )
} }
// Use this to mark an event on the Timeline. The event can be instantaneous or take some amount of time
// to complete, depending on the value passed in flDurationSeconds
//
// Examples could include:
// * a boss battle
// * a cut scene
// * a large team fight
// * picking up a new weapon or ammunition
// * scoring a goal
//
// Parameters:
//
// - pchIcon: specify the name of the icon uploaded through the Steamworks Partner Site for your title
// or one of the provided icons that start with steam_
// - pchTitle & pchDescription: provide a localized string in the language returned by
// SteamUtils()->GetSteamUILanguage()
// - unPriority: specify how important this range is compared to other markers provided by the game.
// Ranges with larger priority values will be displayed more prominently in the UI. This value
// may be between 0 and k_unMaxTimelinePriority.
// - flStartOffsetSeconds: The time that this range started relative to now. Negative times
// indicate an event that happened in the past.
// - flDurationSeconds: How long the time range should be in seconds. For instantaneous events, this
// should be 0
// - ePossibleClip: By setting this parameter to Featured or Standard, the game indicates to Steam that it
// would be appropriate to offer this range as a clip to the user. For instantaneous events, the
// suggested clip will be for a short time before and after the event itself.
void Steam_Timeline::AddTimelineEvent( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip ) void Steam_Timeline::AddTimelineEvent( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip )
{ {
PRINT_DEBUG("'%s' | '%s' - '%s', %u, [%f, %f) %i", pchIcon, pchTitle, pchDescription, unPriority, flStartOffsetSeconds, flDurationSeconds, (int)ePossibleClip); PRINT_DEBUG("icon='%s' | '%s' - '%s', %u, [%f, %f) %i", pchIcon, pchTitle, pchDescription, unPriority, flStartOffsetSeconds, flDurationSeconds, (int)ePossibleClip);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
auto &new_event = timeline_events.emplace_back(TimelineEvent_t{}); auto &new_event = timeline_events.emplace_back(TimelineEvent_t{});
@ -112,24 +153,28 @@ void Steam_Timeline::AddTimelineEvent( const char *pchIcon, const char *pchTitle
new_event.flStartOffsetSeconds = flStartOffsetSeconds; new_event.flStartOffsetSeconds = flStartOffsetSeconds;
// for instantanious event with flDurationSeconds=0 steam creates 8 sec clip // make events last at least 1 sec
if (static_cast<long>(flDurationSeconds * 1000) <= 100) { // <= 100ms if (static_cast<long>(flDurationSeconds * 1000) < 1000) { // < 1000ms
flDurationSeconds = 8; flDurationSeconds = 1;
}
// for events with priority=ETimelineEventClipPriority::k_ETimelineEventClipPriority_Featured steam creates ~8 sec clip
// seen here: https://www.youtube.com/watch?v=YwBD0E4-EsI
if (flDurationSeconds < PRIORITY_CLIP_MIN_SEC && ePossibleClip == ETimelineEventClipPriority::k_ETimelineEventClipPriority_Featured) {
flDurationSeconds = PRIORITY_CLIP_MIN_SEC;
} }
new_event.flDurationSeconds = flDurationSeconds; new_event.flDurationSeconds = flDurationSeconds;
new_event.ePossibleClip = ePossibleClip; new_event.ePossibleClip = ePossibleClip;
} }
// Changes the color of the timeline bar. See ETimelineGameMode comments for how to use each value
void Steam_Timeline::SetTimelineGameMode( ETimelineGameMode eMode ) void Steam_Timeline::SetTimelineGameMode( ETimelineGameMode eMode )
{ {
PRINT_DEBUG("%i", (int)eMode); PRINT_DEBUG("%i", (int)eMode);
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
if (timeline_states.empty()) return; auto &new_timeline_state = timeline_states.emplace_back(TimelineState_t{});
new_timeline_state.bar_color = eMode;
timeline_states.back().bar_color = eMode;
} }

View File

@ -1123,7 +1123,10 @@ SteamAPICall_t Steam_UGC::SetUserItemVote( PublishedFileId_t nPublishedFileID, b
++mod.votesDown; ++mod.votesDown;
} }
settings->addModDetails(nPublishedFileID, mod); settings->addModDetails(nPublishedFileID, mod);
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
@ -1141,7 +1144,10 @@ SteamAPICall_t Steam_UGC::GetUserItemVote( PublishedFileId_t nPublishedFileID )
data.m_bVotedDown = mod.votesDown; data.m_bVotedDown = mod.votesDown;
data.m_bVotedUp = mod.votesUp; data.m_bVotedUp = mod.votesUp;
data.m_bVoteSkipped = true; data.m_bVoteSkipped = true;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
@ -1166,7 +1172,9 @@ SteamAPICall_t Steam_UGC::AddItemToFavorites( AppId_t nAppId, PublishedFileId_t
data.m_eResult = EResult::k_EResultOK; data.m_eResult = EResult::k_EResultOK;
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
@ -1191,7 +1199,9 @@ SteamAPICall_t Steam_UGC::RemoveItemFromFavorites( AppId_t nAppId, PublishedFile
data.m_eResult = EResult::k_EResultOK; data.m_eResult = EResult::k_EResultOK;
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
@ -1209,7 +1219,9 @@ SteamAPICall_t Steam_UGC::SubscribeItem( PublishedFileId_t nPublishedFileID )
} else { } else {
data.m_eResult = k_EResultFail; data.m_eResult = k_EResultFail;
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
// subscribe to this item, will be installed ASAP // subscribe to this item, will be installed ASAP
@ -1228,7 +1240,9 @@ SteamAPICall_t Steam_UGC::UnsubscribeItem( PublishedFileId_t nPublishedFileID )
ugc_bridge->remove_subbed_mod(nPublishedFileID); ugc_bridge->remove_subbed_mod(nPublishedFileID);
} }
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
// unsubscribe from this item, will be uninstalled after game quits // unsubscribe from this item, will be uninstalled after game quits
@ -1383,7 +1397,10 @@ SteamAPICall_t Steam_UGC::StartPlaytimeTracking( PublishedFileId_t *pvecPublishe
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
StopPlaytimeTrackingResult_t data; StopPlaytimeTrackingResult_t data;
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t ) STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t )
@ -1393,7 +1410,10 @@ SteamAPICall_t Steam_UGC::StopPlaytimeTracking( PublishedFileId_t *pvecPublished
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
StopPlaytimeTrackingResult_t data; StopPlaytimeTrackingResult_t data;
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t ) STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t )
@ -1403,7 +1423,10 @@ SteamAPICall_t Steam_UGC::StopPlaytimeTrackingForAllItems()
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
StopPlaytimeTrackingResult_t data; StopPlaytimeTrackingResult_t data;
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }

View File

@ -126,9 +126,11 @@ void Steam_User::RefreshSteam2Login()
bool Steam_User::GetUserDataFolder( char *pchBuffer, int cubBuffer ) bool Steam_User::GetUserDataFolder( char *pchBuffer, int cubBuffer )
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_ENTRY();
if (!cubBuffer) return false; if (!cubBuffer || cubBuffer <= 0) return false;
std::string user_data = local_storage->get_path(Local_Storage::user_data_storage); std::string user_data = local_storage->get_path(Local_Storage::user_data_storage);
if (static_cast<size_t>(cubBuffer) <= user_data.size()) return false;
strncpy(pchBuffer, user_data.c_str(), cubBuffer - 1); strncpy(pchBuffer, user_data.c_str(), cubBuffer - 1);
pchBuffer[cubBuffer - 1] = 0; pchBuffer[cubBuffer - 1] = 0;
return true; return true;
@ -201,6 +203,7 @@ EVoiceResult Steam_User::GetVoice( bool bWantCompressed, void *pDestBuffer, uint
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_ENTRY();
if (!recording) return k_EVoiceResultNotRecording; if (!recording) return k_EVoiceResultNotRecording;
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - last_get_voice).count(); double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - last_get_voice).count();
if (bWantCompressed) { if (bWantCompressed) {
uint32 towrite = static_cast<uint32>(seconds * 1024.0 * 64.0 / 8.0); uint32 towrite = static_cast<uint32>(seconds * 1024.0 * 64.0 / 8.0);
@ -239,6 +242,7 @@ EVoiceResult Steam_User::DecompressVoice( const void *pCompressed, uint32 cbComp
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_ENTRY();
if (!recording) return k_EVoiceResultNotRecording; if (!recording) return k_EVoiceResultNotRecording;
uint32 uncompressed = static_cast<uint32>((double)cbCompressed * ((double)nDesiredSampleRate / 8192.0)); uint32 uncompressed = static_cast<uint32>((double)cbCompressed * ((double)nDesiredSampleRate / 8192.0));
if(nBytesWritten) *nBytesWritten = uncompressed; if(nBytesWritten) *nBytesWritten = uncompressed;
if (uncompressed > cbDestBufferSize) uncompressed = cbDestBufferSize; if (uncompressed > cbDestBufferSize) uncompressed = cbDestBufferSize;
@ -296,7 +300,7 @@ HAuthTicket Steam_User::GetAuthSessionTicket( void *pTicket, int cbMaxTicket, ui
// the ticket will be returned in callback GetTicketForWebApiResponse_t // the ticket will be returned in callback GetTicketForWebApiResponse_t
HAuthTicket Steam_User::GetAuthTicketForWebApi( const char *pchIdentity ) HAuthTicket Steam_User::GetAuthTicketForWebApi( const char *pchIdentity )
{ {
PRINT_DEBUG("%s", pchIdentity); PRINT_DEBUG("'%s'", pchIdentity);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
return auth_manager->getWebApiTicket(pchIdentity); return auth_manager->getWebApiTicket(pchIdentity);
@ -377,7 +381,7 @@ void Steam_User::AdvertiseGame( CSteamID steamIDGameServer, uint32 unIPServer, u
STEAM_CALL_RESULT( EncryptedAppTicketResponse_t ) STEAM_CALL_RESULT( EncryptedAppTicketResponse_t )
SteamAPICall_t Steam_User::RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToInclude ) SteamAPICall_t Steam_User::RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToInclude )
{ {
PRINT_DEBUG("%i", cbDataToInclude); PRINT_DEBUG("%i %p", cbDataToInclude, pDataToInclude);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
EncryptedAppTicketResponse_t data; EncryptedAppTicketResponse_t data;
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
@ -420,7 +424,9 @@ SteamAPICall_t Steam_User::RequestEncryptedAppTicket( void *pDataToInclude, int
encrypted_app_ticket = pb.SerializeAsString(); encrypted_app_ticket = pb.SerializeAsString();
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
} }
// retrieve a finished ticket // retrieve a finished ticket
@ -439,6 +445,7 @@ bool Steam_User::GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *
if (ticket_size > static_cast<uint32>(cbMaxTicket)) return false; if (ticket_size > static_cast<uint32>(cbMaxTicket)) return false;
encrypted_app_ticket.copy((char *)pTicket, cbMaxTicket); encrypted_app_ticket.copy((char *)pTicket, cbMaxTicket);
PRINT_DEBUG("copied successfully");
return true; return true;
} }
@ -471,7 +478,7 @@ int Steam_User::GetPlayerSteamLevel()
STEAM_CALL_RESULT( StoreAuthURLResponse_t ) STEAM_CALL_RESULT( StoreAuthURLResponse_t )
SteamAPICall_t Steam_User::RequestStoreAuthURL( const char *pchRedirectURL ) SteamAPICall_t Steam_User::RequestStoreAuthURL( const char *pchRedirectURL )
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_TODO();
return 0; return 0;
} }
@ -506,7 +513,7 @@ bool Steam_User::BIsPhoneRequiringVerification()
STEAM_CALL_RESULT( MarketEligibilityResponse_t ) STEAM_CALL_RESULT( MarketEligibilityResponse_t )
SteamAPICall_t Steam_User::GetMarketEligibility() SteamAPICall_t Steam_User::GetMarketEligibility()
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_TODO();
return 0; return 0;
} }
@ -514,7 +521,7 @@ SteamAPICall_t Steam_User::GetMarketEligibility()
STEAM_CALL_RESULT( DurationControl_t ) STEAM_CALL_RESULT( DurationControl_t )
SteamAPICall_t Steam_User::GetDurationControl() SteamAPICall_t Steam_User::GetDurationControl()
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_TODO();
return 0; return 0;
} }

View File

@ -255,11 +255,10 @@ SteamAPICall_t Steam_Utils::CheckFileSignature( const char *szFileName )
{ {
PRINT_DEBUG("'%s'", szFileName); PRINT_DEBUG("'%s'", szFileName);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
CheckFileSignature_t data; CheckFileSignature_t data{};
data.m_eCheckFileSignature = k_ECheckFileSignatureValidSignature; data.m_eCheckFileSignature = k_ECheckFileSignatureValidSignature;
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
// TODO callback too? callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
// callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret; return ret;
} }

View File

@ -290,7 +290,7 @@ std::wstring common_helpers::to_lower(std::wstring_view wstr)
{ {
if (wstr.empty()) return {}; if (wstr.empty()) return {};
std::wstring _wstr(wstr.size(), '\0'); std::wstring _wstr(wstr.size(), L'\0');
std::transform(wstr.begin(), wstr.end(), _wstr.begin(), [](wchar_t c) { return std::tolower(c); }); std::transform(wstr.begin(), wstr.end(), _wstr.begin(), [](wchar_t c) { return std::tolower(c); });
return _wstr; return _wstr;
} }
@ -308,7 +308,7 @@ std::wstring common_helpers::to_upper(std::wstring_view wstr)
{ {
if (wstr.empty()) return {}; if (wstr.empty()) return {};
std::wstring _wstr(wstr.size(), '\0'); std::wstring _wstr(wstr.size(), L'\0');
std::transform(wstr.begin(), wstr.end(), _wstr.begin(), [](wchar_t c) { return std::toupper(c); }); std::transform(wstr.begin(), wstr.end(), _wstr.begin(), [](wchar_t c) { return std::toupper(c); });
return _wstr; return _wstr;
} }

View File

@ -4,14 +4,17 @@
[app::general] [app::general]
# by default the emu will report a `non-beta` branch when the game calls `Steam_Apps::GetCurrentBetaName()` # by default the emu will report a `non-beta` branch when the game calls `Steam_Apps::GetCurrentBetaName()`
# make the game/app think we're playing on a beta branch # 1=make the game/app think we're playing on a beta branch
# default=0
is_beta_branch=0 is_beta_branch=0
# the name of the current branch, this must also exist in branches.json # the name of the current branch, this must also exist in branches.json
# otherwise will be ignored by the emu and the default 'public' branch will be used # otherwise will be ignored by the emu and the default 'public' branch will be used
# default=public
branch_name=public branch_name=public
[app::dlcs] [app::dlcs]
# should the emu report all DLCs as unlocked # 1=report all DLCs as unlocked
# 0=report only the DLCs mentioned
# some games check for "hidden" DLCs, hence this should be set to 1 in that case # some games check for "hidden" DLCs, hence this should be set to 1 in that case
# but other games detect emus by querying for a fake/bad DLC, hence this should be set to 0 in that case # but other games detect emus by querying for a fake/bad DLC, hence this should be set to 0 in that case
# default=1 # default=1

View File

@ -3,80 +3,105 @@
# ############################################################################## # # ############################################################################## #
[main::general] [main::general]
# generate new app auth ticket # 1=generate newer version of auth ticket, used by some modern apps
# default=0
new_app_ticket=1 new_app_ticket=1
# generate/embed GC token inside new App Ticket # 1=generate/embed Game Coordinator token inside the new auth ticket
# default=0
gc_token=1 gc_token=1
# pretend the app is running on a steam deck # 1=pretend the app is running on a steam deck
# default=0
steam_deck=0 steam_deck=0
# enable avatar functionality # 1=enable avatar functionality
# default=0
enable_account_avatar=0 enable_account_avatar=0
# prevent Steam_User_Stats::FindLeaderboard() from always succeeding and creating the unknown leaderboard # 1=prevent `Steam_User_Stats::FindLeaderboard()` from always succeeding and creating the unknown leaderboard
# not recommended to disable this # not recommended to enable this
# default=0
disable_leaderboards_create_unknown=0 disable_leaderboards_create_unknown=0
# the emu will only save/update stats defined by the user, unknown stats requested or updated by the game will be rejected # the emu will only save/update stats defined by the user, unknown stats requested or updated by the game will be rejected
# set this to 1 to allow unknown stats # 1=allow unknown stats to be saved/updated
# default=0 # default=0
allow_unknown_stats=0 allow_unknown_stats=0
# whenever a game updates a stat which is tied to an achievement progress, the emu will save that progress immediately # whenever a game updates a stat which is tied to an achievement progress, the emu will save that progress immediately
# but some games will update the stat very frequently (with lower & higher values) resulting in a spam of disk writes or overlay notifications # but some games will update the stat very frequently (with lower & higher values) resulting in a spam of disk writes or overlay notifications
# set this to 0 to save any stat achievement progress value (higher or lower) # 0=save any stat achievement progress value (higher or lower)
# this has no impact on the stat itself, only the achievement progress of a stat which is tied to an achievement # 1=save stat achievement progress value only if it is higher than the current one
# this has no impact on the stat itself, only the achievement progress
# also has no impact on the functions which directly change stats, achievements, or achievements progress # also has no impact on the functions which directly change stats, achievements, or achievements progress
# default=1 # default=1
save_only_higher_stat_achievement_progress=1 save_only_higher_stat_achievement_progress=1
# synchronize user stats/achievements with game servers as soon as possible instead of caching them until the next call to `Steam_RunCallbacks()` # 1=synchronize user stats/achievements with game servers as soon as possible instead of caching them until the next call to `Steam_RunCallbacks()`
# not recommended # not recommended to enable this
# default=0
immediate_gameserver_stats=0 immediate_gameserver_stats=0
# use the proper type of the server list (internet, friends, etc...) when requested by the game # 1=use the proper type of the server list (internet, friends, etc...) when requested by the game
# otherwise, the emu will always return the type "LAN server" # 0=always return the type of the server list as "LAN server"
# not recommended # not recommended to enable this
# default=0
matchmaking_server_list_actual_type=0 matchmaking_server_list_actual_type=0
# grab the server details for match making using an actual server query # 1=grab the server details for match making using an actual server query
# not recommended # 0=use the info from the local network messages shared between client/server
# not recommended to enable this, currently breaks some games
# default=0
matchmaking_server_details_via_source_query=0 matchmaking_server_details_via_source_query=0
# very basic crash logger/printer # very basic crash logger/printer
# this is intended to debug some annoying scenarios, and best used with the debug build of the emu # this is intended to debug some annoying scenarios, and best used with the debug build of the emu
# default=
crash_printer_location=./path/relative/to/dll/crashes.txt crash_printer_location=./path/relative/to/dll/crashes.txt
[main::connectivity] [main::connectivity]
# prevent hooking OS networking APIs and allow any external requests # 1=prevent hooking OS networking APIs and allow any external requests
# only used by the experimental builds on Windows # only used by the experimental builds on **Windows**
# default=0
disable_lan_only=0 disable_lan_only=0
# disable all steam networking interface functionality # 1=disable all steam networking interface functionality
# this won't prevent games/apps from making external requests # this won't prevent games/apps from making external requests
# networking related functionality like lobbies or those that launch a server in the background will not work # networking related functionality like lobbies or those that launch a server in the background will not work
# default=0
disable_networking=0 disable_networking=0
# change the UDP/TCP port the emulator listens on, you should probably not change this because everyone needs to use the same port or you won't find yourselves on the network # change the UDP/TCP port the emulator listens on, you should probably not change this because everyone needs to use the same port or you won't find yourselves on the network
# default=47584
listen_port=47584 listen_port=47584
# pretend steam is running in offline mode # 1=pretend steam is running in offline mode, mainly affects the function `ISteamUser::BLoggedOn()`
# Some games that connect to online servers might only work if the steam emu behaves like steam is in offline mode # Some games that connect to online servers might only work if the steam emu behaves like steam is in offline mode
# default=0
offline=0 offline=0
# prevent sharing stats and achievements with any game server, # 1=prevent sharing stats and achievements with any game server, this also disables the interface ISteamGameServerStats
# this also disables the interface ISteamGameServerStats # default=0
disable_sharing_stats_with_gameserver=0 disable_sharing_stats_with_gameserver=0
# do not send server details to the server browser, only works for game servers # 1=do not send server details to the server browser, only works for game servers
# default=0
disable_source_query=0 disable_source_query=0
# enable sharing leaderboards scores with people playing the same game on the same network # 1=enable sharing leaderboards scores with people playing the same game on the same network
# not ideal and synchronization isn't perfect
# default=0
share_leaderboards_over_network=0 share_leaderboards_over_network=0
# prevent lobby creation in steam matchmaking interface # 1=prevent lobby creation in the steam matchmaking interface
# default=0
disable_lobby_creation=0 disable_lobby_creation=0
# attempt to download external HTTP(S) requests made via Steam_HTTP::SendHTTPRequest() inside "steam_settings/http/" # 1=attempt to download external HTTP(S) requests made via Steam_HTTP::SendHTTPRequest() inside "steam_settings/http/"
# make sure to: # make sure to:
# * set disable_lan_only=1 # * set disable_lan_only=1
# * set disable_networking=0 # * set disable_networking=0
# this will **not** work if the app is using native/OS web APIs # this will **not** work if the app is using native/OS web APIs
# default=0
download_steamhttp_requests=0 download_steamhttp_requests=0
# mostly workarounds for specific problems # mostly workarounds for specific problems
[main::misc] [main::misc]
# force SetAchievement() to always return true # 1=force `ISteamUserStats::SetAchievement()` to always return true
# this is a workaround for some games that otherwise won't work
# default=0
achievements_bypass=0 achievements_bypass=0
# force the function Steam_HTTP::SendHTTPRequest() to always succeed # force the function `Steam_HTTP::SendHTTPRequest()` to always succeed
# default=0
force_steamhttp_success=0 force_steamhttp_success=0
# env var SteamOverlayGameId breaks Steam Input when the game is added to Steam as a non-steam game # env var `SteamOverlayGameId` breaks Steam Input when the game is added to Steam as a non-steam game
# 1=don't write this env var, allowing Steam Input to work
# default=0
disable_steamoverlaygameid_env_var=0 disable_steamoverlaygameid_env_var=0
# add many Steam apps to the list of owned DLCs and the emu's list of installed app IDs # 1=add many Steam apps to the list of owned DLCs and the emu's list of installed app IDs
# useful for many Source-based games # useful for many Source-based games
# https://developer.valvesoftware.com/wiki/Steam_Application_IDs # https://developer.valvesoftware.com/wiki/Steam_Application_IDs
# https://developer.valvesoftware.com/wiki/Dedicated_Servers_List # https://developer.valvesoftware.com/wiki/Dedicated_Servers_List

View File

@ -11,7 +11,8 @@
# ############################################################################## # # ############################################################################## #
[overlay::general] [overlay::general]
# enable the experimental overlay, might cause crashes # 1=enable the experimental overlay, might cause crashes
# default=0
enable_experimental_overlay=0 enable_experimental_overlay=0
# amount of time to wait before attempting to detect and hook the renderer (DirectX, OpenGL, etc...) # amount of time to wait before attempting to detect and hook the renderer (DirectX, OpenGL, etc...)
# default=0 # default=0
@ -19,26 +20,34 @@ hook_delay_sec=0
# timeout for the renderer detector # timeout for the renderer detector
# default=15 # default=15
renderer_detector_timeout_sec=15 renderer_detector_timeout_sec=15
# disable the achievements notifications # 1=disable the achievements notifications
# default=0
disable_achievement_notification=0 disable_achievement_notification=0
# disable friends invitations and messages notifications # 1=disable friends invitations and messages notifications
# default=0
disable_friend_notification=0 disable_friend_notification=0
# disable showing notifications for achievements progress # 1=disable showing notifications for achievements progress
# default=0
disable_achievement_progress=0 disable_achievement_progress=0
# disable any warning in the overlay # 1=disable any warning in the overlay
# default=0
disable_warning_any=0 disable_warning_any=0
# disable the bad app ID warning in the overlay # 1=disable the bad app ID warning in the overlay
# default=0
disable_warning_bad_appid=0 disable_warning_bad_appid=0
# disable the local_save warning in the overlay # 1=disable the local_save warning in the overlay
# default=0
disable_warning_local_save=0 disable_warning_local_save=0
[overlay::appearance] [overlay::appearance]
# load custom TrueType font from a path, it could be absolute, or relative # load custom TrueType font from a path, it could be absolute, or relative
# relative paths will be looked up inside the local folder "steam_settings/fonts" first, # relative paths will be looked up inside the local folder "steam_settings/fonts" first,
# if that wasn't found, it will be looked up inside the global folder "GSE Settings/settings/fonts" # if that wasn't found, it will be looked up inside the global folder "GSE Settings/settings/fonts"
# default=
Font_Override=Roboto-Medium.ttf Font_Override=Roboto-Medium.ttf
# global font size # global font size
# for built-in font, multiple of 16 is recommended. e.g. 16 32... # for built-in font, multiple of 16 is recommended. e.g. 16 32...
# default=16.0
Font_Size=20.0 Font_Size=20.0
# achievement icon size # achievement icon size
@ -75,6 +84,7 @@ Notification_Duration_Chat=4.0
# format for the achievement unlock date/time, limited to 79 characters # format for the achievement unlock date/time, limited to 79 characters
# if the output formatted string exceeded this limit, the builtin format will be used # if the output formatted string exceeded this limit, the builtin format will be used
# look for the format here: https://en.cppreference.com/w/cpp/chrono/c/strftime # look for the format here: https://en.cppreference.com/w/cpp/chrono/c/strftime
# default=%Y/%m/%d - %H:%M:%S
Achievement_Unlock_Datetime_Format=%Y/%m/%d - %H:%M:%S Achievement_Unlock_Datetime_Format=%Y/%m/%d - %H:%M:%S
# main background when you press shift+tab # main background when you press shift+tab

View File

@ -4,12 +4,15 @@
[user::general] [user::general]
# user account name # user account name
# default=gse orca
account_name=gse orca account_name=gse orca
# Steam64 format # your account ID in Steam64 format
# if the specified ID is invalid, the emu will ignore it and generate a proper one
# default=randomly generated by the emu only once and saved in the global settings
account_steamid=76561197960287930 account_steamid=76561197960287930
# the language reported to the app/game # the language reported to the app/game
# this must exist in 'supported_languages.txt', otherwise it will be ignored by the emu
# look for the column 'API language code' here: https://partner.steamgames.com/doc/store/localization/languages # look for the column 'API language code' here: https://partner.steamgames.com/doc/store/localization/languages
# this must also exist in 'supported_languages.txt', otherwise it will be ignored by the emu
# default=english # default=english
language=english language=english
# report a country IP if the game queries it # report a country IP if the game queries it
@ -22,6 +25,7 @@ ip_country=US
# path could be absolute, or relative to the location of the .dll/.so # path could be absolute, or relative to the location of the .dll/.so
# leading and trailing whitespaces are trimmed # leading and trailing whitespaces are trimmed
# when this option is used, the global settings folder is completely ignored, allowing a full portable behavior # when this option is used, the global settings folder is completely ignored, allowing a full portable behavior
# default=
local_save_path=./path/relative/to/dll local_save_path=./path/relative/to/dll
# name of the base folder used to store save data, leading and trailing whitespaces are trimmed # name of the base folder used to store save data, leading and trailing whitespaces are trimmed
# only useful if 'local_save_path' isn't used # only useful if 'local_save_path' isn't used

View File

@ -34,16 +34,16 @@ static dbg_log logger{pe_helpers::get_current_exe_path() + pe_helpers::get_curre
constexpr static const wchar_t STEAM_UNIVERSE[] = L"Public"; constexpr static const wchar_t STEAM_UNIVERSE[] = L"Public";
constexpr static const wchar_t STEAM_URL_PROTOCOL[] = L"URL:steam protocol"; constexpr static const wchar_t STEAM_URL_PROTOCOL[] = L"URL:steam protocol";
const bool loader_is_32 = pe_helpers::is_module_32(GetModuleHandleW(NULL)); const static bool loader_is_32 = pe_helpers::is_module_32(GetModuleHandleW(NULL));
const std::wstring hklm_path(loader_is_32 const static std::wstring hklm_path(loader_is_32
? L"SOFTWARE\\Valve\\Steam" ? L"SOFTWARE\\Valve\\Steam"
: L"SOFTWARE\\WOW6432Node\\Valve\\Steam" : L"SOFTWARE\\WOW6432Node\\Valve\\Steam"
); );
// Declare some variables to be used for Steam registry. // Declare some variables to be used for Steam registry.
const DWORD UserId = 0x03100004771F810D & 0xffffffff; const static DWORD UserId = 0x03100004771F810D & 0xffffffff;
const DWORD ProcessID = GetCurrentProcessId(); const static DWORD ProcessID = GetCurrentProcessId();
static std::string IniFile{}; static std::string IniFile{};
static std::string ClientPath{}; static std::string ClientPath{};
@ -154,20 +154,26 @@ static std::vector<std::string> collect_dlls_to_inject(const bool is_exe_32, std
} }
} }
bool orig_steam_hkcu = false; static bool orig_steam_hkcu = false;
WCHAR OrgSteamCDir_hkcu[8192] = { 0 }; static WCHAR OrgSteamCDir_hkcu[8192] = { 0 };
DWORD Size1_hkcu = _countof(OrgSteamCDir_hkcu); static DWORD Size1_hkcu = sizeof(OrgSteamCDir_hkcu);
WCHAR OrgSteamCDir64_hkcu[8192] = { 0 }; static WCHAR OrgSteamCDir64_hkcu[8192] = { 0 };
DWORD Size2_hkcu = _countof(OrgSteamCDir64_hkcu); static DWORD Size2_hkcu = sizeof(OrgSteamCDir64_hkcu);
bool patch_registry_hkcu() static DWORD OrgSteamActiveUser_hkcu = 0;
static WCHAR OrgSteamUniverse_hkcu[8192] = { 0 };
static DWORD Size4_hkcu = sizeof(OrgSteamUniverse_hkcu);
static bool patch_registry_hkcu()
{ {
HKEY Registrykey = { 0 }; HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
orig_steam_hkcu = true; orig_steam_hkcu = true;
// Get original values to restore later. // Get original values to restore later.
DWORD keyType = REG_SZ; DWORD keyType = REG_SZ;
RegQueryValueExW(Registrykey, L"SteamClientDll", 0, &keyType, (LPBYTE)&OrgSteamCDir_hkcu, &Size1_hkcu); RegQueryValueExW(Registrykey, L"SteamClientDll", 0, &keyType, (LPBYTE)OrgSteamCDir_hkcu, &Size1_hkcu);
RegQueryValueExW(Registrykey, L"SteamClientDll64", 0, &keyType, (LPBYTE)&OrgSteamCDir64_hkcu, &Size2_hkcu); RegQueryValueExW(Registrykey, L"SteamClientDll64", 0, &keyType, (LPBYTE)OrgSteamCDir64_hkcu, &Size2_hkcu);
DWORD SizeActiveUser_hkcu = sizeof(DWORD);
RegQueryValueExW(Registrykey, L"ActiveUser", 0, &keyType, (LPBYTE)&OrgSteamActiveUser_hkcu, &SizeActiveUser_hkcu);
RegQueryValueExW(Registrykey, L"Universe", 0, &keyType, (LPBYTE)OrgSteamUniverse_hkcu, &Size4_hkcu);
logger.write("Found previous registry entry (HKCU) for Steam"); logger.write("Found previous registry entry (HKCU) for Steam");
} else if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, 0, REG_OPTION_NON_VOLATILE, } else if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, 0, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) {
@ -177,18 +183,18 @@ bool patch_registry_hkcu()
return false; return false;
} }
RegSetValueExW(Registrykey, L"ActiveUser", NULL, REG_DWORD, (const BYTE *)&UserId, sizeof(DWORD));
RegSetValueExW(Registrykey, L"pid", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD));
auto client_path = common_helpers::to_wstr(ClientPath); auto client_path = common_helpers::to_wstr(ClientPath);
auto client64_path = common_helpers::to_wstr(Client64Path); auto client64_path = common_helpers::to_wstr(Client64Path);
RegSetValueExW(Registrykey, L"pid", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD));
RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (const BYTE*)(client_path).c_str(), static_cast<DWORD>((client_path.size() + 1) * sizeof(client_path[0]))); RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (const BYTE*)(client_path).c_str(), static_cast<DWORD>((client_path.size() + 1) * sizeof(client_path[0])));
RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE*)client64_path.c_str(), static_cast<DWORD>((client64_path.size() + 1) * sizeof(client64_path[0]))); RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE*)client64_path.c_str(), static_cast<DWORD>((client64_path.size() + 1) * sizeof(client64_path[0])));
RegSetValueExW(Registrykey, L"ActiveUser", NULL, REG_DWORD, (const BYTE *)&UserId, sizeof(DWORD));
RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE)); RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE));
RegCloseKey(Registrykey); RegCloseKey(Registrykey);
return true; return true;
} }
void cleanup_registry_hkcu() static void cleanup_registry_hkcu()
{ {
if (!orig_steam_hkcu) return; if (!orig_steam_hkcu) return;
@ -196,8 +202,10 @@ void cleanup_registry_hkcu()
HKEY Registrykey = { 0 }; HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
// Restore the values. // Restore the values.
RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)OrgSteamCDir_hkcu, Size1_hkcu); RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (const BYTE *)OrgSteamCDir_hkcu, Size1_hkcu);
RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)OrgSteamCDir64_hkcu, Size2_hkcu); RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE *)OrgSteamCDir64_hkcu, Size2_hkcu);
RegSetValueExW(Registrykey, L"ActiveUser", NULL, REG_DWORD, (const BYTE *)&OrgSteamActiveUser_hkcu, sizeof(DWORD));
RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)OrgSteamUniverse_hkcu, Size4_hkcu);
// Close the HKEY Handle. // Close the HKEY Handle.
RegCloseKey(Registrykey); RegCloseKey(Registrykey);
@ -207,17 +215,80 @@ void cleanup_registry_hkcu()
} }
bool orig_steam_hklm = false; static bool orig_steam_hkcu_2 = false;
WCHAR OrgInstallPath_hklm[8192] = { 0 }; static WCHAR OrgSteamModDir_hkcu_2[8192] = { 0 };
DWORD Size1_hklm = _countof(OrgInstallPath_hklm); static DWORD Size1_hkcu_2 = sizeof(OrgSteamModDir_hkcu_2);
bool patch_registry_hklm() static WCHAR OrgSteamPath_hkcu_2[8192] = { 0 };
static DWORD Size2_hkcu_2 = sizeof(OrgSteamPath_hkcu_2);
static WCHAR OrgSteamExe_2[8192] = { 0 };
static DWORD Size3_hkcu_2 = sizeof(OrgSteamExe_2);
static bool patch_registry_hkcu_2()
{
HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
orig_steam_hkcu_2 = true;
// Get original values to restore later.
DWORD keyType = REG_SZ;
RegQueryValueExW(Registrykey, L"SourceModInstallPath", 0, &keyType, (LPBYTE)OrgSteamModDir_hkcu_2, &Size1_hkcu_2);
RegQueryValueExW(Registrykey, L"SteamPath", 0, &keyType, (LPBYTE)OrgSteamPath_hkcu_2, &Size2_hkcu_2);
RegQueryValueExW(Registrykey, L"SteamExe", 0, &keyType, (LPBYTE)OrgSteamExe_2, &Size3_hkcu_2);
logger.write("Found previous registry entry (HKCU #2) for Steam");
} else if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, 0, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) {
logger.write("Created new registry entry (HKCU #2) for Steam");
} else {
logger.write("Unable to patch Registry (HKCU #2), error = " + std::to_string(GetLastError()));
return false;
}
auto my_path = common_helpers::to_wstr(pe_helpers::get_current_exe_path());
my_path.pop_back(); // remove last '\\'
const auto my_exe = common_helpers::to_wstr(pe_helpers::get_current_exe_path() + pe_helpers::get_current_exe_name());
if (AppId.size()) { // empty in persistent mode = 2
const auto appid_dword = std::stoul(AppId);
RegSetValueExW(Registrykey, L"RunningAppID", NULL, REG_DWORD, (const BYTE*)&appid_dword, sizeof(DWORD));
}
RegSetValueExW(Registrykey, L"SourceModInstallPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast<DWORD>((my_path.size() + 1) * sizeof(my_path[0])));
RegSetValueExW(Registrykey, L"SteamPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast<DWORD>((my_path.size() + 1) * sizeof(my_path[0])));
RegSetValueExW(Registrykey, L"SteamExe", NULL, REG_SZ, (const BYTE*)my_exe.c_str(), static_cast<DWORD>((my_exe.size() + 1) * sizeof(my_exe[0])));
RegCloseKey(Registrykey);
return true;
}
static void cleanup_registry_hkcu_2()
{
if (!orig_steam_hkcu_2) return;
logger.write("restoring registry entries (HKCU #2)");
HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
// Restore the values.
RegSetValueExW(Registrykey, L"SourceModInstallPath", NULL, REG_SZ, (LPBYTE)OrgSteamModDir_hkcu_2, Size1_hkcu_2);
RegSetValueExW(Registrykey, L"SteamPath", NULL, REG_SZ, (LPBYTE)OrgSteamPath_hkcu_2, Size2_hkcu_2);
RegSetValueExW(Registrykey, L"SteamExe", NULL, REG_SZ, (LPBYTE)OrgSteamExe_2, Size3_hkcu_2);
// Close the HKEY Handle.
RegCloseKey(Registrykey);
} else {
logger.write("Unable to restore the original registry entry (HKCU #2), error = " + std::to_string(GetLastError()));
}
}
static bool orig_steam_hklm = false;
static WCHAR OrgInstallPath_hklm[8192] = { 0 };
static DWORD Size1_hklm = sizeof(OrgInstallPath_hklm);
static WCHAR OrgUniverse_hklm[8192] = { 0 };
static DWORD Size2_hklm = sizeof(OrgUniverse_hklm);
static bool patch_registry_hklm()
{ {
HKEY Registrykey = { 0 }; HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
orig_steam_hklm = true; orig_steam_hklm = true;
// Get original values to restore later. // Get original values to restore later.
DWORD keyType = REG_SZ; DWORD keyType = REG_SZ;
RegQueryValueExW(Registrykey, L"InstallPath", 0, &keyType, (LPBYTE)&OrgInstallPath_hklm, &Size1_hklm); RegQueryValueExW(Registrykey, L"InstallPath", 0, &keyType, (LPBYTE)OrgInstallPath_hklm, &Size1_hklm);
RegQueryValueExW(Registrykey, L"Universe", 0, &keyType, (LPBYTE)OrgUniverse_hklm, &Size2_hklm);
logger.write("Found previous registry entry (HKLM) for Steam"); logger.write("Found previous registry entry (HKLM) for Steam");
} else if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, } else if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, 0, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) {
@ -227,15 +298,16 @@ bool patch_registry_hklm()
return false; return false;
} }
const auto my_path = common_helpers::to_wstr(pe_helpers::get_current_exe_path()); auto my_path = common_helpers::to_wstr(pe_helpers::get_current_exe_path());
RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast<DWORD>((my_path.size() + 1) * sizeof(my_path[0]))); my_path.pop_back(); // remove last '\\'
RegSetValueExW(Registrykey, L"SteamPID", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD)); RegSetValueExW(Registrykey, L"SteamPID", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD));
RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast<DWORD>((my_path.size() + 1) * sizeof(my_path[0])));
RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE)); RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE));
RegCloseKey(Registrykey); RegCloseKey(Registrykey);
return true; return true;
} }
void cleanup_registry_hklm() static void cleanup_registry_hklm()
{ {
if (!orig_steam_hklm) return; if (!orig_steam_hklm) return;
@ -243,6 +315,7 @@ void cleanup_registry_hklm()
HKEY Registrykey = { 0 }; HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE *)OrgInstallPath_hklm, Size1_hklm); RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE *)OrgInstallPath_hklm, Size1_hklm);
RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)OrgUniverse_hklm, Size2_hklm);
RegCloseKey(Registrykey); RegCloseKey(Registrykey);
} else { } else {
logger.write("Unable to restore the original registry entry (HKLM), error = " + std::to_string(GetLastError())); logger.write("Unable to restore the original registry entry (HKLM), error = " + std::to_string(GetLastError()));
@ -250,15 +323,20 @@ void cleanup_registry_hklm()
} }
bool orig_steam_hkcs_1 = false; static bool orig_steam_hkcs_1 = false;
bool orig_steam_hkcs_2 = false; static bool orig_steam_hkcs_2 = false;
WCHAR OrgCommand_hkcs[8192] = { 0 }; static WCHAR OrgCommand_hkcs[8192] = { 0 };
DWORD Size1_hkcs = _countof(OrgCommand_hkcs); static DWORD Size1_hkcs = sizeof(OrgCommand_hkcs);
bool patch_registry_hkcs() static WCHAR OrgUrlProtocol_hkcs[8192] = { 0 };
static DWORD Size2_hkcs = sizeof(OrgUrlProtocol_hkcs);
static bool patch_registry_hkcs()
{ {
HKEY Registrykey = { 0 }; HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
orig_steam_hkcs_1 = true; orig_steam_hkcs_1 = true;
// Get original values to restore later.
DWORD keyType = REG_SZ;
RegQueryValueExW(Registrykey, L"URL Protocol", 0, &keyType, (LPBYTE)&OrgUrlProtocol_hkcs, &Size2_hkcs);
logger.write("Found previous registry entry (HKCS) #1 for Steam"); logger.write("Found previous registry entry (HKCS) #1 for Steam");
} else if (RegCreateKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, 0, REG_OPTION_NON_VOLATILE, } else if (RegCreateKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, 0, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) {
@ -288,16 +366,16 @@ bool patch_registry_hkcs()
RegSetValueExW(Registrykey, L"URL Protocol", NULL, REG_SZ, (const BYTE *)L"", (DWORD)sizeof(L"")); RegSetValueExW(Registrykey, L"URL Protocol", NULL, REG_SZ, (const BYTE *)L"", (DWORD)sizeof(L""));
RegCloseKey(Registrykey); RegCloseKey(Registrykey);
const auto cmd = common_helpers::to_wstr(pe_helpers::get_current_exe_path() + "steam.exe -- \"%1\""); const auto cmd = common_helpers::to_wstr("\"" + pe_helpers::get_current_exe_path() + pe_helpers::get_current_exe_name() + "\" -- \"%1\"");
RegSetValueExW(Registrykey_2, L"", NULL, REG_SZ, (const BYTE*)cmd.c_str(), static_cast<DWORD>((cmd.size() + 1) * sizeof(cmd[0]))); RegSetValueExW(Registrykey_2, L"", NULL, REG_SZ, (const BYTE*)cmd.c_str(), static_cast<DWORD>((cmd.size() + 1) * sizeof(cmd[0])));
RegCloseKey(Registrykey_2); RegCloseKey(Registrykey_2);
return true; return true;
} }
void cleanup_registry_hkcs() static void cleanup_registry_hkcs()
{ {
if (orig_steam_hkcs_2) { if (orig_steam_hkcs_2) {
logger.write("restoring registry entries (HKCS) #1"); logger.write("restoring registry entries (HKCS) #2");
HKEY Registrykey = { 0 }; HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam\\Shell\\Open\\Command", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam\\Shell\\Open\\Command", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
RegSetValueExW(Registrykey, L"", NULL, REG_SZ, (const BYTE *)OrgCommand_hkcs, Size1_hkcs); RegSetValueExW(Registrykey, L"", NULL, REG_SZ, (const BYTE *)OrgCommand_hkcs, Size1_hkcs);
@ -307,7 +385,16 @@ void cleanup_registry_hkcs()
} }
} }
if (!orig_steam_hkcs_1) { if (orig_steam_hkcs_1) {
logger.write("restoring registry entries (HKCS) #1");
HKEY Registrykey = { 0 };
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) {
RegSetValueExW(Registrykey, L"URL Protocol", NULL, REG_SZ, (const BYTE *)OrgUrlProtocol_hkcs, Size2_hkcs);
RegCloseKey(Registrykey);
} else {
logger.write("Unable to restore the original registry entry (HKCS) #1, error = " + std::to_string(GetLastError()));
}
} else {
logger.write("removing registry entries (HKCS) #2 (added by loader)"); logger.write("removing registry entries (HKCS) #2 (added by loader)");
HKEY Registrykey = { 0 }; HKEY Registrykey = { 0 };
RegDeleteKeyW(HKEY_CLASSES_ROOT, L"steam"); RegDeleteKeyW(HKEY_CLASSES_ROOT, L"steam");
@ -316,7 +403,7 @@ void cleanup_registry_hkcs()
void set_steam_env_vars(const std::string &AppId) static void set_steam_env_vars(const std::string &AppId)
{ {
SetEnvironmentVariableA("SteamAppId", AppId.c_str()); SetEnvironmentVariableA("SteamAppId", AppId.c_str());
SetEnvironmentVariableA("SteamGameId", AppId.c_str()); SetEnvironmentVariableA("SteamGameId", AppId.c_str());
@ -552,12 +639,13 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
return 1; return 1;
} }
} else { // steam://run/ } else { // steam://run/
constexpr const static wchar_t STEAM_LAUNCH_CMD_1[] = L"steam://run/"; constexpr const static wchar_t STEAM_LAUNCH_CMD_1[] = L"-- \"steam://run/";
constexpr const static wchar_t STEAM_LAUNCH_CMD_2[] = L"-- \"steam://rungameid/"; constexpr const static wchar_t STEAM_LAUNCH_CMD_2[] = L"-- \"steam://rungameid/";
AppId.clear(); // we don't care about the app id in the ini AppId.clear(); // we don't care about the app id in the ini
auto my_cmd = lpCmdLine && lpCmdLine[0] auto my_cmd = lpCmdLine && lpCmdLine[0]
? std::wstring(lpCmdLine) ? std::wstring(lpCmdLine)
: std::wstring(); : std::wstring();
//MessageBoxW(NULL, (my_cmd + L" ||| " + std::to_wstring(my_cmd.size())).c_str(), L"DEBUG ME", MB_OK);
logger.write(L"persistent mode 2 detecting steam launch cmd from: '" + my_cmd + L"'"); logger.write(L"persistent mode 2 detecting steam launch cmd from: '" + my_cmd + L"'");
if (my_cmd.find(STEAM_LAUNCH_CMD_1) == 0) { if (my_cmd.find(STEAM_LAUNCH_CMD_1) == 0) {
AppId = common_helpers::to_str( my_cmd.substr(sizeof(STEAM_LAUNCH_CMD_1) / sizeof(STEAM_LAUNCH_CMD_1[0]), my_cmd.find_first_of(L" \t")) ); AppId = common_helpers::to_str( my_cmd.substr(sizeof(STEAM_LAUNCH_CMD_1) / sizeof(STEAM_LAUNCH_CMD_1[0]), my_cmd.find_first_of(L" \t")) );
@ -578,16 +666,37 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
} }
if (!patch_registry_hkcu()) { if (!patch_registry_hkcu()) {
cleanup_registry_hkcu();
cleanup_registry_hkcu_2();
cleanup_registry_hklm();
cleanup_registry_hkcs();
logger.write("Unable to patch Registry (HKCU).");
MessageBoxA(NULL, "Unable to patch Registry (HKCU).", "ColdClientLoader", MB_ICONERROR); MessageBoxA(NULL, "Unable to patch Registry (HKCU).", "ColdClientLoader", MB_ICONERROR);
return 1; return 1;
} }
if (!patch_registry_hkcu_2()) {
cleanup_registry_hkcu();
cleanup_registry_hkcu_2();
cleanup_registry_hklm();
cleanup_registry_hkcs();
logger.write("Unable to patch Registry (HKCU #2).");
MessageBoxA(NULL, "Unable to patch Registry (HKCU #2).", "ColdClientLoader", MB_ICONERROR);
return 1;
}
// this fails due to admin rights when Steam isn't installed, not a big deal // this fails due to admin rights when Steam isn't installed, not a big deal
// ---------------------------------------------- // ----------------------------------------------
patch_registry_hklm(); patch_registry_hklm();
// if (!patch_registry_hklm()) { // if (!patch_registry_hklm()) {
// cleanup_registry_hkcu(); // cleanup_registry_hkcu();
// cleanup_registry_hkcu_2();
// cleanup_registry_hklm(); // cleanup_registry_hklm();
// cleanup_registry_hkcs();
//
// logger.write("Unable to patch Registry (HKLM).");
// MessageBoxA(NULL, "Unable to patch Registry (HKLM).", "ColdClientLoader", MB_ICONERROR); // MessageBoxA(NULL, "Unable to patch Registry (HKLM).", "ColdClientLoader", MB_ICONERROR);
// return 1; // return 1;
// } // }
@ -597,7 +706,9 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
patch_registry_hkcs(); patch_registry_hkcs();
// if (!patch_registry_hkcs()) { // if (!patch_registry_hkcs()) {
// cleanup_registry_hkcu(); // cleanup_registry_hkcu();
// cleanup_registry_hkcu_2();
// cleanup_registry_hklm(); // cleanup_registry_hklm();
// cleanup_registry_hkcs();
// logger.write("Unable to patch Registry (HKCS)."); // logger.write("Unable to patch Registry (HKCS).");
// MessageBoxA(NULL, "Unable to patch Registry (HKCS).", "ColdClientLoader", MB_ICONERROR); // MessageBoxA(NULL, "Unable to patch Registry (HKCS).", "ColdClientLoader", MB_ICONERROR);
@ -621,6 +732,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
if (!CreateProcessW(exe_file.c_str(), (LPWSTR)CommandLine.c_str(), NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, common_helpers::to_wstr(ExeRunDir).c_str(), &info, &processInfo)) { if (!CreateProcessW(exe_file.c_str(), (LPWSTR)CommandLine.c_str(), NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, common_helpers::to_wstr(ExeRunDir).c_str(), &info, &processInfo)) {
logger.write("Unable to load the requested EXE file, error = " + std::to_string(GetLastError())); logger.write("Unable to load the requested EXE file, error = " + std::to_string(GetLastError()));
cleanup_registry_hkcu(); cleanup_registry_hkcu();
cleanup_registry_hkcu_2();
cleanup_registry_hklm(); cleanup_registry_hklm();
cleanup_registry_hkcs(); cleanup_registry_hkcs();
MessageBoxA(NULL, "Unable to load the requested EXE file.", "ColdClientLoader", MB_ICONERROR); MessageBoxA(NULL, "Unable to load the requested EXE file.", "ColdClientLoader", MB_ICONERROR);
@ -645,6 +757,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
CloseHandle(processInfo.hThread); CloseHandle(processInfo.hThread);
cleanup_registry_hkcu(); cleanup_registry_hkcu();
cleanup_registry_hkcu_2();
cleanup_registry_hklm(); cleanup_registry_hklm();
cleanup_registry_hkcs(); cleanup_registry_hkcs();
@ -686,6 +799,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
if (PersistentMode != 2 || AppId.empty()) { // persistent mode 0 or 1, or mode 2 without app id if (PersistentMode != 2 || AppId.empty()) { // persistent mode 0 or 1, or mode 2 without app id
cleanup_registry_hkcu(); cleanup_registry_hkcu();
cleanup_registry_hkcu_2();
cleanup_registry_hklm(); cleanup_registry_hklm();
cleanup_registry_hkcs(); cleanup_registry_hkcs();
} }