share leaderboards scores with connected players, adjust players ranks locally, and sort entries as needed by the game

This commit is contained in:
otavepto 2024-03-30 08:00:05 +02:00 committed by otavepto
parent c17cb8a4f7
commit d2f23fbea5
6 changed files with 677 additions and 216 deletions

View File

@ -4,19 +4,23 @@
- 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
* share leaderboards scores with connected players, adjust players ranks locally, and sort entries as needed by the game, suggested by **[M4RCK5]**
* implemented the missing interface `ISteamGameServerStats`, allowing game servers to exchange user stats & achievements 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,
* new stub/mock 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
* separate the config file `disable_leaderboards_create_unknown.txt`, previously it was tied to `leaderboards.txt`,
by default the emu will create any unknown leaderboards, you can disable this behavior with this file
by default the emu will create any unknown leaderboards, you can disable this behavior with this file
**not recommended** to disable this behavior
* added missing example file `disable_lobby_creation.txt` in `steam_settings` folder + updated release `README`
* set the minimum game server latency/ping to 2ms
* added new function `rmCallbacks()` for the networking, to be able to cleanup callbacks on object destruction
* for windows build script: prevent permissive language extensions via the compiler flag `/permissive-`
* missing `delete` for ugc_bridge + reset pointers on client objects destruction
* allow overlay invitations to obscure game input to be able to accept/reject the request
* fixed a problem in the overlay where players connected on the same network might be missed during startup, resulting in an empty friend list
---

View File

@ -63,6 +63,7 @@ enum Callback_Ids {
CALLBACK_ID_STEAM_MESSAGES,
CALLBACK_ID_NETWORKING_MESSAGES,
CALLBACK_ID_GAMESERVER_STATS,
CALLBACK_ID_LEADERBOARDS_STATS,
CALLBACK_IDS_MAX
};

View File

@ -22,17 +22,26 @@
#include "base.h"
#include "overlay/steam_overlay.h"
struct Steam_Leaderboard_Score {
CSteamID steam_id;
int32 score;
std::vector<int32> score_details;
struct Steam_Leaderboard_Entry {
CSteamID steam_id{};
int32 score{};
std::vector<int32> score_details{};
};
struct Steam_Leaderboard {
std::string name;
ELeaderboardSortMethod sort_method;
ELeaderboardDisplayType display_type;
Steam_Leaderboard_Score self_score;
std::string name{};
ELeaderboardSortMethod sort_method = k_ELeaderboardSortMethodNone;
ELeaderboardDisplayType display_type = k_ELeaderboardDisplayTypeNone;
std::vector<Steam_Leaderboard_Entry> entries{};
Steam_Leaderboard_Entry* find_recent_entry(const CSteamID &steamid) const;
void remove_entries(const CSteamID &steamid);
// remove entries with the same steamid, keeping only most recent one
void remove_duplicate_entries();
void sort_entries();
};
struct achievement_trigger {
@ -90,7 +99,7 @@ private:
class RunEveryRunCB *run_every_runcb{};
class Steam_Overlay* overlay{};
std::vector<struct Steam_Leaderboard> leaderboards{};
std::vector<struct Steam_Leaderboard> cached_leaderboards{};
nlohmann::json defined_achievements{};
nlohmann::json user_achievements{};
@ -103,23 +112,24 @@ private:
GameServerStats_Messages::AllStats pending_server_updates{};
// returns a value 1 -> leaderboards.size(), inclusize
unsigned int find_leaderboard(std::string name);
nlohmann::detail::iter_impl<nlohmann::json> defined_achievements_find(const std::string &key);
void load_achievements_db();
void load_achievements();
void save_achievements();
void save_leaderboard_score(Steam_Leaderboard *leaderboard);
std::vector<Steam_Leaderboard_Score> load_leaderboard_scores(std::string name);
nlohmann::detail::iter_impl<nlohmann::json> defined_achievements_find(const std::string &key);
std::string get_value_for_language(nlohmann::json &json, std::string key, std::string language);
std::vector<Steam_Leaderboard_Entry> load_leaderboard_entries(const std::string &name);
void save_my_leaderboard_entry(const Steam_Leaderboard &leaderboard);
Steam_Leaderboard_Entry* update_leaderboard_entry(Steam_Leaderboard &leaderboard, const Steam_Leaderboard_Entry &entry, bool overwrite = true);
// returns a value 1 -> leaderboards.size(), inclusive
unsigned int find_cached_leaderboard(const std::string &name);
unsigned int cache_leaderboard_ifneeded(const std::string &name, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType);
// null steamid means broadcast to all
void send_my_leaderboard_score(const Steam_Leaderboard &board, const CSteamID *steamid = nullptr, bool want_scores_back = false);
void request_user_leaderboard_entry(const Steam_Leaderboard &board, const CSteamID &steamid);
// change stats/achievements without sending back to server
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 );
@ -127,16 +137,25 @@ private:
InternalSetResult<bool> set_achievement_internal( const char *pchName );
InternalSetResult<bool> clear_achievement_internal( const char *pchName );
void send_updated_stats();
void steam_run_callback();
// requests from server
void network_callback_initial_stats(Common_Message *msg);
void network_callback_updated_stats(Common_Message *msg);
void network_callback(Common_Message *msg);
void network_stats_initial(Common_Message *msg);
void network_stats_updated(Common_Message *msg);
void network_callback_stats(Common_Message *msg);
static void steam_user_stats_network_callback(void *object, Common_Message *msg);
// requests from other users to share leaderboards
void network_leaderboard_update_score(Common_Message *msg, Steam_Leaderboard &board, bool send_score_back);
void network_leaderboard_send_my_score(Common_Message *msg, const Steam_Leaderboard &board);
void network_callback_leaderboards(Common_Message *msg);
// user connect/disconnect
void network_callback_low_level(Common_Message *msg);
static void steam_user_stats_network_low_level(void *object, Common_Message *msg);
static void steam_user_stats_network_stats(void *object, Common_Message *msg);
static void steam_user_stats_network_leaderboards(void *object, Common_Message *msg);
static void steam_user_stats_run_every_runcb(void *object);
public:

View File

@ -268,6 +268,30 @@ message GameServerStats_Messages {
}
}
message Leaderboards_Messages {
message LeaderboardInfo {
string board_name = 1;
int32 sort_method = 2;
int32 display_type = 3;
}
message UserScoreEntry {
int32 score = 1;
repeated int32 score_details = 2;
}
enum Types {
UpdateUserScore = 0; // notify others on the network that our score was updated
UpdateUserScoreMutual = 1; // notify others on the network that our score was updated, and request theirs
RequestUserScore = 2; // request score data from a single user
}
Types type = 1;
LeaderboardInfo leaderboard_info = 2;
oneof data_messages {
UserScoreEntry user_score_entry = 3;
}
}
message Common_Message {
uint64 source_id = 1; // SteamID64 of the sender
uint64 dest_id = 2; // SteamID64 of the target receiver
@ -286,6 +310,7 @@ message Common_Message {
Steam_Messages steam_messages = 14;
Networking_Messages networking_messages = 15;
GameServerStats_Messages gameserver_stats_messages = 16;
Leaderboards_Messages leaderboards_messages = 17;
}
uint32 source_ip = 128;

View File

@ -590,6 +590,11 @@ void Networking::do_callbacks_message(Common_Message *msg)
run_callbacks(CALLBACK_ID_GAMESERVER_STATS, msg);
}
if (msg->has_leaderboards_messages()) {
PRINT_DEBUG("Networking has_leaderboards_messages\n");
run_callbacks(CALLBACK_ID_LEADERBOARDS_STATS, msg);
}
}
bool Networking::handle_tcp(Common_Message *msg, struct TCP_Socket &socket)

File diff suppressed because it is too large Load Diff