* 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
This commit is contained in:
a 2024-08-02 23:41:53 +03:00
parent 19b5ea2e23
commit 450a345333
5 changed files with 196 additions and 111 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,91 +26,98 @@
class Steam_GameStats : class Steam_GameStats :
public ISteamGameStats public ISteamGameStats
{ {
enum class AttributeType_t private:
{ // how much time to wait before removing ended sessions
Int, Str, Float, Int64, constexpr const static int MAX_DEAD_SESSION_SECONDS = 15; // TODO not sure what would be sensible in this case
};
struct Attribute_t enum class AttributeType_t
{ {
const AttributeType_t type; Int, Str, Float, Int64,
union { };
int32 n_data;
std::string s_data;
float f_data;
int64 ll_data;
};
Attribute_t(AttributeType_t type); struct Attribute_t
Attribute_t(const Attribute_t &other); {
Attribute_t(Attribute_t &&other); const AttributeType_t type;
~Attribute_t(); union {
}; int32 n_data;
std::string s_data;
float f_data;
int64 ll_data;
};
struct Row_t Attribute_t(AttributeType_t type);
{ Attribute_t(const Attribute_t &other);
bool committed = false; Attribute_t(Attribute_t &&other);
std::map<std::string, Attribute_t> attributes{}; ~Attribute_t();
}; };
struct Table_t struct Row_t
{ {
std::vector<Row_t> rows{}; bool committed = false;
}; std::map<std::string, Attribute_t> attributes{};
};
struct Session_t struct Table_t
{ {
EGameStatsAccountType nAccountType{}; std::vector<Row_t> rows{};
RTime32 rtTimeStarted{}; };
RTime32 rtTimeEnded{};
int nReasonCode{};
bool ended = false;
std::map<std::string, Attribute_t> attributes{};
std::vector<std::pair<std::string, Table_t>> tables{}; struct Session_t
}; {
EGameStatsAccountType nAccountType{};
RTime32 rtTimeStarted{};
RTime32 rtTimeEnded{};
int nReasonCode{};
bool ended = false;
std::map<std::string, Attribute_t> attributes{};
class Settings *settings{}; std::vector<std::pair<std::string, Table_t>> tables{};
class Networking *network{}; };
class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb{};
std::vector<Session_t> sessions{}; class Settings *settings{};
class Networking *network{};
class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb{};
std::map<uint64, Session_t> sessions{};
bool valid_stats_account_type(int8 nAccountType); uint64 create_session_id() const;
Table_t *get_or_create_session_table(Session_t &session, const char *table_name); bool valid_stats_account_type(int8 nAccountType);
Attribute_t *get_or_create_session_att(const char *att_name, Session_t &session, AttributeType_t type_if_create); Table_t *get_or_create_session_table(Session_t &session, const char *table_name);
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_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 // user connect/disconnect
void network_callback_low_level(Common_Message *msg); 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: 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();
SteamAPICall_t GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted ); SteamAPICall_t GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted );
SteamAPICall_t EndSession( uint64 ulSessionID, RTime32 rtTimeEnded, int nReasonCode ); SteamAPICall_t EndSession( uint64 ulSessionID, RTime32 rtTimeEnded, int nReasonCode );
EResult AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData ); EResult AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData );
EResult AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData ); EResult AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData );
EResult AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData ); EResult AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData );
EResult AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName ); EResult AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName );
EResult CommitRow( uint64 ulRowID ); EResult CommitRow( uint64 ulRowID );
EResult CommitOutstandingRows( uint64 ulSessionID ); EResult CommitOutstandingRows( uint64 ulSessionID );
EResult AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData ); EResult AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData );
EResult AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData ); EResult AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData );
EResult AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData ); EResult AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData );
EResult AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData ); EResult AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData );
EResult AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData ); EResult AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData );
}; };

View File

@ -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

@ -98,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) {
@ -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<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;
} }
@ -151,9 +159,18 @@ Steam_GameStats::Attribute_t *Steam_GameStats::get_or_create_row_att(uint64 ulRo
return &ele_it->second; 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<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);
@ -161,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
@ -173,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
@ -195,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
@ -206,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?
@ -225,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
@ -234,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;
} }
@ -305,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;
@ -316,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;
} }
@ -324,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()) {
@ -334,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;
@ -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? 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?
@ -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? 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?
@ -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? 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?
@ -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? 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;
} }
@ -442,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;
}
}
} }