From 450a345333e6d2e4f25b5aacd636a94b46082393 Mon Sep 17 00:00:00 2001 From: a Date: Fri, 2 Aug 2024 23:41:53 +0300 Subject: [PATCH] * add instance of game_stats for servers, client and server instances are requested by games * delete ended game_stats sessions after 15 seconds to reduce memory usage --- dll/dll/steam_client.h | 2 + dll/dll/steam_gamestats.h | 145 ++++++++++++++------------ dll/steam_client.cpp | 1 + dll/steam_client_interface_getter.cpp | 14 ++- dll/steam_gamestats.cpp | 145 +++++++++++++++++++------- 5 files changed, 196 insertions(+), 111 deletions(-) diff --git a/dll/dll/steam_client.h b/dll/dll/steam_client.h index af958afe..5446a92a 100644 --- a/dll/dll/steam_client.h +++ b/dll/dll/steam_client.h @@ -154,6 +154,8 @@ public: Steam_Networking_Messages *steam_gameserver_networking_messages{}; Steam_Game_Coordinator *steam_gameserver_game_coordinator{}; Steam_Masterserver_Updater *steam_masterserver_updater{}; + Steam_GameStats *steam_gameserver_gamestats{}; + Steam_AppTicket *steam_app_ticket{}; Steam_Overlay* steam_overlay{}; diff --git a/dll/dll/steam_gamestats.h b/dll/dll/steam_gamestats.h index c0856bad..54b9da67 100644 --- a/dll/dll/steam_gamestats.h +++ b/dll/dll/steam_gamestats.h @@ -26,91 +26,98 @@ class Steam_GameStats : public ISteamGameStats { - enum class AttributeType_t - { - Int, Str, Float, Int64, - }; +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 - struct Attribute_t - { - const AttributeType_t type; - union { - int32 n_data; - std::string s_data; - float f_data; - int64 ll_data; - }; + enum class AttributeType_t + { + Int, Str, Float, Int64, + }; - Attribute_t(AttributeType_t type); - Attribute_t(const Attribute_t &other); - Attribute_t(Attribute_t &&other); - ~Attribute_t(); - }; + struct Attribute_t + { + const AttributeType_t type; + union { + int32 n_data; + std::string s_data; + float f_data; + int64 ll_data; + }; - struct Row_t - { - bool committed = false; - std::map attributes{}; - }; + Attribute_t(AttributeType_t type); + Attribute_t(const Attribute_t &other); + Attribute_t(Attribute_t &&other); + ~Attribute_t(); + }; - struct Table_t - { - std::vector rows{}; - }; + struct Row_t + { + bool committed = false; + std::map attributes{}; + }; - struct Session_t - { - EGameStatsAccountType nAccountType{}; - RTime32 rtTimeStarted{}; - RTime32 rtTimeEnded{}; - int nReasonCode{}; - bool ended = false; - std::map attributes{}; + struct Table_t + { + std::vector rows{}; + }; - std::vector> tables{}; - }; + struct Session_t + { + EGameStatsAccountType nAccountType{}; + RTime32 rtTimeStarted{}; + RTime32 rtTimeEnded{}; + int nReasonCode{}; + bool ended = false; + std::map attributes{}; - class Settings *settings{}; - class Networking *network{}; - class SteamCallResults *callback_results{}; - class SteamCallBacks *callbacks{}; - class RunEveryRunCB *run_every_runcb{}; - - std::vector sessions{}; + std::vector> tables{}; + }; + + class Settings *settings{}; + class Networking *network{}; + class SteamCallResults *callback_results{}; + class SteamCallBacks *callbacks{}; + class RunEveryRunCB *run_every_runcb{}; + + std::map sessions{}; - bool valid_stats_account_type(int8 nAccountType); - 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_row_att(uint64 ulRowID, const char *att_name, Table_t &table, AttributeType_t type_if_create); + uint64 create_session_id() const; + bool valid_stats_account_type(int8 nAccountType); + 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_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(); - // user connect/disconnect - void network_callback_low_level(Common_Message *msg); + // user connect/disconnect + void network_callback_low_level(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_network_low_level(void *object, Common_Message *msg); - static void steam_gamestats_run_every_runcb(void *object); public: - Steam_GameStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb); - ~Steam_GameStats(); - - SteamAPICall_t GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted ); - SteamAPICall_t EndSession( uint64 ulSessionID, RTime32 rtTimeEnded, int nReasonCode ); - EResult AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData ); - EResult AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData ); - EResult AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData ); + Steam_GameStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb); + ~Steam_GameStats(); + + SteamAPICall_t GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted ); + SteamAPICall_t EndSession( uint64 ulSessionID, RTime32 rtTimeEnded, int nReasonCode ); + EResult AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData ); + EResult AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData ); + EResult AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData ); - EResult AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName ); - EResult CommitRow( uint64 ulRowID ); - EResult CommitOutstandingRows( uint64 ulSessionID ); - EResult AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData ); - EResult AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData ); - EResult AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData ); + EResult AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName ); + EResult CommitRow( uint64 ulRowID ); + EResult CommitOutstandingRows( uint64 ulSessionID ); + EResult AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData ); + EResult AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData ); + EResult AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData ); - EResult AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData ); - EResult AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData ); + EResult AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData ); + EResult AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData ); }; diff --git a/dll/steam_client.cpp b/dll/steam_client.cpp index e66a3248..dc3a453f 100644 --- a/dll/steam_client.cpp +++ b/dll/steam_client.cpp @@ -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_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_gameserver_gamestats = new Steam_GameStats(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); PRINT_DEBUG("init AppTicket"); steam_app_ticket = new Steam_AppTicket(settings_client); diff --git a/dll/steam_client_interface_getter.cpp b/dll/steam_client_interface_getter.cpp index 38f32f03..ef2ee181 100644 --- a/dll/steam_client_interface_getter.cpp +++ b/dll/steam_client_interface_getter.cpp @@ -37,8 +37,16 @@ ISteamGameStats *Steam_Client::GetISteamGameStats( HSteamUser hSteamUser, HSteam PRINT_DEBUG("%s", pchVersion); 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) { - return reinterpret_cast(static_cast(steam_gamestats)); + return reinterpret_cast(static_cast(steam_gamestats_tmp)); } report_missing_impl_and_exit(pchVersion, EMU_FUNC_NAME); @@ -249,13 +257,15 @@ ISteamMatchmakingServers *Steam_Client::GetISteamMatchmakingServers( HSteamUser // returns the a generic interface 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; bool server = false; if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) { + // PRINT_DEBUG("requesting interface with server pipe"); server = true; } else { + // PRINT_DEBUG("requesting interface with client pipe"); // if this is a user pipe, and version != "SteamNetworkingUtils", and version != "SteamUtils" if ((strstr(pchVersion, "SteamNetworkingUtils") != pchVersion) && (strstr(pchVersion, "SteamUtils") != pchVersion)) { if (!hSteamUser) return NULL; diff --git a/dll/steam_gamestats.cpp b/dll/steam_gamestats.cpp index 55698f14..9039acb3 100644 --- a/dll/steam_gamestats.cpp +++ b/dll/steam_gamestats.cpp @@ -98,18 +98,26 @@ Steam_GameStats::Steam_GameStats(class Settings *settings, class Networking *net this->callbacks = callbacks; 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); } 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); } +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) { switch ((EGameStatsAccountType)nAccountType) { @@ -128,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 &item){ return item.first == table_name; }); if (session.tables.rend() == table_it) { - session.tables.emplace_back(std::pair{}); - table = &session.tables.back().second; + auto& [key, val] = session.tables.emplace_back(std::pair{}); + table = &val; } else { table = &table_it->second; } @@ -151,9 +159,18 @@ Steam_GameStats::Attribute_t *Steam_GameStats::get_or_create_row_att(uint64 ulRo return &ele_it->second; } +Steam_GameStats::Session_t* Steam_GameStats::get_last_active_session() +{ + auto active_session_it = std::find_if(sessions.rbegin(), sessions.rend(), [](std::pair 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 ) { + // 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); std::lock_guard lock(global_mutex); @@ -161,11 +178,13 @@ SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccou (nAppID < 0) || (settings->get_local_game_id().AppID() != (uint32)nAppID) || !valid_stats_account_type(nAccountType)) { + GameStatsSessionIssued_t data_invalid{}; data_invalid.m_bCollectingAny = false; data_invalid.m_bCollectingDetails = false; data_invalid.m_eResult = EResult::k_EResultInvalidParam; data_invalid.m_ulSessionID = 0; + PRINT_DEBUG("[X] invalid param"); 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 @@ -173,16 +192,18 @@ SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccou return ret; } + auto session_id = create_session_id(); Session_t new_session{}; new_session.nAccountType = (EGameStatsAccountType)nAccountType; new_session.rtTimeStarted = rtTimeStarted; - sessions.emplace_back(new_session); + sessions.insert_or_assign(session_id, new_session); GameStatsSessionIssued_t data{}; data.m_bCollectingAny = true; // TODO is this correct? data.m_bCollectingDetails = true; // TODO is this correct? 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)); // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected @@ -195,10 +216,12 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn PRINT_DEBUG("%llu, %u, %i", ulSessionID, rtTimeEnded, nReasonCode); 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{}; data_invalid.m_eResult = EResult::k_EResultInvalidParam; 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)); // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected @@ -206,7 +229,7 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn return ret; } - auto& session = sessions[static_cast(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) { GameStatsSessionClosed_t data_invalid{}; data_invalid.m_eResult = EResult::k_EResultExpired; // TODO is this correct? @@ -225,6 +248,7 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn GameStatsSessionClosed_t data{}; data.m_eResult = EResult::k_EResultOK; data.m_ulSessionID = ulSessionID; + PRINT_DEBUG("ended session"); 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 @@ -234,69 +258,77 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn 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); - 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(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? 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? att->n_data = nData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } 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); - 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(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? 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? att->s_data = pstrData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } 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); - 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(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? 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? att->f_data = fData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } 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); - 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(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? auto table = get_or_create_session_table(session, pstrTableName); table->rows.emplace_back(Row_t{}); if (pulRowID) *pulRowID = (uint64)(table->rows.size() - 1); + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } @@ -305,8 +337,8 @@ EResult Steam_GameStats::CommitRow( uint64 ulRowID ) PRINT_DEBUG("%llu", ulRowID); std::lock_guard lock(global_mutex); - auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + 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; @@ -316,6 +348,7 @@ EResult Steam_GameStats::CommitRow( uint64 ulRowID ) auto& row = table.rows[static_cast(ulRowID)]; row.committed = true; + PRINT_DEBUG("committed successfully"); return EResult::k_EResultOK; } @@ -324,9 +357,10 @@ EResult Steam_GameStats::CommitOutstandingRows( uint64 ulSessionID ) PRINT_DEBUG("%llu", ulSessionID); 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(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? if (session.tables.size()) { @@ -334,18 +368,19 @@ EResult Steam_GameStats::CommitOutstandingRows( uint64 ulSessionID ) row.committed = true; } } + PRINT_DEBUG("committed all successfully"); return EResult::k_EResultOK; } 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); 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; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + 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; @@ -355,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? att->n_data = nData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } 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); 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; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + 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; if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? @@ -375,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? att->s_data = pstrData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } 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); 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; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + 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; if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? @@ -395,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? att->f_data = fData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } 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); - 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(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? 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? att->ll_data = llData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } 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); 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; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + 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; if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? @@ -433,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? att->ll_data = llData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } @@ -442,7 +486,28 @@ EResult Steam_GameStats::AddRowAttributeInt64( uint64 ulRowID, const char *pstrN 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::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; + } + } }