diff --git a/dll/local_storage.h b/dll/local_storage.h index 11f520a..ddb1c33 100644 --- a/dll/local_storage.h +++ b/dll/local_storage.h @@ -47,6 +47,7 @@ public: static constexpr auto settings_storage_folder = "settings"; static constexpr auto remote_storage_folder = "remote"; static constexpr auto stats_storage_folder = "stats"; + static constexpr auto leaderboard_storage_folder = "leaderboard"; static constexpr auto user_data_storage = "local"; static constexpr auto screenshots_folder = "screenshots"; static constexpr auto game_settings_folder = "steam_settings"; diff --git a/dll/steam_user_stats.h b/dll/steam_user_stats.h index 995bd84..b7638dd 100644 --- a/dll/steam_user_stats.h +++ b/dll/steam_user_stats.h @@ -21,10 +21,17 @@ #include "base.h" #include "../overlay_experimental/steam_overlay.h" +struct Steam_Leaderboard_Score { + CSteamID steam_id; + int32 score = 0; + std::vector score_details; +}; + struct Steam_Leaderboard { std::string name; ELeaderboardSortMethod sort_method; ELeaderboardDisplayType display_type; + Steam_Leaderboard_Score self_score; }; struct achievement_trigger { @@ -121,6 +128,60 @@ void save_achievements() local_storage->write_json_file("", achievements_user_file, user_achievements); } +void save_leaderboard_score(Steam_Leaderboard *leaderboard) +{ + std::vector output; + uint64_t steam_id = leaderboard->self_score.steam_id.ConvertToUint64(); + output.push_back(steam_id & 0xFFFFFFFF); + output.push_back(steam_id >> 32); + + output.push_back(leaderboard->self_score.score); + output.push_back(leaderboard->self_score.score_details.size()); + for (auto &s : leaderboard->self_score.score_details) { + output.push_back(s); + } + + std::string leaderboard_name = ascii_to_lowercase(leaderboard->name); + local_storage->store_data(Local_Storage::leaderboard_storage_folder, leaderboard_name, (char* )output.data(), sizeof(uint32_t) * output.size()); +} + +std::vector load_leaderboard_scores(std::string name) +{ + std::vector out; + + std::string leaderboard_name = ascii_to_lowercase(name); + unsigned size = local_storage->file_size(Local_Storage::leaderboard_storage_folder, leaderboard_name); + if (size == 0 || (size % sizeof(uint32_t)) != 0) return out; + + std::vector output(size / sizeof(uint32_t)); + if (local_storage->get_data(Local_Storage::leaderboard_storage_folder, leaderboard_name, (char* )output.data(), size) != size) return out; + + unsigned i = 0; + while (true) { + if ((i + 4) > output.size()) break; + + Steam_Leaderboard_Score score; + score.steam_id = CSteamID((uint64)output[i] + (((uint64)output[i + 1]) << 32)); + i += 2; + score.score = output[i]; + i += 1; + unsigned count = output[i]; + i += 1; + + if ((i + count) > output.size()) break; + + for (unsigned j = 0; j < count; ++j) { + score.score_details.push_back(output[i]); + i += 1; + } + + PRINT_DEBUG("loaded leaderboard score %llu %u\n", score.steam_id.ConvertToUint64(), score.score); + out.push_back(score); + } + + return out; +} + public: Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, Steam_Overlay* overlay): settings(settings), @@ -734,6 +795,14 @@ SteamAPICall_t FindOrCreateLeaderboard( const char *pchLeaderboardName, ELeaderb leaderboard.name = std::string(pchLeaderboardName); leaderboard.sort_method = eLeaderboardSortMethod; leaderboard.display_type = eLeaderboardDisplayType; + + std::vector scores = load_leaderboard_scores(pchLeaderboardName); + for (auto &s : scores) { + if (s.steam_id == settings->get_local_steam_id()) { + leaderboard.self_score = s; + } + } + leaderboards.push_back(leaderboard); leader = leaderboards.size(); } @@ -826,10 +895,12 @@ SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard, { PRINT_DEBUG("DownloadLeaderboardEntries %llu %i %i %i\n", hSteamLeaderboard, eLeaderboardDataRequest, nRangeStart, nRangeEnd); std::lock_guard lock(global_mutex); + if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_uAPICallInvalid; //might return callresult even if hSteamLeaderboard is invalid + LeaderboardScoresDownloaded_t data; data.m_hSteamLeaderboard = hSteamLeaderboard; - data.m_hSteamLeaderboardEntries = 123; - data.m_cEntryCount = 0; + data.m_hSteamLeaderboardEntries = hSteamLeaderboard; + data.m_cEntryCount = leaderboards[hSteamLeaderboard - 1].self_score.steam_id.IsValid(); return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); } @@ -843,10 +914,19 @@ SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLead { PRINT_DEBUG("DownloadLeaderboardEntriesForUsers %i %llu\n", cUsers, cUsers > 0 ? prgUsers[0].ConvertToUint64() : 0); std::lock_guard lock(global_mutex); + if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_uAPICallInvalid; //might return callresult even if hSteamLeaderboard is invalid + + bool get_for_current_id = false; + for (int i = 0; i < cUsers; ++i) { + if (prgUsers[i] == settings->get_local_steam_id()) { + get_for_current_id = true; + } + } + LeaderboardScoresDownloaded_t data; data.m_hSteamLeaderboard = hSteamLeaderboard; - data.m_hSteamLeaderboardEntries = 123; - data.m_cEntryCount = 0; + data.m_hSteamLeaderboardEntries = hSteamLeaderboard; + data.m_cEntryCount = get_for_current_id && leaderboards[hSteamLeaderboard - 1].self_score.steam_id.IsValid(); return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); } @@ -868,7 +948,20 @@ SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLead bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t *pLeaderboardEntry, int32 *pDetails, int cDetailsMax ) { PRINT_DEBUG("GetDownloadedLeaderboardEntry\n"); - return false; + std::lock_guard lock(global_mutex); + if (hSteamLeaderboardEntries > leaderboards.size() || hSteamLeaderboardEntries <= 0) return false; + if (index > 0) return false; + + LeaderboardEntry_t entry = {}; + entry.m_steamIDUser = leaderboards[hSteamLeaderboardEntries - 1].self_score.steam_id; + entry.m_nGlobalRank = 1; + entry.m_nScore = leaderboards[hSteamLeaderboardEntries - 1].self_score.score; + for (int i = 0; i < leaderboards[hSteamLeaderboardEntries - 1].self_score.score_details.size() && i < cDetailsMax; ++i) { + pDetails[i] = leaderboards[hSteamLeaderboardEntries - 1].self_score.score_details[i]; + } + + *pLeaderboardEntry = entry; + return true; } @@ -879,17 +972,39 @@ bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardE STEAM_CALL_RESULT( LeaderboardScoreUploaded_t ) SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount ) { - PRINT_DEBUG("UploadLeaderboardScore\n"); + PRINT_DEBUG("UploadLeaderboardScore %i\n", nScore); std::lock_guard lock(global_mutex); + if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_uAPICallInvalid; //TODO: might return callresult even if hSteamLeaderboard is invalid + + Steam_Leaderboard_Score score; + score.score = nScore; + score.steam_id = settings->get_local_steam_id(); + for (int i = 0; i < cScoreDetailsCount; ++i) { + score.score_details.push_back(pScoreDetails[i]); + } + + bool changed = false; + if (eLeaderboardUploadScoreMethod == k_ELeaderboardUploadScoreMethodKeepBest) { + if (leaderboards[hSteamLeaderboard - 1].self_score.score <= score.score) { + leaderboards[hSteamLeaderboard - 1].self_score = score; + changed = true; + } + } else { + if (leaderboards[hSteamLeaderboard - 1].self_score.score != score.score) changed = true; + leaderboards[hSteamLeaderboard - 1].self_score = score; + } + + if (changed) { + save_leaderboard_score(&(leaderboards[hSteamLeaderboard - 1])); + } + 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_bScoreChanged = changed; + data.m_nGlobalRankNew = 1; data.m_nGlobalRankPrevious = 0; return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); }