mirror of
https://github.com/Detanup01/gbe_fork.git
synced 2024-12-26 02:14:15 +08:00
* implemented the missing interface ISteamGameServerStats
, allowing game servers to exchange user stats with players
* add rmCallback() to networking * refactor gameserver_stats into a separate .cpp file
This commit is contained in:
parent
75bb4ff03d
commit
b6c7df40b6
@ -4,14 +4,16 @@
|
||||
- the above command introduced the ability to run without root
|
||||
- if the script was ran without root, and `-packages_skip` wasn't specified,
|
||||
the script will attempt to detect and use the built-in tool `sudo` if it was available
|
||||
* implemented the missing interface `ISteamGameServerStats`, allowing game servers to exchange user stats with players
|
||||
* for windows: updated stub drm patterns and added a workaround for older variants,
|
||||
this increases the compatibility, but makes it easier to be detected
|
||||
* new stub dll `GameOverlayRenderer` for the experiemntal steamclient setup,
|
||||
some apps verify the existence of this dll, either on disk, or inside their memory space.
|
||||
not recommended to ignore it
|
||||
* allow overlay invitations to obscure game input to be able to accept/reject the request
|
||||
**not recommended** to ignore it
|
||||
* added new function `rmCallbacks()` for the networking, to be able to cleanup callbacks on object destruction
|
||||
* added missing example file `disable_lobby_creation.txt` in `steam_settings` folder + updated release `README`
|
||||
* for windows build script: prevent permissive language extensions via the compiler flag `/permissive-`
|
||||
* allow overlay invitations to obscure game input to be able to accept/reject the request
|
||||
|
||||
---
|
||||
|
||||
|
@ -62,12 +62,13 @@ enum Callback_Ids {
|
||||
CALLBACK_ID_NETWORKING_SOCKETS,
|
||||
CALLBACK_ID_STEAM_MESSAGES,
|
||||
CALLBACK_ID_NETWORKING_MESSAGES,
|
||||
CALLBACK_ID_GAMESERVER_STATS,
|
||||
|
||||
CALLBACK_IDS_MAX
|
||||
};
|
||||
|
||||
struct Network_Callback_Container {
|
||||
std::vector<struct Network_Callback> callbacks;
|
||||
std::vector<struct Network_Callback> callbacks{};
|
||||
};
|
||||
|
||||
struct TCP_Socket {
|
||||
@ -131,12 +132,23 @@ public:
|
||||
void addListenId(CSteamID id);
|
||||
void setAppID(uint32 appid);
|
||||
void Run();
|
||||
|
||||
// send to a specific user, if 0 was passed to set_dest_id() then this will be broadcasted to all users on the network
|
||||
bool sendTo(Common_Message *msg, bool reliable, Connection *conn = NULL);
|
||||
|
||||
// send to all users whose account type is Individual, no need to call set_dest_id(), this is done automatically
|
||||
bool sendToAllIndividuals(Common_Message *msg, bool reliable);
|
||||
|
||||
// send to all active/current connections, no need to call set_dest_id(), this is done automatically
|
||||
bool sendToAll(Common_Message *msg, bool reliable);
|
||||
|
||||
// send to active/current connections with specific ip/port, no need to call set_dest_id(), this is done automatically
|
||||
//TODO: actually send to ip/port
|
||||
bool sendToIPPort(Common_Message *msg, uint32 ip, uint16 port, bool reliable);
|
||||
|
||||
bool setCallback(Callback_Ids id, CSteamID steam_id, void (*message_callback)(void *object, Common_Message *msg), void *object);
|
||||
void rmCallback(Callback_Ids id, CSteamID steam_id, void (*message_callback)(void *object, Common_Message *msg), void *object);
|
||||
|
||||
uint32 getIP(CSteamID id);
|
||||
uint32 getOwnIP();
|
||||
|
||||
|
@ -78,14 +78,8 @@ struct Leaderboard_config {
|
||||
enum ELeaderboardDisplayType display_type;
|
||||
};
|
||||
|
||||
enum Stat_Type {
|
||||
STAT_TYPE_INT,
|
||||
STAT_TYPE_FLOAT,
|
||||
STAT_TYPE_AVGRATE
|
||||
};
|
||||
|
||||
struct Stat_config {
|
||||
enum Stat_Type type;
|
||||
GameServerStats_Messages::StatInfo::Stat_Type type;
|
||||
union {
|
||||
float default_value_float;
|
||||
int32 default_value_int;
|
||||
|
@ -26,8 +26,52 @@ class Steam_GameServerStats : public ISteamGameServerStats
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
struct RequestAllStats {
|
||||
std::chrono::high_resolution_clock::time_point created{};
|
||||
SteamAPICall_t steamAPICall{};
|
||||
CSteamID steamIDUser{};
|
||||
|
||||
bool timeout = false;
|
||||
};
|
||||
|
||||
struct CachedStat {
|
||||
bool dirty = false; // true means it was changed on the server and should be sent to the user
|
||||
GameServerStats_Messages::StatInfo stat{};
|
||||
};
|
||||
struct CachedAchievement {
|
||||
bool dirty = false; // true means it was changed on the server and should be sent to the user
|
||||
GameServerStats_Messages::AchievementInfo ach{};
|
||||
};
|
||||
|
||||
struct UserData {
|
||||
std::map<std::string, CachedStat> stats{};
|
||||
std::map<std::string, CachedAchievement> achievements{};
|
||||
};
|
||||
|
||||
std::vector<RequestAllStats> pending_RequestUserStats{};
|
||||
std::map<uint64, UserData> all_users_data{};
|
||||
|
||||
CachedStat* find_stat(CSteamID steamIDUser, const std::string &key);
|
||||
CachedAchievement* find_ach(CSteamID steamIDUser, const std::string &key);
|
||||
|
||||
void remove_timedout_userstats_requests();
|
||||
void collect_and_send_updated_user_stats();
|
||||
void steam_run_callback();
|
||||
|
||||
// reponses from player
|
||||
void network_callback_initial_stats(Common_Message *msg);
|
||||
void network_callback_updated_stats(Common_Message *msg);
|
||||
void network_callback(Common_Message *msg);
|
||||
|
||||
static void steam_gameserverstats_network_callback(void *object, Common_Message *msg);
|
||||
static void steam_gameserverstats_run_every_runcb(void *object);
|
||||
|
||||
public:
|
||||
Steam_GameServerStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks);
|
||||
Steam_GameServerStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
|
||||
~Steam_GameServerStats();
|
||||
|
||||
// downloads stats for the user
|
||||
// returns a GSStatsReceived_t callback when completed
|
||||
// if the user has no stats, GSStatsReceived_t.m_eResult will be set to k_EResultFail
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -215,6 +215,59 @@ message Steam_Messages {
|
||||
}
|
||||
}
|
||||
|
||||
message GameServerStats_Messages {
|
||||
// --- baisc 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 {
|
||||
bool achieved = 1;
|
||||
}
|
||||
|
||||
// --- requests & responses objects
|
||||
// this is used when updating stats, from server or user, bi-directional
|
||||
message AllStats {
|
||||
map<string, StatInfo> user_stats = 1;
|
||||
map<string, AchievementInfo> user_achievements = 2;
|
||||
}
|
||||
// sent from server as a request, response sent by the user
|
||||
message InitialAllStats {
|
||||
uint64 steam_api_call = 1;
|
||||
|
||||
// optional because the server send doesn't send any data, just steam api call id
|
||||
optional AllStats all_data = 2;
|
||||
}
|
||||
// Request_: from Steam_GameServerStats
|
||||
// Response_: from Steam_User_Stats
|
||||
enum Types {
|
||||
Request_AllUserStats = 0;
|
||||
Response_AllUserStats = 1;
|
||||
|
||||
UpdateUserStats = 2; // sent by both sides
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
oneof data_messages {
|
||||
InitialAllStats initial_user_stats = 2;
|
||||
AllStats update_user_stats = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message Common_Message {
|
||||
uint64 source_id = 1; // SteamID64 of the sender
|
||||
uint64 dest_id = 2; // SteamID64 of the target receiver
|
||||
@ -232,6 +285,7 @@ message Common_Message {
|
||||
Networking_Sockets networking_sockets = 13;
|
||||
Steam_Messages steam_messages = 14;
|
||||
Networking_Messages networking_messages = 15;
|
||||
GameServerStats_Messages gameserver_stats_messages = 16;
|
||||
}
|
||||
|
||||
uint32 source_ip = 128;
|
||||
|
@ -584,6 +584,12 @@ void Networking::do_callbacks_message(Common_Message *msg)
|
||||
PRINT_DEBUG("Networking has_networking_messages\n");
|
||||
run_callbacks(CALLBACK_ID_NETWORKING_MESSAGES, msg);
|
||||
}
|
||||
|
||||
if (msg->has_gameserver_stats_messages()) {
|
||||
PRINT_DEBUG("Networking has_gameserver_stats\n");
|
||||
run_callbacks(CALLBACK_ID_GAMESERVER_STATS, msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Networking::handle_tcp(Common_Message *msg, struct TCP_Socket &socket)
|
||||
@ -1181,7 +1187,7 @@ bool Networking::sendToIPPort(Common_Message *msg, uint32 ip, uint16 port, bool
|
||||
{
|
||||
bool is_local_ip = ((ip >> 24) == 0x7F);
|
||||
uint32_t local_ip = getIP(ids.front());
|
||||
PRINT_DEBUG("sendToIPPort %X %u %X\n", ip, is_local_ip, local_ip);
|
||||
PRINT_DEBUG("Networking::sendToIPPort %X %u %X\n", ip, is_local_ip, local_ip);
|
||||
//TODO: actually send to ip/port
|
||||
for (auto &conn: connections) {
|
||||
if (ntohl(conn.tcp_ip_port.ip) == ip || (is_local_ip && ntohl(conn.tcp_ip_port.ip) == local_ip)) {
|
||||
@ -1215,9 +1221,9 @@ bool Networking::sendTo(Common_Message *msg, bool reliable, Connection *conn)
|
||||
bool ret = false;
|
||||
CSteamID dest_id((uint64)msg->dest_id());
|
||||
if (std::find(ids.begin(), ids.end(), dest_id) != ids.end()) {
|
||||
PRINT_DEBUG("Sending to self\n");
|
||||
PRINT_DEBUG("Networking sending to self\n");
|
||||
if (!conn) {
|
||||
PRINT_DEBUG("local send\n");
|
||||
PRINT_DEBUG("Networking local send\n");
|
||||
local_send.push_back(*msg);
|
||||
ret = true;
|
||||
}
|
||||
@ -1278,7 +1284,11 @@ bool Networking::sendToAll(Common_Message *msg, bool reliable)
|
||||
void Networking::run_callbacks(Callback_Ids id, Common_Message *msg)
|
||||
{
|
||||
for (auto &cb : callbacks[id].callbacks) {
|
||||
if (cb.steam_id.ConvertToUint64() == 0 || msg->dest_id() == 0 || cb.steam_id.ConvertToUint64() == msg->dest_id()) {
|
||||
uint64 callback_allowed_steamid = cb.steam_id.ConvertToUint64();
|
||||
uint64 message_destination_steamid = msg->dest_id();
|
||||
if (callback_allowed_steamid == 0 || // callback wants to receive all messages (callback for broadcast)
|
||||
message_destination_steamid == 0 || // message was broadcasted to all (broadcast message)
|
||||
callback_allowed_steamid == message_destination_steamid) { // callback destination is the same as the message destination
|
||||
cb.message_callback(cb.object, msg);
|
||||
}
|
||||
}
|
||||
@ -1305,7 +1315,7 @@ bool Networking::setCallback(Callback_Ids id, CSteamID steam_id, void (*message_
|
||||
{
|
||||
if (id >= CALLBACK_IDS_MAX) return false;
|
||||
|
||||
struct Network_Callback nc;
|
||||
struct Network_Callback nc{};
|
||||
nc.message_callback = message_callback;
|
||||
nc.object = object;
|
||||
nc.steam_id = steam_id;
|
||||
@ -1314,6 +1324,24 @@ bool Networking::setCallback(Callback_Ids id, CSteamID steam_id, void (*message_
|
||||
return true;
|
||||
}
|
||||
|
||||
void Networking::rmCallback(Callback_Ids id, CSteamID steam_id, void (*message_callback)(void *object, Common_Message *msg), void *object)
|
||||
{
|
||||
if (id >= CALLBACK_IDS_MAX) return;
|
||||
|
||||
auto &target_cb = callbacks[id].callbacks;
|
||||
auto itrm = std::remove_if(
|
||||
target_cb.begin(),
|
||||
target_cb.end(),
|
||||
[=, &steam_id](const struct Network_Callback &item) {
|
||||
return item.message_callback == message_callback &&
|
||||
item.object == object &&
|
||||
item.steam_id == steam_id;
|
||||
}
|
||||
);
|
||||
|
||||
target_cb.erase(itrm, target_cb.end());
|
||||
}
|
||||
|
||||
uint32 Networking::getOwnIP()
|
||||
{
|
||||
return own_ip;
|
||||
|
@ -666,13 +666,13 @@ static void parse_stats(class Settings *settings_client, Settings *settings_serv
|
||||
|
||||
try {
|
||||
if (stat_type == "float") {
|
||||
config.type = Stat_Type::STAT_TYPE_FLOAT;
|
||||
config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_FLOAT;
|
||||
config.default_value_float = std::stof(stat_default_value);
|
||||
} else if (stat_type == "int") {
|
||||
config.type = Stat_Type::STAT_TYPE_INT;
|
||||
config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_INT;
|
||||
config.default_value_int = std::stol(stat_default_value);
|
||||
} else if (stat_type == "avgrate") {
|
||||
config.type = Stat_Type::STAT_TYPE_AVGRATE;
|
||||
config.type = GameServerStats_Messages::StatInfo::STAT_TYPE_AVGRATE;
|
||||
config.default_value_float = std::stof(stat_default_value);
|
||||
} else {
|
||||
PRINT_DEBUG("Error adding stat %s, type %s isn't valid\n", stat_name.c_str(), stat_type.c_str());
|
||||
|
@ -81,7 +81,7 @@ Steam_Client::Steam_Client()
|
||||
|
||||
steam_matchmaking = new Steam_Matchmaking(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_matchmaking_servers = new Steam_Matchmaking_Servers(settings_client, network);
|
||||
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client, steam_overlay);
|
||||
steam_user_stats = new Steam_User_Stats(settings_client, network, local_storage, callback_results_client, callbacks_client, run_every_runcb, steam_overlay);
|
||||
steam_apps = new Steam_Apps(settings_client, callback_results_client);
|
||||
steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb);
|
||||
steam_remote_storage = new Steam_Remote_Storage(settings_client, ugc_bridge, local_storage, callback_results_client);
|
||||
@ -110,7 +110,7 @@ Steam_Client::Steam_Client()
|
||||
PRINT_DEBUG("client init gameserver\n");
|
||||
steam_gameserver = new Steam_GameServer(settings_server, network, callbacks_server);
|
||||
steam_gameserver_utils = new Steam_Utils(settings_server, callback_results_server, steam_overlay);
|
||||
steam_gameserverstats = new Steam_GameServerStats(settings_server, network, callback_results_server, callbacks_server);
|
||||
steam_gameserverstats = new Steam_GameServerStats(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
|
||||
steam_gameserver_networking = new Steam_Networking(settings_server, network, callbacks_server, run_every_runcb);
|
||||
steam_gameserver_http = new Steam_HTTP(settings_server, network, callback_results_server, callbacks_server);
|
||||
steam_gameserver_inventory = new Steam_Inventory(settings_server, callback_results_server, callbacks_server, run_every_runcb, local_storage);
|
||||
@ -128,7 +128,7 @@ Steam_Client::Steam_Client()
|
||||
gameserver_has_ipv6_functions = false;
|
||||
|
||||
last_cb_run = 0;
|
||||
PRINT_DEBUG("Steam_Client init end ----------\n");
|
||||
PRINT_DEBUG("Steam_Client init end *********\n");
|
||||
}
|
||||
|
||||
Steam_Client::~Steam_Client()
|
||||
@ -1914,7 +1914,7 @@ void Steam_Client::RunCallbacks(bool runClientCB, bool runGameserverCB, bool run
|
||||
callbacks_client->runCallBacks();
|
||||
|
||||
last_cb_run = std::chrono::duration_cast<std::chrono::duration<unsigned long long>>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
PRINT_DEBUG("Steam_Client::RunCallbacks done ------------------------------------------------------\n");
|
||||
PRINT_DEBUG("Steam_Client::RunCallbacks done ******************************************************\n");
|
||||
}
|
||||
|
||||
void Steam_Client::DestroyAllInterfaces()
|
||||
|
@ -56,8 +56,12 @@ bool Steam_GameServer::InitGameServer( uint32 unIP, uint16 usGamePort, uint16 us
|
||||
std::string version(pchVersionString);
|
||||
version.erase(std::remove(version.begin(), version.end(), ' '), version.end());
|
||||
version.erase(std::remove(version.begin(), version.end(), '.'), version.end());
|
||||
PRINT_DEBUG("Steam_GameServer::InitGameServer version trimmed '%s'\n", version.c_str());
|
||||
|
||||
try {
|
||||
server_data.set_version(std::stoi(version));
|
||||
auto ver = std::stoul(version);
|
||||
server_data.set_version(ver);
|
||||
PRINT_DEBUG("Steam_GameServer::InitGameServer set version to %lu\n", ver);
|
||||
} catch (...) {
|
||||
PRINT_DEBUG("Steam_GameServer::InitGameServer: not a number: %s\n", pchVersionString);
|
||||
server_data.set_version(0);
|
||||
|
@ -17,14 +17,90 @@
|
||||
|
||||
#include "dll/steam_gameserverstats.h"
|
||||
|
||||
Steam_GameServerStats::Steam_GameServerStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
|
||||
// TODO not sure what's the real value
|
||||
#define PENDING_RequestUserStats_TIMEOUT 7.0
|
||||
|
||||
|
||||
void Steam_GameServerStats::steam_gameserverstats_network_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
// PRINT_DEBUG("Steam_GameServerStats::steam_gameserverstats_network_callback\n");
|
||||
|
||||
auto steam_gameserverstats = (Steam_GameServerStats *)object;
|
||||
steam_gameserverstats->network_callback(msg);
|
||||
}
|
||||
|
||||
void Steam_GameServerStats::steam_gameserverstats_run_every_runcb(void *object)
|
||||
{
|
||||
// PRINT_DEBUG("Steam_GameServerStats::steam_gameserverstats_run_every_runcb\n");
|
||||
|
||||
auto steam_gameserverstats = (Steam_GameServerStats *)object;
|
||||
steam_gameserverstats->steam_run_callback();
|
||||
}
|
||||
|
||||
Steam_GameServerStats::CachedStat* Steam_GameServerStats::find_stat(CSteamID steamIDUser, const std::string &key)
|
||||
{
|
||||
auto it_data = all_users_data.find(steamIDUser.ConvertToUint64());
|
||||
if (all_users_data.end() == it_data) return {}; // no user
|
||||
|
||||
auto it_stat = std::find_if(
|
||||
it_data->second.stats.begin(), it_data->second.stats.end(),
|
||||
[&key](std::pair<const std::string, CachedStat> &item) {
|
||||
const std::string &name = item.first;
|
||||
return key.size() == name.size() &&
|
||||
std::equal(
|
||||
name.begin(), name.end(), key.begin(),
|
||||
[](char a, char b) { return std::tolower(a) == std::tolower(b); }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if (it_data->second.stats.end() == it_stat) return {}; // no stat
|
||||
|
||||
return &it_stat->second;
|
||||
}
|
||||
|
||||
Steam_GameServerStats::CachedAchievement* Steam_GameServerStats::find_ach(CSteamID steamIDUser, const std::string &key)
|
||||
{
|
||||
auto it_data = all_users_data.find(steamIDUser.ConvertToUint64());
|
||||
if (all_users_data.end() == it_data) return {}; // no user
|
||||
|
||||
auto it_ach = std::find_if(
|
||||
it_data->second.achievements.begin(), it_data->second.achievements.end(),
|
||||
[&key](std::pair<const std::string, CachedAchievement> &item) {
|
||||
const std::string &name = item.first;
|
||||
return key.size() == name.size() &&
|
||||
std::equal(
|
||||
name.begin(), name.end(), key.begin(),
|
||||
[](char a, char b) { return std::tolower(a) == std::tolower(b); }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if (it_data->second.achievements.end() == it_ach) return {}; // no user
|
||||
|
||||
return &it_ach->second;
|
||||
}
|
||||
|
||||
Steam_GameServerStats::Steam_GameServerStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
|
||||
this->network->setCallback(CALLBACK_ID_GAMESERVER_STATS, settings->get_local_steam_id(), &Steam_GameServerStats::steam_gameserverstats_network_callback, this);
|
||||
this->run_every_runcb->add(&Steam_GameServerStats::steam_gameserverstats_run_every_runcb, this);
|
||||
|
||||
}
|
||||
|
||||
Steam_GameServerStats::~Steam_GameServerStats()
|
||||
{
|
||||
this->network->rmCallback(CALLBACK_ID_GAMESERVER_STATS, settings->get_local_steam_id(), &Steam_GameServerStats::steam_gameserverstats_network_callback, this);
|
||||
this->run_every_runcb->remove(&Steam_GameServerStats::steam_gameserverstats_run_every_runcb, this);
|
||||
}
|
||||
|
||||
|
||||
// downloads stats for the user
|
||||
// returns a GSStatsReceived_t callback when completed
|
||||
// if the user has no stats, GSStatsReceived_t.m_eResult will be set to k_EResultFail
|
||||
@ -33,33 +109,78 @@ Steam_GameServerStats::Steam_GameServerStats(class Settings *settings, class Net
|
||||
STEAM_CALL_RESULT( GSStatsReceived_t )
|
||||
SteamAPICall_t Steam_GameServerStats::RequestUserStats( CSteamID steamIDUser )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::RequestUserStats\n");
|
||||
PRINT_DEBUG("Steam_GameServerStats::RequestUserStats %llu\n", (uint64)steamIDUser.ConvertToUint64());
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
GSStatsReceived_t data{};
|
||||
data.m_eResult = k_EResultFail;//k_EResultOK;
|
||||
data.m_steamIDUser = steamIDUser;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
struct RequestAllStats new_request{};
|
||||
new_request.created = std::chrono::high_resolution_clock::now();
|
||||
new_request.steamAPICall = callback_results->reserveCallResult();
|
||||
new_request.steamIDUser = steamIDUser;
|
||||
|
||||
pending_RequestUserStats.push_back(new_request);
|
||||
|
||||
auto initial_stats_msg = new GameServerStats_Messages::InitialAllStats();
|
||||
initial_stats_msg->set_steam_api_call(new_request.steamAPICall);
|
||||
|
||||
auto gameserverstats_messages = new GameServerStats_Messages();
|
||||
gameserverstats_messages->set_type(GameServerStats_Messages::Request_AllUserStats);
|
||||
gameserverstats_messages->set_allocated_initial_user_stats(initial_stats_msg);
|
||||
|
||||
Common_Message msg{};
|
||||
// https://protobuf.dev/reference/cpp/cpp-generated/#string
|
||||
// set_allocated_xxx() takes ownership of the allocated object, no need to delete
|
||||
msg.set_allocated_gameserver_stats_messages(gameserverstats_messages);
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_dest_id(new_request.steamIDUser.ConvertToUint64());
|
||||
network->sendTo(&msg, true);
|
||||
|
||||
return new_request.steamAPICall;
|
||||
}
|
||||
|
||||
|
||||
// requests stat information for a user, usable after a successful call to RequestUserStats()
|
||||
bool Steam_GameServerStats::GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::GetUserStat\n");
|
||||
return false;
|
||||
PRINT_DEBUG("Steam_GameServerStats::GetUserStat <int32> %llu '%s' %p\n", (uint64)steamIDUser.ConvertToUint64(), pchName, pData);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!pchName) return false;
|
||||
|
||||
auto stat = find_stat(steamIDUser, pchName);
|
||||
if (!stat) return false;
|
||||
if (stat->stat.stat_type() != GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false;
|
||||
|
||||
if (pData) *pData = stat->stat.value_int();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::GetUserStat\n");
|
||||
return false;
|
||||
PRINT_DEBUG("Steam_GameServerStats::GetUserStat <float> %llu '%s' %p\n", (uint64)steamIDUser.ConvertToUint64(), pchName, pData);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!pchName) return false;
|
||||
|
||||
auto stat = find_stat(steamIDUser, pchName);
|
||||
if (!stat) return false;
|
||||
if (stat->stat.stat_type() == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false;
|
||||
|
||||
if (pData) *pData = stat->stat.value_float();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::GetUserAchievement\n");
|
||||
return false;
|
||||
PRINT_DEBUG("Steam_GameServerStats::GetUserAchievement %llu '%s' %p\n", (uint64)steamIDUser.ConvertToUint64(), pchName, pbAchieved);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!pchName) return false;
|
||||
|
||||
auto ach = find_ach(steamIDUser, pchName);
|
||||
if (!ach) return false;
|
||||
if (pbAchieved) *pbAchieved = ach->ach.achieved();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -69,33 +190,98 @@ bool Steam_GameServerStats::GetUserAchievement( CSteamID steamIDUser, const char
|
||||
// Set the IP range of your official servers on the Steamworks page
|
||||
bool Steam_GameServerStats::SetUserStat( CSteamID steamIDUser, const char *pchName, int32 nData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::SetUserStat\n");
|
||||
return false;
|
||||
PRINT_DEBUG("Steam_GameServerStats::SetUserStat <int32> %llu '%s' %i\n", (uint64)steamIDUser.ConvertToUint64(), pchName, nData);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!pchName) return false;
|
||||
|
||||
auto stat = find_stat(steamIDUser, pchName);
|
||||
if (!stat) return false;
|
||||
if (stat->stat.stat_type() != GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false;
|
||||
if (stat->stat.value_int() == nData) return true; // don't waste time
|
||||
|
||||
stat->dirty = true;
|
||||
stat->stat.set_value_int(nData);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::SetUserStat( CSteamID steamIDUser, const char *pchName, float fData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::SetUserStat\n");
|
||||
return false;
|
||||
PRINT_DEBUG("Steam_GameServerStats::SetUserStat <float> %llu '%s' %f\n", (uint64)steamIDUser.ConvertToUint64(), pchName, fData);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!pchName) return false;
|
||||
|
||||
auto stat = find_stat(steamIDUser, pchName);
|
||||
if (!stat) return false;
|
||||
if (stat->stat.stat_type() == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false;
|
||||
if (stat->stat.value_float() == fData) return true; // don't waste time
|
||||
|
||||
stat->dirty = true;
|
||||
stat->stat.set_value_float(fData); // we set the float field in case it's float or avg
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::UpdateUserAvgRateStat( CSteamID steamIDUser, const char *pchName, float flCountThisSession, double dSessionLength )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::UpdateUserAvgRateStat\n");
|
||||
return false;
|
||||
PRINT_DEBUG("Steam_GameServerStats::UpdateUserAvgRateStat %llu '%s'\n", (uint64)steamIDUser.ConvertToUint64(), pchName);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!pchName) return false;
|
||||
|
||||
auto stat = find_stat(steamIDUser, pchName);
|
||||
if (!stat) return false;
|
||||
if (stat->stat.stat_type() == GameServerStats_Messages::StatInfo::STAT_TYPE_INT) return false;
|
||||
// don't waste time
|
||||
if (stat->stat.has_value_avg() &&
|
||||
stat->stat.value_avg().count_this_session() == flCountThisSession &&
|
||||
stat->stat.value_avg().session_length() == dSessionLength) {
|
||||
return true;
|
||||
}
|
||||
|
||||
stat->dirty = true;
|
||||
|
||||
// https://protobuf.dev/reference/cpp/cpp-generated/#string
|
||||
// set_allocated_xxx() takes ownership of the allocated object, no need to delete
|
||||
auto avg_info = new GameServerStats_Messages::StatInfo::AvgStatInfo();
|
||||
avg_info->set_count_this_session(flCountThisSession);
|
||||
avg_info->set_session_length(dSessionLength);
|
||||
stat->stat.set_allocated_value_avg(avg_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Steam_GameServerStats::SetUserAchievement( CSteamID steamIDUser, const char *pchName )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::SetUserAchievement\n");
|
||||
return false;
|
||||
PRINT_DEBUG("Steam_GameServerStats::SetUserAchievement %llu '%s'\n", (uint64)steamIDUser.ConvertToUint64(), pchName);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!pchName) return false;
|
||||
|
||||
auto ach = find_ach(steamIDUser, pchName);
|
||||
if (!ach) return false;
|
||||
if (ach->ach.achieved() == true) return true; // don't waste time
|
||||
|
||||
ach->dirty = true;
|
||||
ach->ach.set_achieved(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::ClearUserAchievement( CSteamID steamIDUser, const char *pchName )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServerStats::ClearUserAchievement\n");
|
||||
return false;
|
||||
PRINT_DEBUG("Steam_GameServerStats::ClearUserAchievement %llu '%s'\n", (uint64)steamIDUser.ConvertToUint64(), pchName);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!pchName) return false;
|
||||
|
||||
auto ach = find_ach(steamIDUser, pchName);
|
||||
if (!ach) return false;
|
||||
if (ach->ach.achieved() == false) return true; // don't waste time
|
||||
|
||||
ach->dirty = true;
|
||||
ach->ach.set_achieved(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -108,11 +294,237 @@ bool Steam_GameServerStats::ClearUserAchievement( CSteamID steamIDUser, const ch
|
||||
STEAM_CALL_RESULT( GSStatsStored_t )
|
||||
SteamAPICall_t Steam_GameServerStats::StoreUserStats( CSteamID steamIDUser )
|
||||
{
|
||||
// it's not necessary to send all data here
|
||||
PRINT_DEBUG("Steam_GameServerStats::StoreUserStats\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
GSStatsStored_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
GSStatsStored_t data{};
|
||||
|
||||
if (all_users_data.count(steamIDUser.ConvertToUint64())) {
|
||||
data.m_eResult = EResult::k_EResultOK;
|
||||
} else {
|
||||
data.m_eResult = EResult::k_EResultFail;
|
||||
}
|
||||
data.m_steamIDUser = steamIDUser;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.01);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- steam callbacks
|
||||
|
||||
void Steam_GameServerStats::remove_timedout_userstats_requests()
|
||||
{
|
||||
if (pending_RequestUserStats.empty()) return;
|
||||
|
||||
// send all pending RequestUserStats() requests
|
||||
for (auto &pendReq : pending_RequestUserStats) {
|
||||
if (check_timedout(pendReq.created, PENDING_RequestUserStats_TIMEOUT)) {
|
||||
pendReq.timeout = true;
|
||||
|
||||
GSStatsReceived_t data{};
|
||||
data.m_eResult = k_EResultTimeout;
|
||||
data.m_steamIDUser = pendReq.steamIDUser;
|
||||
callback_results->addCallResult(pendReq.steamAPICall, data.k_iCallback, &data, sizeof(data));
|
||||
|
||||
PRINT_DEBUG(
|
||||
"Steam_GameServerStats::steam_run_callback RequestUserStats timeout, %llu\n",
|
||||
pendReq.steamIDUser.ConvertToUint64()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// remove all timedout requests
|
||||
auto itrm = std::remove_if(
|
||||
pending_RequestUserStats.begin(), pending_RequestUserStats.end(),
|
||||
[](const struct RequestAllStats &item) { return item.timeout; }
|
||||
);
|
||||
pending_RequestUserStats.erase(itrm, pending_RequestUserStats.end());
|
||||
}
|
||||
|
||||
void Steam_GameServerStats::collect_and_send_updated_user_stats()
|
||||
{
|
||||
std::map<uint64, UserData> updated_users_data{}; // new data to send
|
||||
|
||||
// collect new data
|
||||
for (auto &this_user : all_users_data) { // foreach user
|
||||
uint64 user_steamid = this_user.first;
|
||||
|
||||
// collect changed stats
|
||||
for (auto &user_stat : this_user.second.stats) {
|
||||
if (user_stat.second.dirty) {
|
||||
user_stat.second.dirty = false;
|
||||
updated_users_data[user_steamid].stats[user_stat.first] = user_stat.second;
|
||||
// clear this to avoid sending it to the user next time
|
||||
if (user_stat.second.stat.has_value_avg()) user_stat.second.stat.clear_value_avg();
|
||||
}
|
||||
}
|
||||
|
||||
// collect changed achievements
|
||||
for (auto &user_ach : this_user.second.achievements) {
|
||||
if (user_ach.second.dirty) {
|
||||
user_ach.second.dirty = false;
|
||||
updated_users_data[user_steamid].achievements[user_ach.first] = user_ach.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send new user stats
|
||||
for (auto &user_new_data : updated_users_data) { // foreach user
|
||||
uint64 user_steamid = user_new_data.first;
|
||||
const auto &new_data = user_new_data.second;
|
||||
auto updated_stats_msg = new GameServerStats_Messages::AllStats();
|
||||
|
||||
// copy new stats
|
||||
auto &updated_stats_map = *updated_stats_msg->mutable_user_stats();
|
||||
for (auto &new_stat : new_data.stats) {
|
||||
updated_stats_map[new_stat.first] = new_stat.second.stat;
|
||||
|
||||
}
|
||||
|
||||
// copy new achievements
|
||||
auto &updated_achs_map = *updated_stats_msg->mutable_user_achievements();
|
||||
for (auto &new_ach : new_data.achievements) {
|
||||
updated_achs_map[new_ach.first] = new_ach.second.ach;
|
||||
|
||||
}
|
||||
|
||||
auto gameserverstats_msg = new GameServerStats_Messages();
|
||||
gameserverstats_msg->set_type(GameServerStats_Messages::UpdateUserStats);
|
||||
gameserverstats_msg->set_allocated_update_user_stats(updated_stats_msg);
|
||||
|
||||
Common_Message msg{};
|
||||
// https://protobuf.dev/reference/cpp/cpp-generated/#string
|
||||
// set_allocated_xxx() takes ownership of the allocated object, no need to delete
|
||||
msg.set_allocated_gameserver_stats_messages(gameserverstats_msg);
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_dest_id(user_steamid);
|
||||
network->sendTo(&msg, true);
|
||||
|
||||
PRINT_DEBUG(
|
||||
"Steam_GameServerStats::collect_and_send_updated_user_stats server sent updated stats %llu: %zu stats, %zu achievements\n",
|
||||
user_steamid, updated_stats_msg->user_stats().size(), updated_stats_msg->user_achievements().size()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Steam_GameServerStats::steam_run_callback()
|
||||
{
|
||||
remove_timedout_userstats_requests();
|
||||
collect_and_send_updated_user_stats();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- networking callbacks
|
||||
|
||||
void Steam_GameServerStats::network_callback_initial_stats(Common_Message *msg)
|
||||
{
|
||||
uint64 user_steamid = msg->source_id();
|
||||
|
||||
PRINT_DEBUG("Steam_GameServerStats::network_callback_initial_stats player sent all their stats %llu\n", user_steamid);
|
||||
if (!msg->gameserver_stats_messages().has_initial_user_stats() ||
|
||||
!msg->gameserver_stats_messages().initial_user_stats().has_all_data()) {
|
||||
PRINT_DEBUG("Steam_GameServerStats::network_callback_initial_stats error empty msg\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &new_data = msg->gameserver_stats_messages().initial_user_stats();
|
||||
|
||||
// find this pending request
|
||||
auto it = std::find_if(
|
||||
pending_RequestUserStats.begin(), pending_RequestUserStats.end(),
|
||||
[=](const RequestAllStats &item) {
|
||||
return item.steamAPICall == new_data.steam_api_call() &&
|
||||
item.steamIDUser == user_steamid;
|
||||
}
|
||||
);
|
||||
if (pending_RequestUserStats.end() == it) { // timeout and already removed
|
||||
PRINT_DEBUG("Steam_GameServerStats::network_callback_initial_stats error got all player stats but pending request timedout and removed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// remove this pending request
|
||||
pending_RequestUserStats.erase(it);
|
||||
|
||||
// copy new stats
|
||||
auto ¤t_stats = all_users_data[user_steamid].stats;
|
||||
current_stats.clear();
|
||||
for (const auto &new_stat : new_data.all_data().user_stats()) {
|
||||
current_stats[new_stat.first].stat = new_stat.second;
|
||||
}
|
||||
|
||||
// copy new achievements
|
||||
auto ¤t_achievements = all_users_data[user_steamid].achievements;
|
||||
current_achievements.clear();
|
||||
for (const auto &new_ach : new_data.all_data().user_achievements()) {
|
||||
current_achievements[new_ach.first].ach = new_ach.second;
|
||||
}
|
||||
|
||||
GSStatsReceived_t data{};
|
||||
data.m_eResult = EResult::k_EResultOK;
|
||||
data.m_steamIDUser = user_steamid;
|
||||
callback_results->addCallResult(it->steamAPICall, data.k_iCallback, &data, sizeof(data));
|
||||
|
||||
PRINT_DEBUG(
|
||||
"Steam_GameServerStats::network_callback_initial_stats server got all player stats %llu: %zu stats, %zu achievements\n",
|
||||
user_steamid, all_users_data[user_steamid].stats.size(), all_users_data[user_steamid].achievements.size()
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Steam_GameServerStats::network_callback_updated_stats(Common_Message *msg)
|
||||
{
|
||||
uint64 user_steamid = msg->source_id();
|
||||
|
||||
PRINT_DEBUG("Steam_GameServerStats::network_callback_updated_stats player sent updated stats %llu\n", user_steamid);
|
||||
if (!msg->gameserver_stats_messages().has_update_user_stats()) {
|
||||
PRINT_DEBUG("Steam_GameServerStats::network_callback_updated_stats error empty msg\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto ¤t_user_data = all_users_data[user_steamid];
|
||||
auto &new_user_data =msg->gameserver_stats_messages().update_user_stats();
|
||||
|
||||
// update stats
|
||||
for (auto &new_stat : new_user_data.user_stats()) {
|
||||
auto ¤t_stat = current_user_data.stats[new_stat.first];
|
||||
current_stat.dirty = false;
|
||||
current_stat.stat = new_stat.second;
|
||||
}
|
||||
|
||||
// update achievements
|
||||
for (auto &new_ach : new_user_data.user_achievements()) {
|
||||
auto ¤t_ach = current_user_data.achievements[new_ach.first];
|
||||
current_ach.dirty = false;
|
||||
current_ach.ach = new_ach.second;
|
||||
}
|
||||
|
||||
PRINT_DEBUG(
|
||||
"Steam_GameServerStats::network_callback got updated user stats %llu: %zu stats, %zu achievements\n",
|
||||
user_steamid, new_user_data.user_stats().size(), new_user_data.user_achievements().size()
|
||||
);
|
||||
}
|
||||
|
||||
// only triggered when we have a message
|
||||
void Steam_GameServerStats::network_callback(Common_Message *msg)
|
||||
{
|
||||
switch (msg->gameserver_stats_messages().type())
|
||||
{
|
||||
// user sent all their stats
|
||||
case GameServerStats_Messages::Response_AllUserStats:
|
||||
network_callback_initial_stats(msg);
|
||||
break;
|
||||
|
||||
// user has updated/new stats
|
||||
case GameServerStats_Messages::UpdateUserStats:
|
||||
network_callback_updated_stats(msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT_DEBUG("Steam_GameServerStats::network_callback unhandled type %i\n", (int)msg->gameserver_stats_messages().type());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
1527
dll/steam_user_stats.cpp
Normal file
1527
dll/steam_user_stats.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user