1
0
Fork 0
mirror of https://github.com/Detanup01/gbe_fork.git synced 2025-08-10 09:25:34 +02:00

Add encrypted app ticket token support

This commit is contained in:
GittyGittyKit 2025-07-08 14:08:30 +02:00
parent d0c36e17d9
commit a303521e92
16 changed files with 475 additions and 80 deletions

View file

@ -331,3 +331,24 @@ package_linux.sh <build_folder>
``` ```
`build_folder` is any folder inside `build/linux`, for example: `gmake2/release` `build_folder` is any folder inside `build/linux`, for example: `gmake2/release`
The above example will create a compressed `.tar` archive inside `build/package/linux/` The above example will create a compressed `.tar` archive inside `build/package/linux/`
---
## **EncryptedAppTicket Token Support**
* Add pre-generated encrypted app tickets without modifying code
* Create `configs.user.ini` in your game's settings folder
* Add a line starting with `token=` followed by your Base64-encoded ticket
* Example: `token=CAIxxxxxxxxxxxxxxx...`
* Most valid tickets start with `CAI` after Base64 encoding
* If no token is found, emulator falls back to standard ticket generation
**Why use this?**
* For apps that require specific ticket formats
* When you need to use tickets from real Steam client
To generate a Base64 token from an existing ticket file:
```python
import base64
with open('ticket.bin', 'rb') as f:
print(base64.b64encode(f.read()).decode('utf-8'))

72
dll/base64.cpp Normal file
View file

@ -0,0 +1,72 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "dll/base64.h"
// Base64 character table
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
// Check if a character is a valid base64 character
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
// Decode a base64 encoded string into a byte vector
std::vector<uint8_t> base64_decode(const std::string& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::vector<uint8_t> ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret.push_back(char_array_3[i]);
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;
for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++)
ret.push_back(char_array_3[j]);
}
return ret;
}

23
dll/dll/base64.h Normal file
View file

@ -0,0 +1,23 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#pragma once
#include <string>
#include <vector>
// Decodes a base64 encoded string into a byte vector
std::vector<uint8_t> base64_decode(const std::string& encoded_string);

View file

@ -66,6 +66,7 @@ enum Callback_Ids {
CALLBACK_ID_NETWORKING_MESSAGES, CALLBACK_ID_NETWORKING_MESSAGES,
CALLBACK_ID_GAMESERVER_STATS, CALLBACK_ID_GAMESERVER_STATS,
CALLBACK_ID_LEADERBOARDS_STATS, CALLBACK_ID_LEADERBOARDS_STATS,
CALLBACK_ID_USER_STATS,
CALLBACK_IDS_MAX CALLBACK_IDS_MAX
}; };

View file

@ -88,7 +88,7 @@ struct Leaderboard_config {
}; };
struct Stat_config { struct Stat_config {
GameServerStats_Messages::StatInfo::Stat_Type type{}; StatInfo::Stat_Type type{};
union { union {
float default_value_float; float default_value_float;
int32 default_value_int; int32 default_value_int;
@ -233,6 +233,9 @@ public:
constexpr const static int INVALID_IMAGE_HANDLE = 0; constexpr const static int INVALID_IMAGE_HANDLE = 0;
constexpr const static int UNLOADED_IMAGE_HANDLE = -1; constexpr const static int UNLOADED_IMAGE_HANDLE = -1;
// Base64 encoded token for EncryptedAppTicket
std::string encrypted_app_ticket_token{};
//Depots //Depots
std::vector<DepotId_t> depots{}; std::vector<DepotId_t> depots{};
@ -429,6 +432,8 @@ public:
bool hasOverlayAutoAcceptInviteFromFriend(uint64_t friend_id) const; bool hasOverlayAutoAcceptInviteFromFriend(uint64_t friend_id) const;
size_t overlayAutoAcceptInvitesCount() const; size_t overlayAutoAcceptInvitesCount() const;
// Method to load token from configs.user.ini
bool load_token_from_config();
}; };
#endif // SETTINGS_INCLUDE_H #endif // SETTINGS_INCLUDE_H

View file

@ -42,7 +42,7 @@ public ISteamGameServerStats
struct CachedStat { struct CachedStat {
bool dirty = false; // true means it was changed on the server and should be sent to the user bool dirty = false; // true means it was changed on the server and should be sent to the user
GameServerStats_Messages::StatInfo stat{}; StatInfo stat{};
}; };
struct CachedAchievement { struct CachedAchievement {
bool dirty = false; // true means it was changed on the server and should be sent to the user bool dirty = false; // true means it was changed on the server and should be sent to the user

View file

@ -57,6 +57,12 @@ struct achievement_trigger {
bool should_indicate_progress(int32 stat) const; bool should_indicate_progress(int32 stat) const;
}; };
struct Pending_User_Stats_Request {
SteamAPICall_t api_id{};
std::chrono::high_resolution_clock::time_point created_time{};
bool is_sent{};
};
class Steam_User_Stats : class Steam_User_Stats :
public ISteamUserStats001, public ISteamUserStats001,
public ISteamUserStats002, public ISteamUserStats002,
@ -110,6 +116,9 @@ private:
GameServerStats_Messages::AllStats pending_server_updates{}; GameServerStats_Messages::AllStats pending_server_updates{};
std::map<uint64, Pending_User_Stats_Request> pending_user_stats_requests;
std::map<uint64, Steam_User_Stats_Data> received_user_stats_data;
void load_achievements_db(); void load_achievements_db();
void load_achievements(); void load_achievements();
void save_achievements(); void save_achievements();
@ -133,13 +142,16 @@ private:
// change stats/achievements without sending back to server // change stats/achievements without sending back to server
bool clear_stats_internal(); bool clear_stats_internal();
InternalSetResult<int32> set_stat_internal( const char *pchName, int32 nData ); InternalSetResult<int32> set_stat_internal( const char *pchName, int32 nData );
InternalSetResult<std::pair<GameServerStats_Messages::StatInfo::Stat_Type, float>> set_stat_internal( const char *pchName, float fData ); InternalSetResult<std::pair<StatInfo::Stat_Type, float>> set_stat_internal( const char *pchName, float fData );
InternalSetResult<std::pair<GameServerStats_Messages::StatInfo::Stat_Type, float>> update_avg_rate_stat_internal( const char *pchName, float flCountThisSession, double dSessionLength ); InternalSetResult<std::pair<StatInfo::Stat_Type, float>> update_avg_rate_stat_internal( const char *pchName, float flCountThisSession, double dSessionLength );
InternalSetResult<bool> set_achievement_internal( const char *pchName ); InternalSetResult<bool> set_achievement_internal( const char *pchName );
InternalSetResult<bool> clear_achievement_internal( const char *pchName ); InternalSetResult<bool> clear_achievement_internal( const char *pchName );
SteamAPICall_t trigger_user_stats_received(CSteamID steam_id_user, SteamAPICall_t api_id = k_uAPICallInvalid, bool success = true);
void send_updated_stats(); void send_updated_stats();
void load_achievements_icons(); void load_achievements_icons();
void send_pending_user_stats_requests();
void process_pending_user_stats_requests(Common_Message *msg);
void steam_run_callback(); void steam_run_callback();
// requests from server // requests from server

View file

@ -216,26 +216,27 @@ message Steam_Messages {
} }
} }
message StatInfo {
enum Stat_Type {
STAT_TYPE_INT = 0;
STAT_TYPE_FLOAT = 1;
STAT_TYPE_AVGRATE = 2;
}
message AvgStatInfo {
float count_this_session = 1;
double session_length = 2;
}
Stat_Type stat_type = 1;
oneof stat_value {
float value_float = 2;
int32 value_int = 3;
}
optional AvgStatInfo value_avg = 4; // only set when type != INT
}
message GameServerStats_Messages { message GameServerStats_Messages {
// --- basic definitions // --- basic definitions
message StatInfo {
enum Stat_Type {
STAT_TYPE_INT = 0;
STAT_TYPE_FLOAT = 1;
STAT_TYPE_AVGRATE = 2;
}
message AvgStatInfo {
float count_this_session = 1;
double session_length = 2;
}
Stat_Type stat_type = 1;
oneof stat_value {
float value_float = 2;
int32 value_int = 3;
}
optional AvgStatInfo value_avg = 4; // only set when type != INT
}
message AchievementInfo { message AchievementInfo {
bool achieved = 1; bool achieved = 1;
} }
@ -295,6 +296,20 @@ message Leaderboards_Messages {
} }
} }
message Steam_User_Stats_Data {
map<string, StatInfo> user_stats = 1;
}
message Steam_User_Stats_Messages {
enum Types {
REQUEST_USERSTATS = 0;
RESPONSE_USERSTATS = 1;
}
Types type = 1;
optional Steam_User_Stats_Data steam_user_stats_data = 2;
}
message Common_Message { message Common_Message {
uint64 source_id = 1; // SteamID64 of the sender uint64 source_id = 1; // SteamID64 of the sender
uint64 dest_id = 2; // SteamID64 of the target receiver uint64 dest_id = 2; // SteamID64 of the target receiver
@ -314,6 +329,7 @@ message Common_Message {
Networking_Messages networking_messages = 15; Networking_Messages networking_messages = 15;
GameServerStats_Messages gameserver_stats_messages = 16; GameServerStats_Messages gameserver_stats_messages = 16;
Leaderboards_Messages leaderboards_messages = 17; Leaderboards_Messages leaderboards_messages = 17;
Steam_User_Stats_Messages steam_user_stats_messages = 18;
} }
uint32 source_ip = 128; uint32 source_ip = 128;

View file

@ -608,6 +608,11 @@ void Networking::do_callbacks_message(Common_Message *msg)
PRINT_DEBUG("has_leaderboards_messages"); PRINT_DEBUG("has_leaderboards_messages");
run_callbacks(CALLBACK_ID_LEADERBOARDS_STATS, msg); run_callbacks(CALLBACK_ID_LEADERBOARDS_STATS, msg);
} }
if (msg->has_steam_user_stats_messages()) {
PRINT_DEBUG("has_steam_user_stats_messages");
run_callbacks(CALLBACK_ID_USER_STATS, msg);
}
} }

View file

@ -17,6 +17,7 @@
#include "dll/settings.h" #include "dll/settings.h"
#include "dll/steam_app_ids.h" #include "dll/steam_app_ids.h"
#include <fstream>
std::string Settings::sanitize(const std::string &name) std::string Settings::sanitize(const std::string &name)
@ -84,6 +85,7 @@ Settings::Settings(CSteamID steam_id, CGameID game_id, const std::string &name,
this->language = lang; this->language = lang;
this->offline = offline; this->offline = offline;
this->encrypted_app_ticket_token = ""; // Initialize token field
} }
// user id // user id
@ -425,3 +427,32 @@ size_t Settings::overlayAutoAcceptInvitesCount() const
{ {
return auto_accept_overlay_invites_friends.size(); return auto_accept_overlay_invites_friends.size();
} }
// Load token from configs.user.ini
bool Settings::load_token_from_config()
{
encrypted_app_ticket_token.clear();
std::string config_path = Local_Storage::get_game_settings_path() + "configs.user.ini";
PRINT_DEBUG("Attempting to read token from %s\n", config_path.c_str());
std::ifstream config_file(utf8_decode(config_path));
if (!config_file.is_open()) {
PRINT_DEBUG("Failed to open configs.user.ini for token reading\n");
return false;
}
std::string line;
while (std::getline(config_file, line)) {
// Look for "token=" at the beginning of line
if (line.find("token=") == 0) {
// Token found - extract everything after "token="
encrypted_app_ticket_token = line.substr(6); // "token=" is 6 characters long
PRINT_DEBUG("Found token in configs.user.ini, length: %zu\n", encrypted_app_ticket_token.length());
return true;
}
}
PRINT_DEBUG("No token found in configs.user.ini\n");
return false;
}

View file

@ -859,15 +859,15 @@ static void parse_stats(class Settings *settings_client, class Settings *setting
try { try {
if (stat_type == "float") { if (stat_type == "float") {
config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT; config.type = StatInfo::STAT_TYPE_FLOAT;
config.default_value_float = std::stof(stat_default_value); config.default_value_float = std::stof(stat_default_value);
config.global_value_double = std::stod(stat_global_value); config.global_value_double = std::stod(stat_global_value);
} else if (stat_type == "int") { } else if (stat_type == "int") {
config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_INT; config.type = StatInfo::STAT_TYPE_INT;
config.default_value_int = std::stol(stat_default_value); config.default_value_int = std::stol(stat_default_value);
config.global_value_int64 = std::stoll(stat_global_value); config.global_value_int64 = std::stoll(stat_global_value);
} else if (stat_type == "avgrate") { } else if (stat_type == "avgrate") {
config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE; config.type = StatInfo::STAT_TYPE_AVGRATE;
config.default_value_float = std::stof(stat_default_value); config.default_value_float = std::stof(stat_default_value);
config.global_value_double = std::stod(stat_global_value); config.global_value_double = std::stod(stat_global_value);
} else { } else {

View file

@ -164,7 +164,7 @@ bool Steam_GameServerStats::GetUserStat( CSteamID steamIDUser, const char *pchNa
auto stat = find_stat(steamIDUser, pchName); auto stat = find_stat(steamIDUser, pchName);
if (!stat) return false; if (!stat) return false;
if (stat->stat.stat_type() != GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false; if (stat->stat.stat_type() != StatInfo::STAT_TYPE_INT) return false;
if (pData) *pData = stat->stat.value_int(); if (pData) *pData = stat->stat.value_int();
return true; return true;
@ -180,7 +180,7 @@ bool Steam_GameServerStats::GetUserStat( CSteamID steamIDUser, const char *pchNa
auto stat = find_stat(steamIDUser, pchName); auto stat = find_stat(steamIDUser, pchName);
if (!stat) return false; if (!stat) return false;
if (stat->stat.stat_type() == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false; if (stat->stat.stat_type() == StatInfo::STAT_TYPE_INT) return false;
if (pData) *pData = stat->stat.value_float(); if (pData) *pData = stat->stat.value_float();
return true; return true;
@ -216,7 +216,7 @@ bool Steam_GameServerStats::SetUserStat( CSteamID steamIDUser, const char *pchNa
auto stat = find_stat(steamIDUser, pchName); auto stat = find_stat(steamIDUser, pchName);
if (!stat) return false; if (!stat) return false;
if (stat->stat.stat_type() != GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false; if (stat->stat.stat_type() != StatInfo::STAT_TYPE_INT) return false;
if (stat->stat.value_int() == nData) return true; // don't waste time if (stat->stat.value_int() == nData) return true; // don't waste time
stat->dirty = true; stat->dirty = true;
@ -237,7 +237,7 @@ bool Steam_GameServerStats::SetUserStat( CSteamID steamIDUser, const char *pchNa
auto stat = find_stat(steamIDUser, pchName); auto stat = find_stat(steamIDUser, pchName);
if (!stat) return false; if (!stat) return false;
if (stat->stat.stat_type() == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false; if (stat->stat.stat_type() == StatInfo::STAT_TYPE_INT) return false;
if (stat->stat.value_float() == fData) return true; // don't waste time if (stat->stat.value_float() == fData) return true; // don't waste time
stat->dirty = true; stat->dirty = true;
@ -258,7 +258,7 @@ bool Steam_GameServerStats::UpdateUserAvgRateStat( CSteamID steamIDUser, const c
auto stat = find_stat(steamIDUser, pchName); auto stat = find_stat(steamIDUser, pchName);
if (!stat) return false; if (!stat) return false;
if (stat->stat.stat_type() == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false; if (stat->stat.stat_type() == StatInfo::STAT_TYPE_INT) return false;
// don't waste time // don't waste time
if (stat->stat.has_value_avg() && if (stat->stat.has_value_avg() &&
stat->stat.value_avg().count_this_session() == flCountThisSession && stat->stat.value_avg().count_this_session() == flCountThisSession &&
@ -270,7 +270,7 @@ bool Steam_GameServerStats::UpdateUserAvgRateStat( CSteamID steamIDUser, const c
// https://protobuf.dev/reference/cpp/cpp-generated/#string // https://protobuf.dev/reference/cpp/cpp-generated/#string
// set_allocated_xxx() takes ownership of the allocated object, no need to delete // set_allocated_xxx() takes ownership of the allocated object, no need to delete
auto avg_info = new GameServerStats_Messages::StatInfo::AvgStatInfo(); auto avg_info = new StatInfo::AvgStatInfo();
avg_info->set_count_this_session(flCountThisSession); avg_info->set_count_this_session(flCountThisSession);
avg_info->set_session_length(dSessionLength); avg_info->set_session_length(dSessionLength);
stat->stat.set_allocated_value_avg(avg_info); stat->stat.set_allocated_value_avg(avg_info);

View file

@ -1243,6 +1243,10 @@ ESteamNetworkingAvailability Steam_Networking_Sockets::InitAuthentication()
{ {
PRINT_DEBUG_TODO(); PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
SteamNetAuthenticationStatus_t data{};
data.m_eAvail = k_ESteamNetworkingAvailability_Current;
memcpy(data.m_debugMsg, "OK", 3);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return k_ESteamNetworkingAvailability_Current; return k_ESteamNetworkingAvailability_Current;
} }
@ -1257,6 +1261,12 @@ ESteamNetworkingAvailability Steam_Networking_Sockets::GetAuthenticationStatus(
{ {
PRINT_DEBUG_TODO(); PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pDetails)
{
*pDetails = {};
pDetails->m_eAvail = k_ESteamNetworkingAvailability_Current;
memcpy(pDetails->m_debugMsg, "OK", 3);
}
return k_ESteamNetworkingAvailability_Current; return k_ESteamNetworkingAvailability_Current;
} }

View file

@ -18,6 +18,7 @@
#include "dll/steam_user.h" #include "dll/steam_user.h"
#include "dll/auth.h" #include "dll/auth.h"
#include "dll/appticket.h" #include "dll/appticket.h"
#include "dll/base64.h"
Steam_User::Steam_User(Settings *settings, Local_Storage *local_storage, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks) Steam_User::Steam_User(Settings *settings, Local_Storage *local_storage, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{ {
@ -761,6 +762,35 @@ SteamAPICall_t Steam_User::RequestEncryptedAppTicket( void *pDataToInclude, int
bool Steam_User::GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) bool Steam_User::GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{ {
PRINT_DEBUG("%i %p %p", cbMaxTicket, pTicket, pcbTicket); PRINT_DEBUG("%i %p %p", cbMaxTicket, pTicket, pcbTicket);
// Try to load a token from configs.user.ini first
if (settings->load_token_from_config() && !settings->encrypted_app_ticket_token.empty()) {
PRINT_DEBUG("Using token from configs.user.ini\n");
// Decode token from Base64
std::vector<uint8_t> ticket_data = base64_decode(settings->encrypted_app_ticket_token);
if (ticket_data.empty()) {
PRINT_DEBUG("Failed to decode token from base64\n");
} else {
uint32 ticket_size = static_cast<uint32>(ticket_data.size());
if (pcbTicket) *pcbTicket = ticket_size;
if (cbMaxTicket <= 0) {
if (!pcbTicket) return false;
return true;
}
if (!pTicket) return false;
if (ticket_size > static_cast<uint32>(cbMaxTicket)) return false;
memcpy(pTicket, ticket_data.data(), ticket_size);
PRINT_DEBUG("Successfully used token from configs.user.ini (%u bytes)\n", ticket_size);
return true;
}
}
// Fallback to the standard ticket generation if no token was found or decoded
uint32 ticket_size = static_cast<uint32>(encrypted_app_ticket.size()); uint32 ticket_size = static_cast<uint32>(encrypted_app_ticket.size());
if (pcbTicket) *pcbTicket = ticket_size; if (pcbTicket) *pcbTicket = ticket_size;
@ -860,4 +890,4 @@ bool Steam_User::BSetDurationControlOnlineState( EDurationControlOnlineState eNe
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_ENTRY();
return false; return false;
} }

View file

@ -113,6 +113,7 @@ Steam_User_Stats::Steam_User_Stats(Settings *settings, class Networking *network
if (settings->share_leaderboards_over_network) { if (settings->share_leaderboards_over_network) {
this->network->setCallback(CALLBACK_ID_LEADERBOARDS_STATS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_leaderboards, this); this->network->setCallback(CALLBACK_ID_LEADERBOARDS_STATS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_leaderboards, this);
} }
this->network->setCallback(CALLBACK_ID_USER_STATS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_stats, this);
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_low_level, this); this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_low_level, this);
this->run_every_runcb->add(&Steam_User_Stats::steam_user_stats_run_every_runcb, this); this->run_every_runcb->add(&Steam_User_Stats::steam_user_stats_run_every_runcb, this);
} }
@ -125,6 +126,7 @@ Steam_User_Stats::~Steam_User_Stats()
if (settings->share_leaderboards_over_network) { if (settings->share_leaderboards_over_network) {
this->network->rmCallback(CALLBACK_ID_LEADERBOARDS_STATS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_leaderboards, this); this->network->rmCallback(CALLBACK_ID_LEADERBOARDS_STATS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_leaderboards, this);
} }
this->network->rmCallback(CALLBACK_ID_USER_STATS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_stats, this);
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_low_level, this); this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_User_Stats::steam_user_stats_network_low_level, this);
this->run_every_runcb->remove(&Steam_User_Stats::steam_user_stats_run_every_runcb, this); this->run_every_runcb->remove(&Steam_User_Stats::steam_user_stats_run_every_runcb, this);
} }
@ -195,9 +197,9 @@ ESteamUserStatType Steam_User_Stats::GetStatType( CGameID nGameID, const char *p
switch (stat_info->second.type) switch (stat_info->second.type)
{ {
case GameServerStats_Messages::StatInfo::STAT_TYPE_INT: return ESteamUserStatType::k_ESteamUserStatTypeINT; case StatInfo::STAT_TYPE_INT: return ESteamUserStatType::k_ESteamUserStatTypeINT;
case GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT: return ESteamUserStatType::k_ESteamUserStatTypeFLOAT; case StatInfo::STAT_TYPE_FLOAT: return ESteamUserStatType::k_ESteamUserStatTypeFLOAT;
case GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE: return ESteamUserStatType::k_ESteamUserStatTypeAVGRATE; case StatInfo::STAT_TYPE_AVGRATE: return ESteamUserStatType::k_ESteamUserStatTypeAVGRATE;
default: PRINT_DEBUG("[X] unhandled type %i", (int)stat_info->second.type); break; default: PRINT_DEBUG("[X] unhandled type %i", (int)stat_info->second.type); break;
} }
@ -440,6 +442,7 @@ void Steam_User_Stats::steam_run_callback()
{ {
send_updated_stats(); send_updated_stats();
load_achievements_icons(); load_achievements_icons();
send_pending_user_stats_requests();
} }
@ -465,6 +468,25 @@ void Steam_User_Stats::network_callback_low_level(Common_Message *msg)
board.remove_entries(steamid); board.remove_entries(steamid);
} }
// TODO: need tests on real steam
bool had_data = false;
auto it_res_r = received_user_stats_data.find(steamid.ConvertToUint64());
if (it_res_r != received_user_stats_data.end()) {
had_data = true;
it_res_r = received_user_stats_data.erase(it_res_r);
}
auto it_res_p = pending_user_stats_requests.find(steamid.ConvertToUint64());
if (it_res_p != pending_user_stats_requests.end()) {
trigger_user_stats_received(steamid, it_res_p->second.api_id);
it_res_p = pending_user_stats_requests.erase(it_res_p);
}
else if (had_data) {
UserStatsUnloaded_t data{};
data.m_steamIDUser = steamid;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.0);
}
// PRINT_DEBUG("removed user %llu", (uint64)steamid.ConvertToUint64()); // PRINT_DEBUG("removed user %llu", (uint64)steamid.ConvertToUint64());
} }
break; break;

View file

@ -32,7 +32,7 @@ bool Steam_User_Stats::clear_stats_internal()
switch (stat.second.type) switch (stat.second.type)
{ {
case GameServerStats_Messages::StatInfo::STAT_TYPE_INT: { case StatInfo::STAT_TYPE_INT: {
auto data = stat.second.default_value_int; auto data = stat.second.default_value_int;
bool needs_disk_write = false; bool needs_disk_write = false;
@ -48,8 +48,8 @@ bool Steam_User_Stats::clear_stats_internal()
} }
break; break;
case GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT: case StatInfo::STAT_TYPE_FLOAT:
case GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE: { case StatInfo::STAT_TYPE_AVGRATE: {
auto data = stat.second.default_value_float; auto data = stat.second.default_value_float;
bool needs_disk_write = false; bool needs_disk_write = false;
@ -85,13 +85,13 @@ Steam_User_Stats::InternalSetResult<int32> Steam_User_Stats::set_stat_internal(
auto stats_data = stats_config.find(stat_name); auto stats_data = stats_config.find(stat_name);
if (stats_config.end() == stats_data && settings->allow_unknown_stats) { if (stats_config.end() == stats_data && settings->allow_unknown_stats) {
Stat_config cfg{}; Stat_config cfg{};
cfg.type = GameServerStats_Messages::StatInfo::STAT_TYPE_INT; cfg.type = StatInfo::STAT_TYPE_INT;
cfg.default_value_int = 0; cfg.default_value_int = 0;
stats_data = settings->setStatDefiniton(stat_name, cfg); stats_data = settings->setStatDefiniton(stat_name, cfg);
} }
if (stats_config.end() == stats_data) return result; if (stats_config.end() == stats_data) return result;
if (stats_data->second.type != GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return result; if (stats_data->second.type != StatInfo::STAT_TYPE_INT) return result;
result.internal_name = stat_name; result.internal_name = stat_name;
result.current_val = nData; result.current_val = nData;
@ -145,11 +145,11 @@ Steam_User_Stats::InternalSetResult<int32> Steam_User_Stats::set_stat_internal(
return result; return result;
} }
Steam_User_Stats::InternalSetResult<std::pair<GameServerStats_Messages::StatInfo::Stat_Type, float>> Steam_User_Stats::set_stat_internal( const char *pchName, float fData ) Steam_User_Stats::InternalSetResult<std::pair<StatInfo::Stat_Type, float>> Steam_User_Stats::set_stat_internal( const char *pchName, float fData )
{ {
PRINT_DEBUG("<float> '%s' = %f", pchName, fData); PRINT_DEBUG("<float> '%s' = %f", pchName, fData);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
Steam_User_Stats::InternalSetResult<std::pair<GameServerStats_Messages::StatInfo::Stat_Type, float>> result{}; Steam_User_Stats::InternalSetResult<std::pair<StatInfo::Stat_Type, float>> result{};
if (!pchName) return result; if (!pchName) return result;
std::string stat_name(common_helpers::to_lower(pchName)); std::string stat_name(common_helpers::to_lower(pchName));
@ -158,13 +158,13 @@ Steam_User_Stats::InternalSetResult<std::pair<GameServerStats_Messages::StatInfo
auto stats_data = stats_config.find(stat_name); auto stats_data = stats_config.find(stat_name);
if (stats_config.end() == stats_data && settings->allow_unknown_stats) { if (stats_config.end() == stats_data && settings->allow_unknown_stats) {
Stat_config cfg{}; Stat_config cfg{};
cfg.type = GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT; cfg.type = StatInfo::STAT_TYPE_FLOAT;
cfg.default_value_float = 0; cfg.default_value_float = 0;
stats_data = settings->setStatDefiniton(stat_name, cfg); stats_data = settings->setStatDefiniton(stat_name, cfg);
} }
if (stats_config.end() == stats_data) return result; if (stats_config.end() == stats_data) return result;
if (stats_data->second.type == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return result; if (stats_data->second.type == StatInfo::STAT_TYPE_INT) return result;
result.internal_name = stat_name; result.internal_name = stat_name;
result.current_val.first = stats_data->second.type; result.current_val.first = stats_data->second.type;
@ -219,11 +219,11 @@ Steam_User_Stats::InternalSetResult<std::pair<GameServerStats_Messages::StatInfo
return result; return result;
} }
Steam_User_Stats::InternalSetResult<std::pair<GameServerStats_Messages::StatInfo::Stat_Type, float>> Steam_User_Stats::update_avg_rate_stat_internal( const char *pchName, float flCountThisSession, double dSessionLength ) Steam_User_Stats::InternalSetResult<std::pair<StatInfo::Stat_Type, float>> Steam_User_Stats::update_avg_rate_stat_internal( const char *pchName, float flCountThisSession, double dSessionLength )
{ {
PRINT_DEBUG("%s", pchName); PRINT_DEBUG("%s", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
Steam_User_Stats::InternalSetResult<std::pair<GameServerStats_Messages::StatInfo::Stat_Type, float>> result{}; Steam_User_Stats::InternalSetResult<std::pair<StatInfo::Stat_Type, float>> result{};
if (!pchName) return result; if (!pchName) return result;
std::string stat_name(common_helpers::to_lower(pchName)); std::string stat_name(common_helpers::to_lower(pchName));
@ -232,13 +232,13 @@ Steam_User_Stats::InternalSetResult<std::pair<GameServerStats_Messages::StatInfo
auto stats_data = stats_config.find(stat_name); auto stats_data = stats_config.find(stat_name);
if (stats_config.end() == stats_data && settings->allow_unknown_stats) { if (stats_config.end() == stats_data && settings->allow_unknown_stats) {
Stat_config cfg{}; Stat_config cfg{};
cfg.type = GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE; cfg.type = StatInfo::STAT_TYPE_AVGRATE;
cfg.default_value_float = 0; cfg.default_value_float = 0;
stats_data = settings->setStatDefiniton(stat_name, cfg); stats_data = settings->setStatDefiniton(stat_name, cfg);
} }
if (stats_config.end() == stats_data) return result; if (stats_config.end() == stats_data) return result;
if (stats_data->second.type == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return result; if (stats_data->second.type == StatInfo::STAT_TYPE_INT) return result;
result.internal_name = stat_name; result.internal_name = stat_name;
@ -281,6 +281,26 @@ void Steam_User_Stats::steam_user_stats_network_stats(void *object, Common_Messa
inst->network_callback_stats(msg); inst->network_callback_stats(msg);
} }
SteamAPICall_t Steam_User_Stats::trigger_user_stats_received(CSteamID steam_id_user, SteamAPICall_t api_id, bool success)
{
UserStatsReceived_t data{};
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = success ? k_EResultOK : k_EResultFail; // TODO: based on doc, real steam tests needed
data.m_steamIDUser = steam_id_user;
// appid 756800 expects both: a callback (global event occurring in the Steam environment),
// and a callresult (the specific result of this function call)
// otherwise it will lock-up and hang at startup
SteamAPICall_t ret{};
if (api_id == k_uAPICallInvalid) {
ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
else {
ret = callback_results->addCallResult(api_id, data.k_iCallback, &data, sizeof(data), 0.1);
}
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
return ret;
}
// Ask the server to send down this user's data and achievements for this game // Ask the server to send down this user's data and achievements for this game
STEAM_CALL_BACK( UserStatsReceived_t ) STEAM_CALL_BACK( UserStatsReceived_t )
@ -311,13 +331,13 @@ bool Steam_User_Stats::GetStat( const char *pchName, int32 *pData )
auto stats_data = stats_config.find(stat_name); auto stats_data = stats_config.find(stat_name);
if (stats_config.end() == stats_data && settings->allow_unknown_stats) { if (stats_config.end() == stats_data && settings->allow_unknown_stats) {
Stat_config cfg{}; Stat_config cfg{};
cfg.type = GameServerStats_Messages::StatInfo::STAT_TYPE_INT; cfg.type = StatInfo::STAT_TYPE_INT;
cfg.default_value_int = 0; cfg.default_value_int = 0;
stats_data = settings->setStatDefiniton(stat_name, cfg); stats_data = settings->setStatDefiniton(stat_name, cfg);
} }
if (stats_config.end() == stats_data) return false; if (stats_config.end() == stats_data) return false;
if (stats_data->second.type != GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false; if (stats_data->second.type != StatInfo::STAT_TYPE_INT) return false;
auto cached_stat = stats_cache_int.find(stat_name); auto cached_stat = stats_cache_int.find(stat_name);
if (cached_stat != stats_cache_int.end()) { if (cached_stat != stats_cache_int.end()) {
@ -350,13 +370,13 @@ bool Steam_User_Stats::GetStat( const char *pchName, float *pData )
auto stats_data = stats_config.find(stat_name); auto stats_data = stats_config.find(stat_name);
if (stats_config.end() == stats_data && settings->allow_unknown_stats) { if (stats_config.end() == stats_data && settings->allow_unknown_stats) {
Stat_config cfg{}; Stat_config cfg{};
cfg.type = GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT; cfg.type = StatInfo::STAT_TYPE_FLOAT;
cfg.default_value_float = 0; cfg.default_value_float = 0;
stats_data = settings->setStatDefiniton(stat_name, cfg); stats_data = settings->setStatDefiniton(stat_name, cfg);
} }
if (stats_config.end() == stats_data) return false; if (stats_config.end() == stats_data) return false;
if (stats_data->second.type == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false; if (stats_data->second.type == StatInfo::STAT_TYPE_INT) return false;
auto cached_stat = stats_cache_float.find(stat_name); auto cached_stat = stats_cache_float.find(stat_name);
if (cached_stat != stats_cache_float.end()) { if (cached_stat != stats_cache_float.end()) {
@ -387,7 +407,7 @@ bool Steam_User_Stats::SetStat( const char *pchName, int32 nData )
auto ret = set_stat_internal(pchName, nData ); auto ret = set_stat_internal(pchName, nData );
if (ret.success && ret.notify_server ) { if (ret.success && ret.notify_server ) {
auto &new_stat = (*pending_server_updates.mutable_user_stats())[ret.internal_name]; auto &new_stat = (*pending_server_updates.mutable_user_stats())[ret.internal_name];
new_stat.set_stat_type(GameServerStats_Messages::StatInfo::STAT_TYPE_INT); new_stat.set_stat_type(StatInfo::STAT_TYPE_INT);
new_stat.set_value_int(ret.current_val); new_stat.set_value_int(ret.current_val);
if (settings->immediate_gameserver_stats) send_updated_stats(); if (settings->immediate_gameserver_stats) send_updated_stats();
@ -475,16 +495,17 @@ SteamAPICall_t Steam_User_Stats::RequestUserStats( CSteamID steamIDUser )
// load_achievements(); // load_achievements();
//} //}
UserStatsReceived_t data{}; if (steamIDUser == settings->get_local_steam_id()) {
data.m_nGameID = settings->get_local_game_id().ToUint64(); return trigger_user_stats_received(steamIDUser);
data.m_eResult = k_EResultOK; }
data.m_steamIDUser = steamIDUser;
// appid 756800 expects both: a callback (global event occurring in the Steam environment), Pending_User_Stats_Request pusr{};
// and a callresult (the specific result of this function call) pusr.api_id = callback_results->reserveCallResult();
// otherwise it will lock-up and hang at startup pusr.created_time = std::chrono::high_resolution_clock::now();
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1); pusr.is_sent = false;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1); pending_user_stats_requests[steamIDUser.ConvertToUint64()] = pusr;
return ret;
return pusr.api_id;
} }
@ -497,9 +518,28 @@ bool Steam_User_Stats::GetUserStat( CSteamID steamIDUser, const char *pchName, i
if (!pchName) return false; if (!pchName) return false;
if (steamIDUser == settings->get_local_steam_id()) { if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData); return GetStat(pchName, pData);
} else { }
if (pData)
*pData = 0; *pData = 0;
std::string stat_name = common_helpers::to_lower(pchName);
const auto &stats_config = settings->getStats();
auto stats_data = stats_config.find(stat_name);
if (stats_data != stats_config.end()) {
if (stats_data->second.type != StatInfo::STAT_TYPE_INT)
return false;
}
auto it_rusd = received_user_stats_data.find(steamIDUser.ConvertToUint64());
if (it_rusd != received_user_stats_data.end()) {
auto stats_map = it_rusd->second.user_stats();
auto it_user_data = stats_map.find(stat_name);
if (it_user_data != stats_map.end()) {
if (pData)
*pData = it_user_data->second.value_int();
}
} }
return true; return true;
@ -513,9 +553,28 @@ bool Steam_User_Stats::GetUserStat( CSteamID steamIDUser, const char *pchName, f
if (!pchName) return false; if (!pchName) return false;
if (steamIDUser == settings->get_local_steam_id()) { if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData); return GetStat(pchName, pData);
} else { }
*pData = 0;
if (pData)
*pData = 0.0f;
std::string stat_name = common_helpers::to_lower(pchName);
const auto &stats_config = settings->getStats();
auto stats_data = stats_config.find(stat_name);
if (stats_data != stats_config.end()) {
if (stats_data->second.type == StatInfo::STAT_TYPE_INT)
return false;
}
auto it_rusd = received_user_stats_data.find(steamIDUser.ConvertToUint64());
if (it_rusd != received_user_stats_data.end()) {
auto stats_map = it_rusd->second.user_stats();
auto it_user_data = stats_map.find(stat_name);
if (it_user_data != stats_map.end()) {
if (pData)
*pData = it_user_data->second.value_float();
}
} }
return true; return true;
@ -538,12 +597,12 @@ bool Steam_User_Stats::ResetAllStats( bool bAchievementsToo )
switch (stat.second.type) switch (stat.second.type)
{ {
case GameServerStats_Messages::StatInfo::STAT_TYPE_INT: case StatInfo::STAT_TYPE_INT:
new_stat.set_value_int(stat.second.default_value_int); new_stat.set_value_int(stat.second.default_value_int);
break; break;
case GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE: case StatInfo::STAT_TYPE_AVGRATE:
case GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT: case StatInfo::STAT_TYPE_FLOAT:
new_stat.set_value_float(stat.second.default_value_float); new_stat.set_value_float(stat.second.default_value_float);
break; break;
@ -630,7 +689,7 @@ bool Steam_User_Stats::GetGlobalStat( const char *pchStatName, int64 *pData )
const auto &stats_config = settings->getStats(); const auto &stats_config = settings->getStats();
auto stats_data = stats_config.find(stat_name); auto stats_data = stats_config.find(stat_name);
if (stats_data != stats_config.end()) { if (stats_data != stats_config.end()) {
if (stats_data->second.type != GameServerStats_Messages::StatInfo::STAT_TYPE_INT) if (stats_data->second.type != StatInfo::STAT_TYPE_INT)
return false; return false;
if (pData) if (pData)
@ -649,7 +708,7 @@ bool Steam_User_Stats::GetGlobalStat( const char *pchStatName, double *pData )
const auto &stats_config = settings->getStats(); const auto &stats_config = settings->getStats();
auto stats_data = stats_config.find(stat_name); auto stats_data = stats_config.find(stat_name);
if (stats_data != stats_config.end()) { if (stats_data != stats_config.end()) {
if (stats_data->second.type == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) if (stats_data->second.type == StatInfo::STAT_TYPE_INT)
return false; return false;
if (pData) if (pData)
@ -733,15 +792,15 @@ void Steam_User_Stats::network_stats_initial(Common_Message *msg)
this_stat.set_stat_type(stat.second.type); this_stat.set_stat_type(stat.second.type);
switch (stat.second.type) switch (stat.second.type)
{ {
case GameServerStats_Messages::StatInfo::STAT_TYPE_INT: { case StatInfo::STAT_TYPE_INT: {
int32 val = 0; int32 val = 0;
GetStat(stat.first.c_str(), &val); GetStat(stat.first.c_str(), &val);
this_stat.set_value_int(val); this_stat.set_value_int(val);
} }
break; break;
case GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE: // we set the float value also for avg case StatInfo::STAT_TYPE_AVGRATE: // we set the float value also for avg
case GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT: { case StatInfo::STAT_TYPE_FLOAT: {
float val = 0; float val = 0;
GetStat(stat.first.c_str(), &val); GetStat(stat.first.c_str(), &val);
this_stat.set_value_float(val); this_stat.set_value_float(val);
@ -804,13 +863,13 @@ void Steam_User_Stats::network_stats_updated(Common_Message *msg)
for (auto &new_stat : new_user_data.user_stats()) { for (auto &new_stat : new_user_data.user_stats()) {
switch (new_stat.second.stat_type()) switch (new_stat.second.stat_type())
{ {
case GameServerStats_Messages::StatInfo::STAT_TYPE_INT: { case StatInfo::STAT_TYPE_INT: {
set_stat_internal(new_stat.first.c_str(), new_stat.second.value_int()); set_stat_internal(new_stat.first.c_str(), new_stat.second.value_int());
} }
break; break;
case GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE: case StatInfo::STAT_TYPE_AVGRATE:
case GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT: { case StatInfo::STAT_TYPE_FLOAT: {
set_stat_internal(new_stat.first.c_str(), new_stat.second.value_float()); set_stat_internal(new_stat.first.c_str(), new_stat.second.value_float());
// non-INT values could have avg values // non-INT values could have avg values
if (new_stat.second.has_value_avg()) { if (new_stat.second.has_value_avg()) {
@ -840,6 +899,91 @@ void Steam_User_Stats::network_stats_updated(Common_Message *msg)
); );
} }
void Steam_User_Stats::send_pending_user_stats_requests()
{
if (!pending_user_stats_requests.empty()) {
for (auto it_pusr = pending_user_stats_requests.begin(); it_pusr != pending_user_stats_requests.end();) {
if (check_timedout(it_pusr->second.created_time, 3.0)) {
PRINT_DEBUG("send_pending_user_stats_requests time out");
trigger_user_stats_received(CSteamID(it_pusr->first), it_pusr->second.api_id);
it_pusr = pending_user_stats_requests.erase(it_pusr);
continue;
}
if (!it_pusr->second.is_sent) {
PRINT_DEBUG("send_pending_user_stats_requests send");
Common_Message msg;
msg.set_source_id(static_cast<uint64_t>(settings->get_local_steam_id().ConvertToUint64()));
msg.set_dest_id(static_cast<uint64_t>(it_pusr->first));
Steam_User_Stats_Messages *sus_msg = new Steam_User_Stats_Messages();
sus_msg->set_type(Steam_User_Stats_Messages::REQUEST_USERSTATS);
msg.set_allocated_steam_user_stats_messages(sus_msg);
network->sendTo(&msg, true);
it_pusr->second.is_sent = true;
}
++it_pusr;
}
}
}
void Steam_User_Stats::process_pending_user_stats_requests(Common_Message *msg)
{
switch (msg->steam_user_stats_messages().type())
{
case Steam_User_Stats_Messages::REQUEST_USERSTATS: {
PRINT_DEBUG("steam_user_stats_messages REQUEST_USERSTATS");
Steam_User_Stats_Data *susd_alloc = new Steam_User_Stats_Data();
const auto &stats_config = settings->getStats();
for (const auto &sc : stats_config) {
std::string stats_name = sc.first;
StatInfo::Stat_Type stats_type = sc.second.type;
if (stats_type == StatInfo::STAT_TYPE_INT) {
int32 stats_value = 0;
GetStat(stats_name.c_str(), &stats_value);
(*susd_alloc->mutable_user_stats())[stats_name].set_value_int(stats_value);
}
else {
float stats_value = 0.0f;
GetStat(stats_name.c_str(), &stats_value);
(*susd_alloc->mutable_user_stats())[stats_name].set_value_float(stats_value);
}
}
Common_Message msg_;
msg_.set_source_id(static_cast<uint64_t>(settings->get_local_steam_id().ConvertToUint64()));
msg_.set_dest_id(msg->source_id());
Steam_User_Stats_Messages *response_message = new Steam_User_Stats_Messages();
response_message->set_type(Steam_User_Stats_Messages::RESPONSE_USERSTATS);
response_message->set_allocated_steam_user_stats_data(susd_alloc);
msg_.set_allocated_steam_user_stats_messages(response_message);
network->sendTo(&msg_, true);
break;
}
case Steam_User_Stats_Messages::RESPONSE_USERSTATS: {
PRINT_DEBUG("steam_user_stats_messages RESPONSE_USERSTATS");
if (msg->steam_user_stats_messages().has_steam_user_stats_data()) {
PRINT_DEBUG("received user stats messages");
const Steam_User_Stats_Data &received_user_stats = msg->steam_user_stats_messages().steam_user_stats_data();
uint64 received_steam_stats_id = static_cast<uint64>(msg->source_id());
auto it_pusr = pending_user_stats_requests.find(received_steam_stats_id);
if (it_pusr == pending_user_stats_requests.end()) {
PRINT_DEBUG("received but timed out, ignoring");
}
else {
PRINT_DEBUG("received, refreshing or adding to cache");
received_user_stats_data[received_steam_stats_id] = received_user_stats;
trigger_user_stats_received(CSteamID(received_steam_stats_id), it_pusr->second.api_id);
it_pusr = pending_user_stats_requests.erase(it_pusr);
}
}
break;
}
}
}
void Steam_User_Stats::network_callback_stats(Common_Message *msg) void Steam_User_Stats::network_callback_stats(Common_Message *msg)
{ {
// network->sendToAll() sends to current user also // network->sendToAll() sends to current user also
@ -868,4 +1012,7 @@ void Steam_User_Stats::network_callback_stats(Common_Message *msg)
PRINT_DEBUG("unhandled type %i", (int)msg->gameserver_stats_messages().type()); PRINT_DEBUG("unhandled type %i", (int)msg->gameserver_stats_messages().type());
break; break;
} }
if (msg->has_steam_user_stats_messages())
process_pending_user_stats_requests(msg);
} }