Added customizable notification

Notification can have a type depending on what to show.
merge-requests/28/head
Nemirtingas 2019-11-08 15:52:38 +01:00
parent 982ec56007
commit ce79df1c26
4 changed files with 1138 additions and 630 deletions

827
dll/steam_user_stats.cpp Normal file
View File

@ -0,0 +1,827 @@
/* 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 "steam_user_stats.h"
#include "dll.h"
unsigned int Steam_User_Stats::find_leaderboard(std::string name)
{
unsigned index = 1;
for (auto& leaderboard : leaderboards) {
if (leaderboard.name == name) return index;
++index;
}
return 0;
}
void Steam_User_Stats::load_achievements_db()
{
std::string file_path = Local_Storage::get_game_settings_path() + achievements_user_file;
local_storage->load_json(file_path, defined_achievements);
}
void Steam_User_Stats::load_achievements()
{
local_storage->load_json_file("", achievements_user_file, user_achievements);
}
void Steam_User_Stats::save_achievements()
{
local_storage->write_json_file("", achievements_user_file, user_achievements);
}
Steam_User_Stats::Steam_User_Stats(Settings* settings, Local_Storage* local_storage, class SteamCallResults* callback_results, class SteamCallBacks* callbacks) :
settings(settings),
local_storage(local_storage),
callback_results(callback_results),
callbacks(callbacks),
defined_achievements(nlohmann::json::object()),
user_achievements(nlohmann::json::object())
{
load_achievements_db(); // achievements db
load_achievements(); // achievements per user
}
nlohmann::json const& Steam_User_Stats::GetAchievementsDb() const
{
return defined_achievements;
}
// Ask the server to send down this user's data and achievements for this game
STEAM_CALL_BACK(UserStatsReceived_t)
bool Steam_User_Stats::RequestCurrentStats()
{
PRINT_DEBUG("Steam_User_Stats::RequestCurrentStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = settings->get_local_steam_id();
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
return true;
}
// Data accessors
bool Steam_User_Stats::GetStat(const char* pchName, int32* pData)
{
PRINT_DEBUG("GetStat int32 %s\n", pchName);
if (!pchName || !pData) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto stats_config = settings->getStats();
auto stats_data = stats_config.find(pchName);
if (stats_data != stats_config.end()) {
if (stats_data->second.type != Stat_Type::STAT_TYPE_INT) return false;
}
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char*)pData, sizeof(*pData));
if (read_data == sizeof(int32))
return true;
if (stats_data != stats_config.end()) {
*pData = stats_data->second.default_value_int;
return true;
}
return false;
}
bool Steam_User_Stats::GetStat(const char* pchName, float* pData)
{
PRINT_DEBUG("GetStat float %s\n", pchName);
if (!pchName || !pData) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto stats_config = settings->getStats();
auto stats_data = stats_config.find(pchName);
if (stats_data != stats_config.end()) {
if (stats_data->second.type == Stat_Type::STAT_TYPE_INT) return false;
}
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char*)pData, sizeof(*pData));
if (read_data == sizeof(float))
return true;
if (stats_data != stats_config.end()) {
*pData = stats_data->second.default_value_float;
return true;
}
return false;
}
// Set / update data
bool Steam_User_Stats::SetStat(const char* pchName, int32 nData)
{
PRINT_DEBUG("SetStat int32 %s\n", pchName);
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, (char*)&nData, sizeof(nData)) == sizeof(nData);
}
bool Steam_User_Stats::SetStat(const char* pchName, float fData)
{
PRINT_DEBUG("SetStat float %s\n", pchName);
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, (char*)&fData, sizeof(fData)) == sizeof(fData);
}
bool Steam_User_Stats::UpdateAvgRateStat(const char* pchName, float flCountThisSession, double dSessionLength)
{
PRINT_DEBUG("UpdateAvgRateStat %s\n", pchName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
char data[sizeof(float) + sizeof(float) + sizeof(double)];
int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char*)data, sizeof(*data));
float oldcount = 0;
double oldsessionlength = 0;
if (read_data == sizeof(data)) {
memcpy(&oldcount, data + sizeof(float), sizeof(oldcount));
memcpy(&oldsessionlength, data + sizeof(float) + sizeof(double), sizeof(oldsessionlength));
}
oldcount += flCountThisSession;
oldsessionlength += dSessionLength;
float average = oldcount / oldsessionlength;
memcpy(data, &average, sizeof(average));
memcpy(data + sizeof(float), &oldcount, sizeof(oldcount));
memcpy(data + sizeof(float) * 2, &oldsessionlength, sizeof(oldsessionlength));
return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, data, sizeof(data)) == sizeof(data);
}
// Achievement flag accessors
bool Steam_User_Stats::GetAchievement(const char* pchName, bool* pbAchieved)
{
PRINT_DEBUG("GetAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return item["name"].get<std::string>() == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end() && ach != user_achievements.end()) {
if (pbAchieved != nullptr) *pbAchieved = (*ach)["earned"];
return true;
}
}
catch (...) {}
if (pbAchieved != nullptr)*pbAchieved = false;
return false;
}
bool Steam_User_Stats::SetAchievement(const char* pchName)
{
PRINT_DEBUG("SetAchievement %s\n", pchName);
if (pchName == nullptr) return false;
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return item["name"].get<std::string>() == pchName;
});
if (it != defined_achievements.end()) {
if (user_achievements.find(pchName) == user_achievements.end() || user_achievements[pchName].value("earned", false) == false) {
user_achievements[pchName]["earned"] = true;
user_achievements[pchName]["earned_time"] = std::chrono::duration_cast<std::chrono::duration<uint32>>(std::chrono::system_clock::now().time_since_epoch()).count();
get_steam_client()->steam_overlay->AddAchievementNotification(it.value());
}
return true;
}
}
catch (...) {}
return false;
}
bool Steam_User_Stats::ClearAchievement(const char* pchName)
{
PRINT_DEBUG("ClearAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
user_achievements[pchName]["earned"] = false;
user_achievements[pchName]["earned_time"] = static_cast<uint32>(0);
return true;
}
}
catch (...) {}
return false;
}
// Get the achievement status, and the time it was unlocked if unlocked.
// If the return value is true, but the unlock time is zero, that means it was unlocked before Steam
// began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970.
bool Steam_User_Stats::GetAchievementAndUnlockTime(const char* pchName, bool* pbAchieved, uint32* punUnlockTime)
{
PRINT_DEBUG("GetAchievementAndUnlockTime\n");
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end() && ach != user_achievements.end()) {
if (pbAchieved != nullptr) *pbAchieved = (*ach)["earned"];
if (punUnlockTime != nullptr) *punUnlockTime = (*ach)["earned_time"];
return true;
}
}
catch (...) {}
if (pbAchieved != nullptr) *pbAchieved = false;
if (punUnlockTime != nullptr) *punUnlockTime = 0;
return true;
}
// Store the current data on the server, will get a callback when set
// And one callback for every new achievement
//
// If the callback has a result of k_EResultInvalidParam, one or more stats
// uploaded has been rejected, either because they broke constraints
// or were out of date. In this case the server sends back updated values.
// The stats should be re-iterated to keep in sync.
bool Steam_User_Stats::StoreStats()
{
PRINT_DEBUG("StoreStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
save_achievements();
UserStatsStored_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return true;
}
// Achievement / GroupAchievement metadata
// Gets the icon of the achievement, which is a handle to be used in ISteamUtils::GetImageRGBA(), or 0 if none set.
// A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconFetched_t callback
// which will notify you when the bits are ready. If the callback still returns zero, then there is no image set for the
// specified achievement.
int Steam_User_Stats::GetAchievementIcon(const char* pchName)
{
PRINT_DEBUG("GetAchievementIcon\n");
if (pchName == nullptr) return 0;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Get general attributes for an achievement. Accepts the following keys:
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8)
// - "hidden" for retrieving if an achievement is hidden (returns "0" when not hidden, "1" when hidden)
const char* Steam_User_Stats::GetAchievementDisplayAttribute(const char* pchName, const char* pchKey)
{
PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey);
if (pchName == nullptr) return "";
if (pchKey == nullptr) return "";
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (strcmp(pchKey, "name") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["displayName"].get_ptr<std::string*>()->c_str();
}
}
catch (...) {}
}
if (strcmp(pchKey, "desc") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["description"].get_ptr<std::string*>()->c_str();
}
}
catch (...) {}
}
if (strcmp(pchKey, "hidden") == 0) {
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["hidden"].get_ptr<std::string*>()->c_str();
}
}
catch (...) {}
}
return "";
}
// Achievement progress - triggers an AchievementProgress callback, that is all.
// Calling this w/ N out of N progress will NOT set the achievement, the game must still do that.
bool Steam_User_Stats::IndicateAchievementProgress(const char* pchName, uint32 nCurProgress, uint32 nMaxProgress)
{
PRINT_DEBUG("IndicateAchievementProgress\n");
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
try {
auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) {
return static_cast<std::string const&>(item["name"]) == pchName;
});
auto ach = user_achievements.find(pchName);
if (it != defined_achievements.end()) {
bool achieved = false;
if (ach != user_achievements.end()) {
bool achieved = ach->value("earned", false);
}
UserAchievementStored_t data = {};
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_bGroupAchievement = false;
strncpy(data.m_rgchAchievementName, pchName, k_cchStatNameMax);
if (achieved) {
data.m_nCurProgress = 0;
data.m_nMaxProgress = 0;
}
else {
user_achievements[pchName]["progress"] = nCurProgress;
user_achievements[pchName]["max_progress"] = nMaxProgress;
data.m_nCurProgress = nCurProgress;
data.m_nMaxProgress = nMaxProgress;
}
save_achievements();
callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
return true;
}
}
catch (...) {}
return false;
}
// Used for iterating achievements. In general games should not need these functions because they should have a
// list of existing achievements compiled into them
uint32 Steam_User_Stats::GetNumAchievements()
{
PRINT_DEBUG("GetNumAchievements\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return defined_achievements.size();
}
// Get achievement name iAchievement in [0,GetNumAchievements)
const char* Steam_User_Stats::GetAchievementName(uint32 iAchievement)
{
PRINT_DEBUG("GetAchievementName\n");
try {
static std::string achievement_name;
achievement_name = defined_achievements[iAchievement]["name"].get<std::string>();
return achievement_name.c_str();
}
catch (...) {}
return "";
}
// Friends stats & achievements
// downloads stats for the user
// returns a UserStatsReceived_t received when completed
// if the other user has no stats, UserStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats won't be auto-updated; you'll need to call RequestUserStats() again to refresh any data
STEAM_CALL_RESULT(UserStatsReceived_t)
SteamAPICall_t Steam_User_Stats::RequestUserStats(CSteamID steamIDUser)
{
PRINT_DEBUG("Steam_User_Stats::RequestUserStats %llu\n", steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
// Enable this to allow hot reload achievements status
//if (steamIDUser == settings->get_local_steam_id()) {
// load_achievements();
//}
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = steamIDUser;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
// requests stat information for a user, usable after a successful call to RequestUserStats()
bool Steam_User_Stats::GetUserStat(CSteamID steamIDUser, const char* pchName, int32* pData)
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
}
else {
*pData = 0;
}
return true;
}
bool Steam_User_Stats::GetUserStat(CSteamID steamIDUser, const char* pchName, float* pData)
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
}
else {
*pData = 0;
}
return true;
}
bool Steam_User_Stats::GetUserAchievement(CSteamID steamIDUser, const char* pchName, bool* pbAchieved)
{
PRINT_DEBUG("GetUserAchievement %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
return GetAchievement(pchName, pbAchieved);
}
return false;
}
// See notes for GetAchievementAndUnlockTime above
bool Steam_User_Stats::GetUserAchievementAndUnlockTime(CSteamID steamIDUser, const char* pchName, bool* pbAchieved, uint32* punUnlockTime)
{
PRINT_DEBUG("GetUserAchievementAndUnlockTime %s\n", pchName);
if (pchName == nullptr) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
return GetAchievementAndUnlockTime(pchName, pbAchieved, punUnlockTime);
}
return false;
}
// Reset stats
bool Steam_User_Stats::ResetAllStats(bool bAchievementsToo)
{
PRINT_DEBUG("ResetAllStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO
if (bAchievementsToo) {
std::for_each(user_achievements.begin(), user_achievements.end(), [](nlohmann::json& v) {
v["earned"] = false;
v["earned_time"] = 0;
});
}
return true;
}
// Leaderboard functions
// asks the Steam back-end for a leaderboard by name, and will create it if it's not yet
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
SteamAPICall_t Steam_User_Stats::FindOrCreateLeaderboard(const char* pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType)
{
PRINT_DEBUG("FindOrCreateLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
unsigned int leader = find_leaderboard(pchLeaderboardName);
if (!leader) {
struct Steam_Leaderboard leaderboard;
leaderboard.name = std::string(pchLeaderboardName);
leaderboard.sort_method = eLeaderboardSortMethod;
leaderboard.display_type = eLeaderboardDisplayType;
leaderboards.push_back(leaderboard);
leader = leaderboards.size();
}
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = leader;
data.m_bLeaderboardFound = 1;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// as above, but won't create the leaderboard if it's not found
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
SteamAPICall_t Steam_User_Stats::FindLeaderboard(const char* pchLeaderboardName)
{
PRINT_DEBUG("FindLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto settings_Leaderboards = settings->getLeaderboards();
if (settings_Leaderboards.count(pchLeaderboardName)) {
auto config = settings_Leaderboards[pchLeaderboardName];
return FindOrCreateLeaderboard(pchLeaderboardName, config.sort_method, config.display_type);
}
else if (settings->createUnknownLeaderboards()) {
return FindOrCreateLeaderboard(pchLeaderboardName, k_ELeaderboardSortMethodDescending, k_ELeaderboardDisplayTypeNumeric);
}
else {
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = find_leaderboard(pchLeaderboardName);;
data.m_bLeaderboardFound = !!data.m_hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
}
// returns the name of a leaderboard
const char* Steam_User_Stats::GetLeaderboardName(SteamLeaderboard_t hSteamLeaderboard)
{
PRINT_DEBUG("GetLeaderboardName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return "";
return leaderboards[hSteamLeaderboard - 1].name.c_str();
}
// returns the total number of entries in a leaderboard, as of the last request
int Steam_User_Stats::GetLeaderboardEntryCount(SteamLeaderboard_t hSteamLeaderboard)
{
PRINT_DEBUG("GetLeaderboardEntryCount\n");
return 0;
}
// returns the sort method of the leaderboard
ELeaderboardSortMethod Steam_User_Stats::GetLeaderboardSortMethod(SteamLeaderboard_t hSteamLeaderboard)
{
PRINT_DEBUG("GetLeaderboardSortMethod\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardSortMethodNone;
return leaderboards[hSteamLeaderboard - 1].sort_method;
}
// returns the display type of the leaderboard
ELeaderboardDisplayType Steam_User_Stats::GetLeaderboardDisplayType(SteamLeaderboard_t hSteamLeaderboard)
{
PRINT_DEBUG("GetLeaderboardDisplayType\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardDisplayTypeNone;
return leaderboards[hSteamLeaderboard - 1].display_type;
}
// Asks the Steam back-end for a set of rows in the leaderboard.
// This call is asynchronous, with the result returned in LeaderboardScoresDownloaded_t
// LeaderboardScoresDownloaded_t will contain a handle to pull the results from GetDownloadedLeaderboardEntries() (below)
// You can ask for more entries than exist, and it will return as many as do exist.
// k_ELeaderboardDataRequestGlobal requests rows in the leaderboard from the full table, with nRangeStart & nRangeEnd in the range [1, TotalEntries]
// k_ELeaderboardDataRequestGlobalAroundUser requests rows around the current user, nRangeStart being negate
// e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after
// k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user
STEAM_CALL_RESULT(LeaderboardScoresDownloaded_t)
SteamAPICall_t Steam_User_Stats::DownloadLeaderboardEntries(SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd)
{
PRINT_DEBUG("DownloadLeaderboardEntries %llu %i %i %i\n", hSteamLeaderboard, eLeaderboardDataRequest, nRangeStart, nRangeEnd);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers
// if a user doesn't have a leaderboard entry, they won't be included in the result
// a max of 100 users can be downloaded at a time, with only one outstanding call at a time
STEAM_METHOD_DESC(Downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers)
STEAM_CALL_RESULT(LeaderboardScoresDownloaded_t)
SteamAPICall_t Steam_User_Stats::DownloadLeaderboardEntriesForUsers(SteamLeaderboard_t hSteamLeaderboard,
STEAM_ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID * prgUsers, int cUsers)
{
PRINT_DEBUG("DownloadLeaderboardEntriesForUsers %i %llu\n", cUsers, cUsers > 0 ? prgUsers[0].ConvertToUint64() : 0);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Returns data about a single leaderboard entry
// use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries
// e.g.
// void OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded )
// {
// for ( int index = 0; index < pLeaderboardScoresDownloaded->m_cEntryCount; index++ )
// {
// LeaderboardEntry_t leaderboardEntry;
// int32 details[3]; // we know this is how many we've stored previously
// GetDownloadedLeaderboardEntry( pLeaderboardScoresDownloaded->m_hSteamLeaderboardEntries, index, &leaderboardEntry, details, 3 );
// assert( leaderboardEntry.m_cDetails == 3 );
// ...
// }
// once you've accessed all the entries, the data will be free'd, and the SteamLeaderboardEntries_t handle will become invalid
bool Steam_User_Stats::GetDownloadedLeaderboardEntry(SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t * pLeaderboardEntry, int32 * pDetails, int cDetailsMax)
{
PRINT_DEBUG("GetDownloadedLeaderboardEntry\n");
return false;
}
// Uploads a user score to the Steam back-end.
// This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t
// Details are extra game-defined information regarding how the user got that score
// pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list
STEAM_CALL_RESULT(LeaderboardScoreUploaded_t)
SteamAPICall_t Steam_User_Stats::UploadLeaderboardScore(SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 * pScoreDetails, int cScoreDetailsCount)
{
PRINT_DEBUG("UploadLeaderboardScore\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoreUploaded_t data;
data.m_bSuccess = 1; //needs to be success or DOA6 freezes when uploading score.
//data.m_bSuccess = 0;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_nScore = nScore;
//data.m_bScoreChanged = 1;
data.m_bScoreChanged = 0;
//data.m_nGlobalRankNew = 1;
data.m_nGlobalRankNew = 0;
data.m_nGlobalRankPrevious = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t Steam_User_Stats::UploadLeaderboardScore(SteamLeaderboard_t hSteamLeaderboard, int32 nScore, int32 * pScoreDetails, int cScoreDetailsCount)
{
PRINT_DEBUG("UploadLeaderboardScore old\n");
return UploadLeaderboardScore(hSteamLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, nScore, pScoreDetails, cScoreDetailsCount);
}
// Attaches a piece of user generated content the user's entry on a leaderboard.
// hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare().
// This call is asynchronous, with the result returned in LeaderboardUGCSet_t.
STEAM_CALL_RESULT(LeaderboardUGCSet_t)
SteamAPICall_t Steam_User_Stats::AttachLeaderboardUGC(SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC)
{
PRINT_DEBUG("AttachLeaderboardUGC\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardUGCSet_t data = {};
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) {
data.m_eResult = k_EResultFail;
}
else {
data.m_eResult = k_EResultOK;
}
data.m_hSteamLeaderboard = hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Retrieves the number of players currently playing your game (online + offline)
// This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t
STEAM_CALL_RESULT(NumberOfCurrentPlayers_t)
SteamAPICall_t Steam_User_Stats::GetNumberOfCurrentPlayers()
{
PRINT_DEBUG("GetNumberOfCurrentPlayers\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
NumberOfCurrentPlayers_t data;
data.m_bSuccess = 1;
data.m_cPlayers = 69;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Requests that Steam fetch data on the percentage of players who have received each achievement
// for the game globally.
// This call is asynchronous, with the result returned in GlobalAchievementPercentagesReady_t.
STEAM_CALL_RESULT(GlobalAchievementPercentagesReady_t)
SteamAPICall_t Steam_User_Stats::RequestGlobalAchievementPercentages()
{
PRINT_DEBUG("RequestGlobalAchievementPercentages\n");
}
// Get the info on the most achieved achievement for the game, returns an iterator index you can use to fetch
// the next most achieved afterwards. Will return -1 if there is no data on achievement
// percentages (ie, you haven't called RequestGlobalAchievementPercentages and waited on the callback).
int Steam_User_Stats::GetMostAchievedAchievementInfo(char* pchName, uint32 unNameBufLen, float* pflPercent, bool* pbAchieved)
{
PRINT_DEBUG("GetMostAchievedAchievementInfo\n");
}
// Get the info on the next most achieved achievement for the game. Call this after GetMostAchievedAchievementInfo or another
// GetNextMostAchievedAchievementInfo call passing the iterator from the previous call. Returns -1 after the last
// achievement has been iterated.
int Steam_User_Stats::GetNextMostAchievedAchievementInfo(int iIteratorPrevious, char* pchName, uint32 unNameBufLen, float* pflPercent, bool* pbAchieved)
{
PRINT_DEBUG("GetNextMostAchievedAchievementInfo\n");
}
// Returns the percentage of users who have achieved the specified achievement.
bool Steam_User_Stats::GetAchievementAchievedPercent(const char* pchName, float* pflPercent)
{
PRINT_DEBUG("GetAchievementAchievedPercent\n");
}
// Requests global stats data, which is available for stats marked as "aggregated".
// This call is asynchronous, with the results returned in GlobalStatsReceived_t.
// nHistoryDays specifies how many days of day-by-day history to retrieve in addition
// to the overall totals. The limit is 60.
STEAM_CALL_RESULT(GlobalStatsReceived_t)
SteamAPICall_t Steam_User_Stats::RequestGlobalStats(int nHistoryDays)
{
PRINT_DEBUG("RequestGlobalStats %i\n", nHistoryDays);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
GlobalStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Gets the lifetime totals for an aggregated stat
bool Steam_User_Stats::GetGlobalStat(const char* pchStatName, int64 * pData)
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
bool Steam_User_Stats::GetGlobalStat(const char* pchStatName, double* pData)
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
// Gets history for an aggregated stat. pData will be filled with daily values, starting with today.
// So when called, pData[0] will be today, pData[1] will be yesterday, and pData[2] will be two days ago,
// etc. cubData is the size in bytes of the pubData buffer. Returns the number of
// elements actually set.
int32 Steam_User_Stats::GetGlobalStatHistory(const char* pchStatName, STEAM_ARRAY_COUNT(cubData) int64 * pData, uint32 cubData)
{
PRINT_DEBUG("GetGlobalStatHistory int64 %s\n", pchStatName);
return 0;
}
int32 Steam_User_Stats::GetGlobalStatHistory(const char* pchStatName, STEAM_ARRAY_COUNT(cubData) double* pData, uint32 cubData)
{
PRINT_DEBUG("GetGlobalStatHistory double %s\n", pchStatName);
return 0;
}

View File

@ -15,636 +15,252 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_USER_STATS_H__
#define __INCLUDED_STEAM_USER_STATS_H__
#include "base.h" #include "base.h"
#include <iomanip>
#include <fstream>
#include "../json/json.hpp"
struct Steam_Leaderboard { struct Steam_Leaderboard {
std::string name; std::string name;
ELeaderboardSortMethod sort_method; ELeaderboardSortMethod sort_method;
ELeaderboardDisplayType display_type; ELeaderboardDisplayType display_type;
}; };
class Steam_User_Stats : class Steam_User_Stats :
public ISteamUserStats003, public ISteamUserStats003,
public ISteamUserStats004, public ISteamUserStats004,
public ISteamUserStats005, public ISteamUserStats005,
public ISteamUserStats006, public ISteamUserStats006,
public ISteamUserStats007, public ISteamUserStats007,
public ISteamUserStats008, public ISteamUserStats008,
public ISteamUserStats009, public ISteamUserStats009,
public ISteamUserStats010, public ISteamUserStats010,
public ISteamUserStats public ISteamUserStats
{ {
Local_Storage *local_storage; public:
Settings *settings; static constexpr auto achievements_user_file = "achievements.json";
SteamCallResults *callback_results;
class SteamCallBacks *callbacks; private:
Local_Storage* local_storage;
Settings* settings;
SteamCallResults* callback_results;
class SteamCallBacks* callbacks;
std::vector<struct Steam_Leaderboard> leaderboards; std::vector<struct Steam_Leaderboard> leaderboards;
unsigned int find_leaderboard(std::string name) nlohmann::json defined_achievements;
{ nlohmann::json user_achievements;
unsigned index = 1;
for (auto &leaderboard : leaderboards) {
if (leaderboard.name == name) return index;
++index;
}
return 0; unsigned int find_leaderboard(std::string name);
}
void load_achievements_db();
void load_achievements();
void save_achievements();
public: public:
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks) Steam_User_Stats(Settings* settings, Local_Storage* local_storage, class SteamCallResults* callback_results, class SteamCallBacks* callbacks);
{
this->local_storage = local_storage; nlohmann::json const& GetAchievementsDb() const;
this->settings = settings;
this->callback_results = callback_results; // Ask the server to send down this user's data and achievements for this game
this->callbacks = callbacks; STEAM_CALL_BACK(UserStatsReceived_t)
} bool RequestCurrentStats();
// Ask the server to send down this user's data and achievements for this game // Data accessors
STEAM_CALL_BACK( UserStatsReceived_t ) bool GetStat(const char* pchName, int32* pData);
bool RequestCurrentStats() bool GetStat(const char* pchName, float* pData);
{
PRINT_DEBUG("Steam_User_Stats::RequestCurrentStats\n"); // Set / update data
std::lock_guard<std::recursive_mutex> lock(global_mutex); bool SetStat(const char* pchName, int32 nData);
bool SetStat(const char* pchName, float fData);
UserStatsReceived_t data; bool UpdateAvgRateStat(const char* pchName, float flCountThisSession, double dSessionLength);
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK; // Achievement flag accessors
data.m_steamIDUser = settings->get_local_steam_id(); bool GetAchievement(const char* pchName, bool* pbAchieved);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1); bool SetAchievement(const char* pchName);
return true; bool ClearAchievement(const char* pchName);
}
// Get the achievement status, and the time it was unlocked if unlocked.
// Data accessors // If the return value is true, but the unlock time is zero, that means it was unlocked before Steam
bool GetStat( const char *pchName, int32 *pData ) // began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970.
{ bool GetAchievementAndUnlockTime(const char* pchName, bool* pbAchieved, uint32* punUnlockTime);
PRINT_DEBUG("GetStat int32 %s\n", pchName);
if (!pchName || !pData) return false; // Store the current data on the server, will get a callback when set
std::lock_guard<std::recursive_mutex> lock(global_mutex); // And one callback for every new achievement
auto stats_config = settings->getStats(); //
auto stats_data = stats_config.find(pchName); // If the callback has a result of k_EResultInvalidParam, one or more stats
if (stats_data != stats_config.end()) { // uploaded has been rejected, either because they broke constraints
if (stats_data->second.type != Stat_Type::STAT_TYPE_INT) return false; // or were out of date. In this case the server sends back updated values.
} // The stats should be re-iterated to keep in sync.
bool StoreStats();
int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )pData, sizeof(*pData));
if (read_data == sizeof(int32)) // Achievement / GroupAchievement metadata
return true;
// Gets the icon of the achievement, which is a handle to be used in ISteamUtils::GetImageRGBA(), or 0 if none set.
if (stats_data != stats_config.end()) { // A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconFetched_t callback
*pData = stats_data->second.default_value_int; // which will notify you when the bits are ready. If the callback still returns zero, then there is no image set for the
return true; // specified achievement.
} int GetAchievementIcon(const char* pchName);
return false;
} // Get general attributes for an achievement. Accepts the following keys:
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8)
bool GetStat( const char *pchName, float *pData ) // - "hidden" for retrieving if an achievement is hidden (returns "0" when not hidden, "1" when hidden)
{ const char* GetAchievementDisplayAttribute(const char* pchName, const char* pchKey);
PRINT_DEBUG("GetStat float %s\n", pchName);
if (!pchName || !pData) return false; // Achievement progress - triggers an AchievementProgress callback, that is all.
std::lock_guard<std::recursive_mutex> lock(global_mutex); // Calling this w/ N out of N progress will NOT set the achievement, the game must still do that.
auto stats_config = settings->getStats(); bool IndicateAchievementProgress(const char* pchName, uint32 nCurProgress, uint32 nMaxProgress);
auto stats_data = stats_config.find(pchName);
if (stats_data != stats_config.end()) { // Used for iterating achievements. In general games should not need these functions because they should have a
if (stats_data->second.type == Stat_Type::STAT_TYPE_INT) return false; // list of existing achievements compiled into them
} uint32 GetNumAchievements();
int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )pData, sizeof(*pData)); // Get achievement name iAchievement in [0,GetNumAchievements)
if (read_data == sizeof(float)) const char* GetAchievementName(uint32 iAchievement);
return true;
// Friends stats & achievements
if (stats_data != stats_config.end()) {
*pData = stats_data->second.default_value_float; // downloads stats for the user
return true; // returns a UserStatsReceived_t received when completed
} // if the other user has no stats, UserStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats won't be auto-updated; you'll need to call RequestUserStats() again to refresh any data
return false; STEAM_CALL_RESULT(UserStatsReceived_t)
} SteamAPICall_t RequestUserStats(CSteamID steamIDUser);
// Set / update data // requests stat information for a user, usable after a successful call to RequestUserStats()
bool SetStat( const char *pchName, int32 nData ) bool GetUserStat(CSteamID steamIDUser, const char* pchName, int32* pData);
{ bool GetUserStat(CSteamID steamIDUser, const char* pchName, float* pData);
PRINT_DEBUG("SetStat int32 %s\n", pchName);
if (!pchName) return false; bool GetUserAchievement(CSteamID steamIDUser, const char* pchName, bool* pbAchieved);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
// See notes for GetAchievementAndUnlockTime above
return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, (char* )&nData, sizeof(nData)) == sizeof(nData); bool GetUserAchievementAndUnlockTime(CSteamID steamIDUser, const char* pchName, bool* pbAchieved, uint32* punUnlockTime);
}
// Reset stats
bool SetStat( const char *pchName, float fData ) bool ResetAllStats(bool bAchievementsToo);
{
PRINT_DEBUG("SetStat float %s\n", pchName); // Leaderboard functions
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> lock(global_mutex); // asks the Steam back-end for a leaderboard by name, and will create it if it's not yet
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, (char* )&fData, sizeof(fData)) == sizeof(fData); STEAM_CALL_RESULT(LeaderboardFindResult_t)
} SteamAPICall_t FindOrCreateLeaderboard(const char* pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType);
bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dSessionLength ) // as above, but won't create the leaderboard if it's not found
{ // This call is asynchronous, with the result returned in LeaderboardFindResult_t
PRINT_DEBUG("UpdateAvgRateStat %s\n", pchName); STEAM_CALL_RESULT(LeaderboardFindResult_t)
std::lock_guard<std::recursive_mutex> lock(global_mutex); SteamAPICall_t FindLeaderboard(const char* pchLeaderboardName);
char data[sizeof(float) + sizeof(float) + sizeof(double)]; // returns the name of a leaderboard
int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )data, sizeof(*data)); const char* GetLeaderboardName(SteamLeaderboard_t hSteamLeaderboard);
float oldcount = 0;
double oldsessionlength = 0; // returns the total number of entries in a leaderboard, as of the last request
if (read_data == sizeof(data)) { int GetLeaderboardEntryCount(SteamLeaderboard_t hSteamLeaderboard);
memcpy(&oldcount, data + sizeof(float), sizeof(oldcount));
memcpy(&oldsessionlength, data + sizeof(float) + sizeof(double), sizeof(oldsessionlength)); // returns the sort method of the leaderboard
} ELeaderboardSortMethod GetLeaderboardSortMethod(SteamLeaderboard_t hSteamLeaderboard);
oldcount += flCountThisSession; // returns the display type of the leaderboard
oldsessionlength += dSessionLength; ELeaderboardDisplayType GetLeaderboardDisplayType(SteamLeaderboard_t hSteamLeaderboard);
float average = oldcount / oldsessionlength; // Asks the Steam back-end for a set of rows in the leaderboard.
memcpy(data, &average, sizeof(average)); // This call is asynchronous, with the result returned in LeaderboardScoresDownloaded_t
memcpy(data + sizeof(float), &oldcount, sizeof(oldcount)); // LeaderboardScoresDownloaded_t will contain a handle to pull the results from GetDownloadedLeaderboardEntries() (below)
memcpy(data + sizeof(float) * 2, &oldsessionlength, sizeof(oldsessionlength)); // You can ask for more entries than exist, and it will return as many as do exist.
// k_ELeaderboardDataRequestGlobal requests rows in the leaderboard from the full table, with nRangeStart & nRangeEnd in the range [1, TotalEntries]
return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, data, sizeof(data)) == sizeof(data); // k_ELeaderboardDataRequestGlobalAroundUser requests rows around the current user, nRangeStart being negate
} // e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after
// k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user
STEAM_CALL_RESULT(LeaderboardScoresDownloaded_t)
// Achievement flag accessors SteamAPICall_t DownloadLeaderboardEntries(SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd);
bool GetAchievement( const char *pchName, bool *pbAchieved )
{ // as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers
//TODO: these achievement functions need to load a list of achievements from somewhere, return false so that kf2 doesn't loop endlessly // if a user doesn't have a leaderboard entry, they won't be included in the result
PRINT_DEBUG("GetAchievement %s\n", pchName); // a max of 100 users can be downloaded at a time, with only one outstanding call at a time
*pbAchieved = false; STEAM_METHOD_DESC(Downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers)
return false; STEAM_CALL_RESULT(LeaderboardScoresDownloaded_t)
} SteamAPICall_t DownloadLeaderboardEntriesForUsers(SteamLeaderboard_t hSteamLeaderboard,
STEAM_ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID* prgUsers, int cUsers);
bool SetAchievement( const char *pchName )
{ // Returns data about a single leaderboard entry
PRINT_DEBUG("SetAchievement %s\n", pchName); // use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries
return false; // e.g.
} // void OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded )
// {
bool ClearAchievement( const char *pchName ) // for ( int index = 0; index < pLeaderboardScoresDownloaded->m_cEntryCount; index++ )
{ // {
PRINT_DEBUG("ClearAchievement %s\n", pchName); // LeaderboardEntry_t leaderboardEntry;
return false; // int32 details[3]; // we know this is how many we've stored previously
} // GetDownloadedLeaderboardEntry( pLeaderboardScoresDownloaded->m_hSteamLeaderboardEntries, index, &leaderboardEntry, details, 3 );
// assert( leaderboardEntry.m_cDetails == 3 );
// ...
// Get the achievement status, and the time it was unlocked if unlocked. // }
// If the return value is true, but the unlock time is zero, that means it was unlocked before Steam // once you've accessed all the entries, the data will be free'd, and the SteamLeaderboardEntries_t handle will become invalid
// began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970. bool GetDownloadedLeaderboardEntry(SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t* pLeaderboardEntry, int32* pDetails, int cDetailsMax);
bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
{ // Uploads a user score to the Steam back-end.
PRINT_DEBUG("GetAchievementAndUnlockTime\n"); // This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t
*pbAchieved = false; // Details are extra game-defined information regarding how the user got that score
return true; // pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list
} STEAM_CALL_RESULT(LeaderboardScoreUploaded_t)
SteamAPICall_t UploadLeaderboardScore(SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32* pScoreDetails, int cScoreDetailsCount);
// Store the current data on the server, will get a callback when set SteamAPICall_t UploadLeaderboardScore(SteamLeaderboard_t hSteamLeaderboard, int32 nScore, int32* pScoreDetails, int cScoreDetailsCount);
// And one callback for every new achievement
// // Attaches a piece of user generated content the user's entry on a leaderboard.
// If the callback has a result of k_EResultInvalidParam, one or more stats // hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare().
// uploaded has been rejected, either because they broke constraints // This call is asynchronous, with the result returned in LeaderboardUGCSet_t.
// or were out of date. In this case the server sends back updated values. STEAM_CALL_RESULT(LeaderboardUGCSet_t)
// The stats should be re-iterated to keep in sync. SteamAPICall_t AttachLeaderboardUGC(SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC);
bool StoreStats()
{ // Retrieves the number of players currently playing your game (online + offline)
PRINT_DEBUG("StoreStats\n"); // This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t
std::lock_guard<std::recursive_mutex> lock(global_mutex); STEAM_CALL_RESULT(NumberOfCurrentPlayers_t)
SteamAPICall_t GetNumberOfCurrentPlayers();
UserStatsStored_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64(); // Requests that Steam fetch data on the percentage of players who have received each achievement
data.m_eResult = k_EResultOK; // for the game globally.
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); // This call is asynchronous, with the result returned in GlobalAchievementPercentagesReady_t.
return true; STEAM_CALL_RESULT(GlobalAchievementPercentagesReady_t)
} SteamAPICall_t RequestGlobalAchievementPercentages();
// Get the info on the most achieved achievement for the game, returns an iterator index you can use to fetch
// Achievement / GroupAchievement metadata // the next most achieved afterwards. Will return -1 if there is no data on achievement
// percentages (ie, you haven't called RequestGlobalAchievementPercentages and waited on the callback).
// Gets the icon of the achievement, which is a handle to be used in ISteamUtils::GetImageRGBA(), or 0 if none set. int GetMostAchievedAchievementInfo(char* pchName, uint32 unNameBufLen, float* pflPercent, bool* pbAchieved);
// A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconFetched_t callback
// which will notify you when the bits are ready. If the callback still returns zero, then there is no image set for the // Get the info on the next most achieved achievement for the game. Call this after GetMostAchievedAchievementInfo or another
// specified achievement. // GetNextMostAchievedAchievementInfo call passing the iterator from the previous call. Returns -1 after the last
int GetAchievementIcon( const char *pchName ) // achievement has been iterated.
{ int GetNextMostAchievedAchievementInfo(int iIteratorPrevious, char* pchName, uint32 unNameBufLen, float* pflPercent, bool* pbAchieved);
PRINT_DEBUG("GetAchievementIcon\n");
return 0; // Returns the percentage of users who have achieved the specified achievement.
} bool GetAchievementAchievedPercent(const char* pchName, float* pflPercent);
// Requests global stats data, which is available for stats marked as "aggregated".
// Get general attributes for an achievement. Accepts the following keys: // This call is asynchronous, with the results returned in GlobalStatsReceived_t.
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8) // nHistoryDays specifies how many days of day-by-day history to retrieve in addition
// - "hidden" for retrieving if an achievement is hidden (returns "0" when not hidden, "1" when hidden) // to the overall totals. The limit is 60.
const char * GetAchievementDisplayAttribute( const char *pchName, const char *pchKey ) STEAM_CALL_RESULT(GlobalStatsReceived_t)
{ SteamAPICall_t RequestGlobalStats(int nHistoryDays);
PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey);
return ""; //TODO // Gets the lifetime totals for an aggregated stat
bool GetGlobalStat(const char* pchStatName, int64* pData);
if (strcmp (pchKey, "name") == 0) { bool GetGlobalStat(const char* pchStatName, double* pData);
return "Achievement Name";
} // Gets history for an aggregated stat. pData will be filled with daily values, starting with today.
// So when called, pData[0] will be today, pData[1] will be yesterday, and pData[2] will be two days ago,
if (strcmp (pchKey, "desc") == 0) { // etc. cubData is the size in bytes of the pubData buffer. Returns the number of
return "Achievement Description"; // elements actually set.
} int32 GetGlobalStatHistory(const char* pchStatName, STEAM_ARRAY_COUNT(cubData) int64* pData, uint32 cubData);
int32 GetGlobalStatHistory(const char* pchStatName, STEAM_ARRAY_COUNT(cubData) double* pData, uint32 cubData);
if (strcmp (pchKey, "hidden") == 0) {
return "0";
}
return "";
}
// Achievement progress - triggers an AchievementProgress callback, that is all.
// Calling this w/ N out of N progress will NOT set the achievement, the game must still do that.
bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint32 nMaxProgress )
{
PRINT_DEBUG("IndicateAchievementProgress\n");
}
// Used for iterating achievements. In general games should not need these functions because they should have a
// list of existing achievements compiled into them
uint32 GetNumAchievements()
{
PRINT_DEBUG("GetNumAchievements\n");
return 0;
}
// Get achievement name iAchievement in [0,GetNumAchievements)
const char * GetAchievementName( uint32 iAchievement )
{
PRINT_DEBUG("GetAchievementName\n");
return "";
}
// Friends stats & achievements
// downloads stats for the user
// returns a UserStatsReceived_t received when completed
// if the other user has no stats, UserStatsReceived_t.m_eResult will be set to k_EResultFail
// these stats won't be auto-updated; you'll need to call RequestUserStats() again to refresh any data
STEAM_CALL_RESULT( UserStatsReceived_t )
SteamAPICall_t RequestUserStats( CSteamID steamIDUser )
{
PRINT_DEBUG("Steam_User_Stats::RequestUserStats %llu\n", steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
UserStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
data.m_steamIDUser = steamIDUser;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
// requests stat information for a user, usable after a successful call to RequestUserStats()
bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData )
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
} else {
*pData = 0;
}
return true;
}
bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData )
{
PRINT_DEBUG("GetUserStat %s %llu\n", pchName, steamIDUser.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
GetStat(pchName, pData);
} else {
*pData = 0;
}
return true;
}
bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved )
{
PRINT_DEBUG("GetUserAchievement %s\n", pchName);
return false;
}
// See notes for GetAchievementAndUnlockTime above
bool GetUserAchievementAndUnlockTime( CSteamID steamIDUser, const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
{
PRINT_DEBUG("GetUserAchievementAndUnlockTime %s\n", pchName);
return false;
}
// Reset stats
bool ResetAllStats( bool bAchievementsToo )
{
PRINT_DEBUG("ResetAllStats\n");
//TODO
return true;
}
// Leaderboard functions
// asks the Steam back-end for a leaderboard by name, and will create it if it's not yet
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT(LeaderboardFindResult_t)
SteamAPICall_t FindOrCreateLeaderboard( const char *pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType )
{
PRINT_DEBUG("FindOrCreateLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
unsigned int leader = find_leaderboard(pchLeaderboardName);
if (!leader) {
struct Steam_Leaderboard leaderboard;
leaderboard.name = std::string(pchLeaderboardName);
leaderboard.sort_method = eLeaderboardSortMethod;
leaderboard.display_type = eLeaderboardDisplayType;
leaderboards.push_back(leaderboard);
leader = leaderboards.size();
}
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = leader;
data.m_bLeaderboardFound = 1;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// as above, but won't create the leaderboard if it's not found
// This call is asynchronous, with the result returned in LeaderboardFindResult_t
STEAM_CALL_RESULT( LeaderboardFindResult_t )
SteamAPICall_t FindLeaderboard( const char *pchLeaderboardName )
{
PRINT_DEBUG("FindLeaderboard %s\n", pchLeaderboardName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto settings_Leaderboards = settings->getLeaderboards();
if (settings_Leaderboards.count(pchLeaderboardName)) {
auto config = settings_Leaderboards[pchLeaderboardName];
return FindOrCreateLeaderboard(pchLeaderboardName, config.sort_method, config.display_type);
} else if (settings->createUnknownLeaderboards()) {
return FindOrCreateLeaderboard(pchLeaderboardName, k_ELeaderboardSortMethodDescending, k_ELeaderboardDisplayTypeNumeric);
} else {
LeaderboardFindResult_t data;
data.m_hSteamLeaderboard = find_leaderboard(pchLeaderboardName);;
data.m_bLeaderboardFound = !!data.m_hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
}
// returns the name of a leaderboard
const char * GetLeaderboardName( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardName\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return "";
return leaderboards[hSteamLeaderboard - 1].name.c_str();
}
// returns the total number of entries in a leaderboard, as of the last request
int GetLeaderboardEntryCount( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardEntryCount\n");
return 0;
}
// returns the sort method of the leaderboard
ELeaderboardSortMethod GetLeaderboardSortMethod( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardSortMethod\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardSortMethodNone;
return leaderboards[hSteamLeaderboard - 1].sort_method;
}
// returns the display type of the leaderboard
ELeaderboardDisplayType GetLeaderboardDisplayType( SteamLeaderboard_t hSteamLeaderboard )
{
PRINT_DEBUG("GetLeaderboardDisplayType\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_ELeaderboardDisplayTypeNone;
return leaderboards[hSteamLeaderboard - 1].display_type;
}
// Asks the Steam back-end for a set of rows in the leaderboard.
// This call is asynchronous, with the result returned in LeaderboardScoresDownloaded_t
// LeaderboardScoresDownloaded_t will contain a handle to pull the results from GetDownloadedLeaderboardEntries() (below)
// You can ask for more entries than exist, and it will return as many as do exist.
// k_ELeaderboardDataRequestGlobal requests rows in the leaderboard from the full table, with nRangeStart & nRangeEnd in the range [1, TotalEntries]
// k_ELeaderboardDataRequestGlobalAroundUser requests rows around the current user, nRangeStart being negate
// e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after
// k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user
STEAM_CALL_RESULT( LeaderboardScoresDownloaded_t )
SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd )
{
PRINT_DEBUG("DownloadLeaderboardEntries %llu %i %i %i\n", hSteamLeaderboard, eLeaderboardDataRequest, nRangeStart, nRangeEnd);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers
// if a user doesn't have a leaderboard entry, they won't be included in the result
// a max of 100 users can be downloaded at a time, with only one outstanding call at a time
STEAM_METHOD_DESC(Downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers)
STEAM_CALL_RESULT( LeaderboardScoresDownloaded_t )
SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLeaderboard,
STEAM_ARRAY_COUNT_D(cUsers, Array of users to retrieve) CSteamID *prgUsers, int cUsers )
{
PRINT_DEBUG("DownloadLeaderboardEntriesForUsers %i %llu\n", cUsers, cUsers > 0 ? prgUsers[0].ConvertToUint64() : 0);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoresDownloaded_t data;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_hSteamLeaderboardEntries = 123;
data.m_cEntryCount = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Returns data about a single leaderboard entry
// use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries
// e.g.
// void OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded )
// {
// for ( int index = 0; index < pLeaderboardScoresDownloaded->m_cEntryCount; index++ )
// {
// LeaderboardEntry_t leaderboardEntry;
// int32 details[3]; // we know this is how many we've stored previously
// GetDownloadedLeaderboardEntry( pLeaderboardScoresDownloaded->m_hSteamLeaderboardEntries, index, &leaderboardEntry, details, 3 );
// assert( leaderboardEntry.m_cDetails == 3 );
// ...
// }
// once you've accessed all the entries, the data will be free'd, and the SteamLeaderboardEntries_t handle will become invalid
bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t *pLeaderboardEntry, int32 *pDetails, int cDetailsMax )
{
PRINT_DEBUG("GetDownloadedLeaderboardEntry\n");
return false;
}
// Uploads a user score to the Steam back-end.
// This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t
// Details are extra game-defined information regarding how the user got that score
// pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list
STEAM_CALL_RESULT( LeaderboardScoreUploaded_t )
SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount )
{
PRINT_DEBUG("UploadLeaderboardScore\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardScoreUploaded_t data;
data.m_bSuccess = 1; //needs to be success or DOA6 freezes when uploading score.
//data.m_bSuccess = 0;
data.m_hSteamLeaderboard = hSteamLeaderboard;
data.m_nScore = nScore;
//data.m_bScoreChanged = 1;
data.m_bScoreChanged = 0;
//data.m_nGlobalRankNew = 1;
data.m_nGlobalRankNew = 0;
data.m_nGlobalRankPrevious = 0;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, int32 nScore, int32 *pScoreDetails, int cScoreDetailsCount )
{
PRINT_DEBUG("UploadLeaderboardScore old\n");
return UploadLeaderboardScore(hSteamLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, nScore, pScoreDetails, cScoreDetailsCount);
}
// Attaches a piece of user generated content the user's entry on a leaderboard.
// hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare().
// This call is asynchronous, with the result returned in LeaderboardUGCSet_t.
STEAM_CALL_RESULT( LeaderboardUGCSet_t )
SteamAPICall_t AttachLeaderboardUGC( SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC )
{
PRINT_DEBUG("AttachLeaderboardUGC\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
LeaderboardUGCSet_t data = {};
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) {
data.m_eResult = k_EResultFail;
} else {
data.m_eResult = k_EResultOK;
}
data.m_hSteamLeaderboard = hSteamLeaderboard;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Retrieves the number of players currently playing your game (online + offline)
// This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t
STEAM_CALL_RESULT( NumberOfCurrentPlayers_t )
SteamAPICall_t GetNumberOfCurrentPlayers()
{
PRINT_DEBUG("GetNumberOfCurrentPlayers\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
NumberOfCurrentPlayers_t data;
data.m_bSuccess = 1;
data.m_cPlayers = 69;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Requests that Steam fetch data on the percentage of players who have received each achievement
// for the game globally.
// This call is asynchronous, with the result returned in GlobalAchievementPercentagesReady_t.
STEAM_CALL_RESULT( GlobalAchievementPercentagesReady_t )
SteamAPICall_t RequestGlobalAchievementPercentages()
{
PRINT_DEBUG("RequestGlobalAchievementPercentages\n");
}
// Get the info on the most achieved achievement for the game, returns an iterator index you can use to fetch
// the next most achieved afterwards. Will return -1 if there is no data on achievement
// percentages (ie, you haven't called RequestGlobalAchievementPercentages and waited on the callback).
int GetMostAchievedAchievementInfo( char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved )
{
PRINT_DEBUG("GetMostAchievedAchievementInfo\n");
}
// Get the info on the next most achieved achievement for the game. Call this after GetMostAchievedAchievementInfo or another
// GetNextMostAchievedAchievementInfo call passing the iterator from the previous call. Returns -1 after the last
// achievement has been iterated.
int GetNextMostAchievedAchievementInfo( int iIteratorPrevious, char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved )
{
PRINT_DEBUG("GetNextMostAchievedAchievementInfo\n");
}
// Returns the percentage of users who have achieved the specified achievement.
bool GetAchievementAchievedPercent( const char *pchName, float *pflPercent )
{
PRINT_DEBUG("GetAchievementAchievedPercent\n");
}
// Requests global stats data, which is available for stats marked as "aggregated".
// This call is asynchronous, with the results returned in GlobalStatsReceived_t.
// nHistoryDays specifies how many days of day-by-day history to retrieve in addition
// to the overall totals. The limit is 60.
STEAM_CALL_RESULT( GlobalStatsReceived_t )
SteamAPICall_t RequestGlobalStats( int nHistoryDays )
{
PRINT_DEBUG("RequestGlobalStats %i\n", nHistoryDays);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
GlobalStatsReceived_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// Gets the lifetime totals for an aggregated stat
bool GetGlobalStat( const char *pchStatName, int64 *pData )
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
bool GetGlobalStat( const char *pchStatName, double *pData )
{
PRINT_DEBUG("GetGlobalStat %s\n", pchStatName);
return false;
}
// Gets history for an aggregated stat. pData will be filled with daily values, starting with today.
// So when called, pData[0] will be today, pData[1] will be yesterday, and pData[2] will be two days ago,
// etc. cubData is the size in bytes of the pubData buffer. Returns the number of
// elements actually set.
int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData) int64 *pData, uint32 cubData )
{
PRINT_DEBUG("GetGlobalStatHistory int64 %s\n", pchStatName);
return 0;
}
int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData) double *pData, uint32 cubData )
{
PRINT_DEBUG("GetGlobalStatHistory double %s\n", pchStatName);
return 0;
}
}; };
#endif//__INCLUDED_STEAM_USER_STATS_H__

View File

@ -213,7 +213,7 @@ void Steam_Overlay::ShowOverlay(bool state)
overlay_state_changed = true; overlay_state_changed = true;
} }
void Steam_Overlay::NotifyUser(friend_window_state& friend_state, std::string const& message) void Steam_Overlay::NotifyUser(friend_window_state& friend_state)
{ {
if (!(friend_state.window_state & window_state_show) || !show_overlay) if (!(friend_state.window_state & window_state_show) || !show_overlay)
{ {
@ -221,7 +221,6 @@ void Steam_Overlay::NotifyUser(friend_window_state& friend_state, std::string co
#ifdef __WINDOWS__ #ifdef __WINDOWS__
PlaySound((LPCSTR)notif_invite_wav, NULL, SND_ASYNC | SND_MEMORY); PlaySound((LPCSTR)notif_invite_wav, NULL, SND_ASYNC | SND_MEMORY);
#endif #endif
AddNotification(message);
} }
} }
@ -239,7 +238,8 @@ void Steam_Overlay::SetLobbyInvite(Friend friendId, uint64 lobbyId)
frd.window_state |= window_state_lobby_invite; frd.window_state |= window_state_lobby_invite;
// Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows) // Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows)
frd.window_state &= ~window_state_rich_invite; frd.window_state &= ~window_state_rich_invite;
NotifyUser(i->second, i->first.name() + " invited you to join a game"); AddInviteNotification(*i);
NotifyUser(i->second);
} }
} }
@ -257,7 +257,8 @@ void Steam_Overlay::SetRichInvite(Friend friendId, const char* connect_str)
frd.window_state |= window_state_rich_invite; frd.window_state |= window_state_rich_invite;
// Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows) // Make sure don't have rich presence invite and a lobby invite (it should not happen but who knows)
frd.window_state &= ~window_state_lobby_invite; frd.window_state &= ~window_state_lobby_invite;
NotifyUser(i->second, i->first.name() + " invited you to join a game"); AddInviteNotification(*i);
NotifyUser(i->second);
} }
} }
@ -285,13 +286,14 @@ void Steam_Overlay::FriendDisconnect(Friend _friend)
friends.erase(it); friends.erase(it);
} }
void Steam_Overlay::AddNotification(std::string const& message) void Steam_Overlay::AddMessageNotification(std::string const& message)
{ {
int id = find_free_notification_id(notifications); int id = find_free_notification_id(notifications);
if (id != 0) if (id != 0)
{ {
Notification notif; Notification notif;
notif.id = id; notif.id = id;
notif.type = notification_type_message;
notif.message = message; notif.message = message;
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()); notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif); notifications.emplace_back(notif);
@ -300,6 +302,40 @@ void Steam_Overlay::AddNotification(std::string const& message)
PRINT_DEBUG("No more free id to create a notification window\n"); PRINT_DEBUG("No more free id to create a notification window\n");
} }
void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach)
{
int id = find_free_notification_id(notifications);
if (id != 0)
{
Notification notif;
notif.id = id;
notif.type = notification_type_achievement;
// Load achievement image
notif.message = ach["displayName"].get<std::string>() + "\n" + ach["description"].get<std::string>();
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
}
else
PRINT_DEBUG("No more free id to create a notification window\n");
}
void Steam_Overlay::AddInviteNotification(std::pair<const Friend, friend_window_state>& wnd_state)
{
int id = find_free_notification_id(notifications);
if (id != 0)
{
Notification notif;
notif.id = id;
notif.type = notification_type_invite;
notif.frd = &wnd_state;
notif.message = wnd_state.first.name() + " invited you to join a game";
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
notifications.emplace_back(notif);
}
else
PRINT_DEBUG("No more free id to create a notification window\n");
}
bool Steam_Overlay::FriendHasLobby(uint64 friend_id) bool Steam_Overlay::FriendHasLobby(uint64 friend_id)
{ {
Steam_Friends* steamFriends = get_steam_client()->steam_friends; Steam_Friends* steamFriends = get_steam_client()->steam_friends;
@ -341,13 +377,13 @@ void Steam_Overlay::BuildContextMenu(Friend const& frd, friend_window_state& sta
// If we have the same appid, activate the invite/join buttons // If we have the same appid, activate the invite/join buttons
if (settings->get_local_game_id().AppID() == frd.appid()) if (settings->get_local_game_id().AppID() == frd.appid())
{ {
if (IHaveLobby() && ImGui::Button("Invite")) if (IHaveLobby() && ImGui::Button("Invite###PopupInvite"))
{ {
state.window_state |= window_state_invite; state.window_state |= window_state_invite;
has_friend_action.push(frd); has_friend_action.push(frd);
close_popup = true; close_popup = true;
} }
if (FriendHasLobby(frd.id()) && ImGui::Button("Join")) if (FriendHasLobby(frd.id()) && ImGui::Button("Join###PopupJoin"))
{ {
state.window_state |= window_state_join; state.window_state |= window_state_join;
has_friend_action.push(frd); has_friend_action.push(frd);
@ -499,9 +535,26 @@ void Steam_Overlay::BuildNotifications(int width, int height)
ImGui::SetNextWindowPos(ImVec2((float)width - width * Notification::width, Notification::height * font_size * i )); ImGui::SetNextWindowPos(ImVec2((float)width - width * Notification::width, Notification::height * font_size * i ));
ImGui::SetNextWindowSize(ImVec2( width * Notification::width, Notification::height * font_size )); ImGui::SetNextWindowSize(ImVec2( width * Notification::width, Notification::height * font_size ));
ImGui::Begin(std::to_string(it->id).c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGui::Begin(std::to_string(it->id).c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMouseInputs); ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoDecoration);
switch (it->type)
{
case notification_type_achievement:
ImGui::TextWrapped("%s", it->message.c_str()); ImGui::TextWrapped("%s", it->message.c_str());
break;
case notification_type_invite:
{
ImGui::TextWrapped("%s", it->message.c_str());
if (ImGui::Button("Join"))
{
has_friend_action.push(it->frd->first);
it->start_time = std::chrono::seconds(0);
}
}
break;
case notification_type_message:
ImGui::TextWrapped("%s", it->message.c_str()); break;
}
ImGui::End(); ImGui::End();
@ -619,7 +672,8 @@ void Steam_Overlay::Callback(Common_Message *msg)
friend_info->second.window_state |= window_state_need_attention; friend_info->second.window_state |= window_state_need_attention;
} }
NotifyUser(friend_info->second, friend_info->first.name() + " says: " + steam_message.message()); AddMessageNotification(friend_info->first.name() + " says: " + steam_message.message());
NotifyUser(friend_info->second);
} }
} }
} }

View File

@ -41,10 +41,17 @@ struct Friend_Less
} }
}; };
enum notification_type
{
notification_type_message = 0,
notification_type_invite,
notification_type_achievement,
};
struct Notification struct Notification
{ {
static constexpr float width = 0.25; static constexpr float width = 0.25;
static constexpr float height = 4.0; static constexpr float height = 5.0;
static constexpr std::chrono::milliseconds fade_in = std::chrono::milliseconds(2000); static constexpr std::chrono::milliseconds fade_in = std::chrono::milliseconds(2000);
static constexpr std::chrono::milliseconds fade_out = std::chrono::milliseconds(2000); static constexpr std::chrono::milliseconds fade_out = std::chrono::milliseconds(2000);
static constexpr std::chrono::milliseconds show_time = std::chrono::milliseconds(6000) + fade_in + fade_out; static constexpr std::chrono::milliseconds show_time = std::chrono::milliseconds(6000) + fade_in + fade_out;
@ -55,8 +62,10 @@ struct Notification
static constexpr float max_alpha = 0.5f; static constexpr float max_alpha = 0.5f;
int id; int id;
uint8 type;
std::chrono::seconds start_time; std::chrono::seconds start_time;
std::string message; std::string message;
std::pair<const Friend, friend_window_state>* frd;
}; };
#ifndef NO_OVERLAY #ifndef NO_OVERLAY
@ -97,7 +106,7 @@ class Steam_Overlay
bool FriendHasLobby(uint64 friend_id); bool FriendHasLobby(uint64 friend_id);
bool IHaveLobby(); bool IHaveLobby();
void NotifyUser(friend_window_state& friend_state, std::string const& message); void NotifyUser(friend_window_state& friend_state);
// Right click on friend // Right click on friend
void BuildContextMenu(Friend const& frd, friend_window_state &state); void BuildContextMenu(Friend const& frd, friend_window_state &state);
@ -136,7 +145,9 @@ public:
void FriendConnect(Friend _friend); void FriendConnect(Friend _friend);
void FriendDisconnect(Friend _friend); void FriendDisconnect(Friend _friend);
void AddNotification(std::string const& message); void AddMessageNotification(std::string const& message);
void AddAchievementNotification(nlohmann::json const& ach);
void AddInviteNotification(std::pair<const Friend, friend_window_state> &wnd_state);
}; };
#else #else