From 21e08ed220bcbc3700b06d651e56efaca3c27ed9 Mon Sep 17 00:00:00 2001 From: Nemirtingas Date: Sat, 24 Aug 2019 17:42:39 +0200 Subject: [PATCH] Achievements support & inventory customization. Added achievements support. Achievements are saved like real steam, see your inventory directory.(like items, uses a .json, tool can be used to dump achievements from steam public api). You will need a public apikey. (See https://steamcommunity.com/dev) Reworked item support to split inventory items to defined items. --- dll/item_db_loader.cpp | 62 ---------- dll/item_db_loader.h | 24 ---- dll/local_storage.cpp | 75 ++++++++++- dll/local_storage.h | 21 ++-- dll/steam_client.cpp | 11 +- dll/steam_inventory.h | 191 ++++++++++++++++++---------- dll/steam_remote_storage.h | 34 ++--- dll/steam_user.h | 2 +- dll/steam_user_stats.h | 247 +++++++++++++++++++------------------ 9 files changed, 365 insertions(+), 302 deletions(-) delete mode 100644 dll/item_db_loader.cpp delete mode 100644 dll/item_db_loader.h diff --git a/dll/item_db_loader.cpp b/dll/item_db_loader.cpp deleted file mode 100644 index ee59105..0000000 --- a/dll/item_db_loader.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (C) 2019 Nemirtingas (Maxime P) - 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 - . */ -#include "item_db_loader.h" - -#include -#include "../json/json.hpp" - -void read_items_db(std::string items_db, std::map> *items, std::atomic_bool *is_loadedb) -{ - std::ifstream items_file(items_db); - // If there is a file and we opened it - if( items_file ) - { - items_file.seekg(0, std::ios::end); - size_t size = items_file.tellg(); - std::string buffer(size, '\0'); - items_file.seekg(0); - // Read it entirely, if the .json file gets too big, - // I should look into this and split reads into smaller parts. - items_file.read(&buffer[0], size); - items_file.close(); - - try - { - std::map> tmp; - nlohmann::json json = nlohmann::json::parse(buffer); - - for (auto& i : json.items()) - { - SteamItemDef_t key = std::stoi((*i).key()); - nlohmann::json& value = (*i).value(); - for (auto& j : value.items()) - { - tmp[key][(*j).key()] = (*j).value(); - } - } - - items->swap(tmp); - } - catch (std::exception& e) - { - PRINT_DEBUG("Error while parsing json: %s\n", e.what()); - } - } - - PRINT_DEBUG("Loaded json. Loaded %u items.\n", items->size()); - *is_loadedb = true; -} \ No newline at end of file diff --git a/dll/item_db_loader.h b/dll/item_db_loader.h deleted file mode 100644 index 89c0912..0000000 --- a/dll/item_db_loader.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright (C) 2019 Nemirtingas (Maxime P) - 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;*/ -#ifndef __ITEM_DB_LOADER_INCLUDED__ -#define __ITEM_DB_LOADER_INCLUDED__ - -#include "base.h" // For SteamItemDef_t - -#include -void read_items_db(std::string items_db, std::map> *items, std::atomic_bool *is_loaded); - -#endif//__ITEM_DB_LOADER_INCLUDED__ \ No newline at end of file diff --git a/dll/local_storage.cpp b/dll/local_storage.cpp index d5d6d46..5b1ab8c 100644 --- a/dll/local_storage.cpp +++ b/dll/local_storage.cpp @@ -20,6 +20,7 @@ #include #include #include +#include struct File_Data { std::string name; @@ -52,6 +53,11 @@ std::string Local_Storage::get_global_settings_path() return ""; } +std::string Local_Storage::get_global_inventory_path() +{ + return ""; +} + Local_Storage::Local_Storage(std::string save_directory) { @@ -127,6 +133,16 @@ bool Local_Storage::update_save_filenames(std::string folder) return true; } +bool Local_Storage::load_inventory_file(nlohmann::json& json, std::string const&file) +{ + return true +} + +bool Local_Storage::write_inventory_file(nlohmann::json const& json, std::string const&file) +{ + return true; +} + std::vector Local_Storage::get_filenames_path(std::string path) { return std::vector(); @@ -378,7 +394,7 @@ std::string Local_Storage::get_program_path() std::string Local_Storage::get_game_settings_path() { - return get_program_path().append(GAME_SETTINGS_FOLDER).append(PATH_SEPARATOR); + return get_program_path().append(game_settings_folder).append(PATH_SEPARATOR); } #if defined(STEAM_WIN32) @@ -508,7 +524,12 @@ std::string Local_Storage::get_path(std::string folder) std::string Local_Storage::get_global_settings_path() { - return save_directory + SETTINGS_STORAGE_FOLDER + PATH_SEPARATOR; + return save_directory + settings_storage_folder + PATH_SEPARATOR; +} + +std::string Local_Storage::get_global_inventory_path() +{ + return save_directory + inventory_storage_folder + PATH_SEPARATOR; } std::vector Local_Storage::get_filenames_path(std::string path) @@ -680,4 +701,54 @@ bool Local_Storage::update_save_filenames(std::string folder) return true; } +bool Local_Storage::load_inventory_file(nlohmann::json& json, std::string const&file) +{ + std::string inv_path = std::move(get_global_inventory_path() + appid + file); + std::ifstream inventory_file(inv_path); + // If there is a file and we opened it + if (inventory_file) + { + inventory_file.seekg(0, std::ios::end); + size_t size = inventory_file.tellg(); + std::string buffer(size, '\0'); + inventory_file.seekg(0); + // Read it entirely, if the .json file gets too big, + // I should look into this and split reads into smaller parts. + inventory_file.read(&buffer[0], size); + inventory_file.close(); + + try { + json = std::move(nlohmann::json::parse(buffer)); + PRINT_DEBUG("Loaded inventory \"%s\". Loaded %u items.\n", inv_path.c_str(), json.size()); + return true; + } catch (std::exception& e) { + PRINT_DEBUG("Error while parsing \"%s\" inventory json: %s\n", inv_path.c_str(), e.what()); + } + } + else + { + PRINT_DEBUG("Couldn't open file \"%s\" to read inventory\n", inv_path.c_str()); + } + + return false; +} + +bool Local_Storage::write_inventory_file(nlohmann::json const& json, std::string const&file) +{ + std::string inv_path = std::move(get_global_inventory_path() + appid); + + create_directory(inv_path); + + std::ofstream inventory_file(inv_path + file, std::ios::trunc | std::ios::out); + if (inventory_file) + { + inventory_file << std::setw(2) << json; + return true; + } + + PRINT_DEBUG("Couldn't open file \"%s\" to write inventory\n", inv_path.c_str()); + + return false; +} + #endif diff --git a/dll/local_storage.h b/dll/local_storage.h index fb22db7..ef618a5 100644 --- a/dll/local_storage.h +++ b/dll/local_storage.h @@ -21,18 +21,21 @@ #ifndef LOCAL_STORAGE_INCLUDE #define LOCAL_STORAGE_INCLUDE -#define SETTINGS_STORAGE_FOLDER "settings" -#define REMOTE_STORAGE_FOLDER "remote" -#define STATS_STORAGE_FOLDER "stats" -#define USER_DATA_FOLDER "local" - -#define GAME_SETTINGS_FOLDER "steam_settings" - #include +#include "../json/json.hpp" #define MAX_FILENAME_LENGTH 300 class Local_Storage { +public: + static constexpr auto inventory_storage_folder = "inventory"; + static constexpr auto settings_storage_folder = "settings"; + static constexpr auto remote_storage_folder = "remote"; + static constexpr auto stats_storage_folder = "stats"; + static constexpr auto user_data_storage = "local"; + static constexpr auto game_settings_folder = "steam_settings"; + +private: std::string save_directory; std::string appid; public: @@ -56,9 +59,13 @@ public: bool file_delete(std::string folder, std::string file); uint64_t file_timestamp(std::string folder, std::string file); std::string get_global_settings_path(); + std::string get_global_inventory_path(); std::string get_path(std::string folder); bool update_save_filenames(std::string folder); + + bool load_inventory_file(nlohmann::json &json, std::string const&file); + bool write_inventory_file(nlohmann::json const& json, std::string const&file); }; #endif diff --git a/dll/steam_client.cpp b/dll/steam_client.cpp index ba33cf5..8ae55e7 100644 --- a/dll/steam_client.cpp +++ b/dll/steam_client.cpp @@ -162,7 +162,7 @@ Steam_Client::Steam_Client() uint64 steam_id = 0; bool generate_new = false; //try to load steam id from game specific settings folder first - if (local_storage->get_data(SETTINGS_STORAGE_FOLDER, "user_steam_id.txt", array_steam_id, sizeof(array_steam_id) - 1) > 0) { + if (local_storage->get_data(Local_Storage::settings_storage_folder, "user_steam_id.txt", array_steam_id, sizeof(array_steam_id) - 1) > 0) { user_id = CSteamID((uint64)std::atoll(array_steam_id)); if (!user_id.IsValid()) { generate_new = true; @@ -335,9 +335,6 @@ Steam_Client::Steam_Client() } } - std::string achievements_db_file_path = (Local_Storage::get_game_settings_path() + "achievements.json"); - std::string items_db_file_path = (Local_Storage::get_game_settings_path() + "items.json"); - network = new Networking(settings_server->get_local_steam_id(), appid, port, &custom_broadcasts); callback_results_client = new SteamCallResults(); @@ -353,7 +350,7 @@ Steam_Client::Steam_Client() steam_utils = new Steam_Utils(settings_client, callback_results_client); steam_matchmaking = new Steam_Matchmaking(settings_client, network, callback_results_client, callbacks_client, run_every_runcb); steam_matchmaking_servers = new Steam_Matchmaking_Servers(settings_client, network); - steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client, achievements_db_file_path); + steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client); steam_apps = new Steam_Apps(settings_client, callback_results_client); steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb); steam_remote_storage = new Steam_Remote_Storage(settings_client, local_storage, callback_results_client); @@ -365,7 +362,7 @@ Steam_Client::Steam_Client() steam_music = new Steam_Music(callbacks_client); steam_musicremote = new Steam_MusicRemote(); steam_HTMLsurface = new Steam_HTMLsurface(settings_client, network, callback_results_client, callbacks_client); - steam_inventory = new Steam_Inventory(settings_client, callback_results_client, callbacks_client, run_every_runcb, items_db_file_path); + steam_inventory = new Steam_Inventory(settings_client, callback_results_client, callbacks_client, run_every_runcb, local_storage); steam_video = new Steam_Video(); steam_parental = new Steam_Parental(); steam_networking_sockets = new Steam_Networking_Sockets(settings_client, network, callback_results_client, callbacks_client, run_every_runcb); @@ -383,7 +380,7 @@ Steam_Client::Steam_Client() steam_gameserverstats = new Steam_GameServerStats(settings_server, network, callback_results_server, callbacks_server); steam_gameserver_networking = new Steam_Networking(settings_server, network, callbacks_server, run_every_runcb); steam_gameserver_http = new Steam_HTTP(settings_server, network, callback_results_server, callbacks_server); - steam_gameserver_inventory = new Steam_Inventory(settings_server, callback_results_server, callbacks_server, run_every_runcb, items_db_file_path); + steam_gameserver_inventory = new Steam_Inventory(settings_server, callback_results_server, callbacks_server, run_every_runcb, local_storage); steam_gameserver_ugc = new Steam_UGC(settings_server, callback_results_server, callbacks_server); steam_gameserver_apps = new Steam_Apps(settings_server, callback_results_server); steam_gameserver_networking_sockets = new Steam_Networking_Sockets(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); diff --git a/dll/steam_inventory.h b/dll/steam_inventory.h index 2eec7fb..8518230 100644 --- a/dll/steam_inventory.h +++ b/dll/steam_inventory.h @@ -15,8 +15,8 @@ License along with the Goldberg Emulator; if not, see . */ -#include "item_db_loader.h" -#include +#include "base.h" // For SteamItemDef_t +#include "../json/json.hpp" struct Steam_Inventory_Requests { double timeout = 0.1; @@ -42,22 +42,24 @@ class Steam_Inventory : public ISteamInventory002, public ISteamInventory { +public: + static constexpr auto items_user_file = "items.json"; + +private: class Settings *settings; class SteamCallResults *callback_results; class SteamCallBacks *callbacks; class RunEveryRunCB *run_every_runcb; + class Local_Storage* local_storage; std::vector inventory_requests; - std::map> items; - // Like typedefs - using item_iterator = std::map>::iterator; - using attr_iterator = std::map::iterator; + nlohmann::json defined_items; + nlohmann::json user_items; - std::atomic_bool items_loaded; - std::string items_db_file; - std::once_flag load_items_flag; + bool inventory_loaded; bool call_definition_update; + bool call_inventory_update; bool definition_update_called; bool full_update_called; @@ -89,6 +91,40 @@ struct Steam_Inventory_Requests *get_inventory_result(SteamInventoryResult_t res return &(*request); } +void read_items_db() +{ + std::string items_db_path = Local_Storage::get_game_settings_path() + items_user_file; + PRINT_DEBUG("Items file path: %s\n", items_db_path.c_str()); + std::ifstream inventory_file(items_db_path); + // If there is a file and we opened it + if (inventory_file) + { + inventory_file.seekg(0, std::ios::end); + size_t size = inventory_file.tellg(); + std::string buffer(size, '\0'); + inventory_file.seekg(0); + // Read it entirely, if the .json file gets too big, + // I should look into this and split reads into smaller parts. + inventory_file.read(&buffer[0], size); + inventory_file.close(); + + try + { + defined_items = std::move(nlohmann::json::parse(buffer)); + PRINT_DEBUG("Loaded inventory. Loaded %u items.\n", defined_items.size()); + } + catch (std::exception& e) + { + PRINT_DEBUG("Error while parsing inventory json: %s\n", e.what()); + } + } +} + +void read_inventory_db() +{ + local_storage->load_inventory_file(user_items, items_user_file); +} + public: static void run_every_runcb_cb(void *object) @@ -99,21 +135,21 @@ static void run_every_runcb_cb(void *object) obj->RunCallbacks(); } -Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb, std::string items_db_file_path) +Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb, class Local_Storage *local_storage): + settings(settings), + callback_results(callback_results), + callbacks(callbacks), + run_every_runcb(run_every_runcb), + local_storage(local_storage), + defined_items(nlohmann::json::object()), + user_items(nlohmann::json::object()), + inventory_loaded(false), + call_definition_update(false), + call_inventory_update(false), + definition_update_called(false), + full_update_called(false) { - items_db_file = items_db_file_path; - PRINT_DEBUG("Items file path: %s\n", items_db_file.c_str()); - items_loaded = false; - - this->settings = settings; - this->callbacks = callbacks; - this->callback_results = callback_results; - this->run_every_runcb = run_every_runcb; this->run_every_runcb->add(&Steam_Inventory::run_every_runcb_cb, this); - - call_definition_update = false; - definition_update_called = false; - full_update_called = false; } ~Steam_Inventory() @@ -160,6 +196,7 @@ bool GetResultItems( SteamInventoryResult_t resultHandle, struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle); if (!request) return false; if (!request->result_done()) return false; + if (!inventory_loaded) return false; if (pOutItemsArray != nullptr) { @@ -167,21 +204,35 @@ bool GetResultItems( SteamInventoryResult_t resultHandle, if (request->full_query) { // We end if we reached the end of items or the end of buffer - for( auto i = items.begin(); i != items.end() && max_items; ++i, --max_items ) + for( auto i = user_items.begin(); i != user_items.end() && max_items; ++i, --max_items ) { - pOutItemsArray->m_iDefinition = i->first; - pOutItemsArray->m_itemId = i->first; - pOutItemsArray->m_unQuantity = 1; + pOutItemsArray->m_iDefinition = std::stoi(i.key()); + pOutItemsArray->m_itemId = pOutItemsArray->m_iDefinition; + try + { + pOutItemsArray->m_unQuantity = i.value().get(); + } + catch (...) + { + pOutItemsArray->m_unQuantity = 0; + } pOutItemsArray->m_unFlags = k_ESteamItemNoTrade; ++pOutItemsArray; } - *punOutItemsArraySize = std::min(*punOutItemsArraySize, static_cast(items.size())); + *punOutItemsArraySize = std::min(*punOutItemsArraySize, static_cast(user_items.size())); } else { for (auto &itemid : request->instance_ids) { if (!max_items) break; pOutItemsArray->m_iDefinition = itemid; pOutItemsArray->m_itemId = itemid; - pOutItemsArray->m_unQuantity = 1; + try + { + pOutItemsArray->m_unQuantity = user_items[itemid].get(); + } + catch (...) + { + pOutItemsArray->m_unQuantity = 0; + } pOutItemsArray->m_unFlags = k_ESteamItemNoTrade; ++pOutItemsArray; --max_items; @@ -190,7 +241,7 @@ bool GetResultItems( SteamInventoryResult_t resultHandle, } else if (punOutItemsArraySize != nullptr) { - *punOutItemsArraySize = items.size(); + *punOutItemsArraySize = user_items.size(); } PRINT_DEBUG("GetResultItems good\n"); @@ -278,7 +329,7 @@ bool GetAllItems( SteamInventoryResult_t *pResultHandle ) std::lock_guard lock(global_mutex); struct Steam_Inventory_Requests* request = new_inventory_result(); - if (!definition_update_called) call_definition_update = true; + call_inventory_update = true; if (pResultHandle != nullptr) *pResultHandle = request->inventory_result; @@ -302,6 +353,7 @@ bool GetItemsByID( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT( unC std::lock_guard lock(global_mutex); if (pResultHandle) { struct Steam_Inventory_Requests *request = new_inventory_result(false, pInstanceIDs, unCountInstanceIDs); + //call_inventory_update = true; *pResultHandle = request->inventory_result; return true; } @@ -559,15 +611,15 @@ bool GetItemDefinitionIDs( if (pItemDefIDs == nullptr) { - *punItemDefIDsArraySize = items.size(); + *punItemDefIDsArraySize = defined_items.size(); return true; } - if (*punItemDefIDsArraySize < items.size()) + if (*punItemDefIDsArraySize < defined_items.size()) return false; - for (auto& i : items) - *pItemDefIDs++ = i.first; + for (auto i = defined_items.begin(); i != defined_items.end(); ++i) + *pItemDefIDs++ = std::stoi(i.key()); return true; } @@ -588,25 +640,38 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope PRINT_DEBUG("GetItemDefinitionProperty %i %s\n", iDefinition, pchPropertyName); std::lock_guard lock(global_mutex); - item_iterator item; - if ((item = items.find(iDefinition)) != items.end()) + auto item = defined_items.find(std::to_string(iDefinition)); + if (item != defined_items.end()) { - attr_iterator attr; if (pchPropertyName != nullptr) { // Should I check for punValueBufferSizeOut == nullptr ? // Try to get the property - if ((attr = item->second.find(pchPropertyName)) != items[iDefinition].end()) + auto attr = item.value().find(pchPropertyName); + if (attr != item.value().end()) { - std::string const& val = attr->second; + std::string val; + try + { + val = attr.value().get(); + } + catch (...) + { + pchPropertyName = ""; + *punValueBufferSizeOut = 0; + PRINT_DEBUG("Error, item: %d, attr: %s is not a string!", iDefinition, pchPropertyName); + return true; + } if (pchValueBuffer != nullptr) { // copy what we can strncpy(pchValueBuffer, val.c_str(), *punValueBufferSizeOut); } - - // Set punValueBufferSizeOut to the property size - *punValueBufferSizeOut = std::min(static_cast(val.length() + 1), *punValueBufferSizeOut); + else + { + // Set punValueBufferSizeOut to the property size + *punValueBufferSizeOut = val.length() + 1; + } if (pchValueBuffer != nullptr) { @@ -628,8 +693,8 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope { // Should I check for punValueBufferSizeOut == nullptr ? *punValueBufferSizeOut = 0; - for (auto& i : item->second) - *punValueBufferSizeOut += i.first.length() + 1; // Size of key + comma, and the last is not a comma but null char + for (auto i = item.value().begin(); i != item.value().end(); ++i) + *punValueBufferSizeOut += i.key().length() + 1; // Size of key + comma, and the last is not a comma but null char } else { @@ -637,16 +702,16 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope uint32_t len = *punValueBufferSizeOut-1; *punValueBufferSizeOut = 0; memset(pchValueBuffer, 0, len); - for( auto i = item->second.begin(); i != item->second.end() && len > 0; ++i ) + for( auto i = item.value().begin(); i != item.value().end() && len > 0; ++i ) { - strncat(pchValueBuffer, i->first.c_str(), len); + strncat(pchValueBuffer, i.key().c_str(), len); // Count how many chars we copied // Either the string length or the buffer size if its too small - uint32 x = std::min(len, static_cast(i->first.length())); + uint32 x = std::min(len, static_cast(i.key().length())); *punValueBufferSizeOut += x; len -= x; - if (len && std::distance(i, item->second.end()) != 1) // If this is not the last item, add a comma + if (len && std::distance(i, item.value().end()) != 1) // If this is not the last item, add a comma strncat(pchValueBuffer, ",", len--); // Always add 1, its a comma or the null terminator @@ -781,24 +846,26 @@ bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventory void RunCallbacks() { - if (call_definition_update || inventory_requests.size()) { - std::call_once(load_items_flag, [&]() { - std::thread items_load_thread(read_items_db, items_db_file, &items, &items_loaded); - items_load_thread.detach(); - }); + if (call_definition_update) { + read_items_db(); + definition_update_called = true; + + SteamInventoryDefinitionUpdate_t data = {}; + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + call_definition_update = false; } - if (items_loaded) { - if (call_definition_update) { - SteamInventoryDefinitionUpdate_t data = {}; - callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); - call_definition_update = false; - definition_update_called = true; - } + if (call_inventory_update) { + read_inventory_db(); + inventory_loaded = true; + call_inventory_update = false; + } + + if (definition_update_called && inventory_loaded) + { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - - for (auto & r : inventory_requests) { + for (auto& r : inventory_requests) { if (!r.done && std::chrono::duration_cast>(now - r.time_created).count() > r.timeout) { if (r.full_query) { if (!full_update_called) { diff --git a/dll/steam_remote_storage.h b/dll/steam_remote_storage.h index d0c7646..5a95d2a 100644 --- a/dll/steam_remote_storage.h +++ b/dll/steam_remote_storage.h @@ -69,7 +69,7 @@ Steam_Remote_Storage(class Settings *settings, Local_Storage *local_storage, cla this->local_storage = local_storage; this->callback_results = callback_results; steam_cloud_enabled = true; - local_storage->update_save_filenames(REMOTE_STORAGE_FOLDER); + local_storage->update_save_filenames(Local_Storage::remote_storage_folder); } // NOTE @@ -84,7 +84,7 @@ bool FileWrite( const char *pchFile, const void *pvData, int32 cubData ) { PRINT_DEBUG("Steam_Remote_Storage::FileWrite %s %u\n", pchFile, cubData); std::lock_guard lock(global_mutex); - int data_stored = local_storage->store_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubData); + int data_stored = local_storage->store_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubData); PRINT_DEBUG("Steam_Remote_Storage::Stored %i, %u\n", data_stored, data_stored == cubData); return data_stored == cubData; } @@ -93,7 +93,7 @@ int32 FileRead( const char *pchFile, void *pvData, int32 cubDataToRead ) { PRINT_DEBUG("Steam_Remote_Storage::FileRead %s %i\n", pchFile, cubDataToRead); std::lock_guard lock(global_mutex); - int read_data = local_storage->get_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubDataToRead); + int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubDataToRead); if (read_data < 0) read_data = 0; PRINT_DEBUG("Read %i\n", read_data); return read_data; @@ -104,7 +104,7 @@ SteamAPICall_t FileWriteAsync( const char *pchFile, const void *pvData, uint32 c { PRINT_DEBUG("Steam_Remote_Storage::FileWriteAsync\n"); std::lock_guard lock(global_mutex); - bool success = local_storage->store_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubData) == cubData; + bool success = local_storage->store_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubData) == cubData; RemoteStorageFileWriteAsyncComplete_t data; data.m_eResult = k_EResultOK; @@ -118,7 +118,7 @@ SteamAPICall_t FileReadAsync( const char *pchFile, uint32 nOffset, uint32 cubToR PRINT_DEBUG("Steam_Remote_Storage::FileReadAsync\n"); std::lock_guard lock(global_mutex); - unsigned int size = local_storage->file_size(REMOTE_STORAGE_FOLDER, pchFile); + unsigned int size = local_storage->file_size(Local_Storage::remote_storage_folder, pchFile); RemoteStorageFileReadAsyncComplete_t data; if (size <= nOffset) { @@ -153,7 +153,7 @@ bool FileReadAsyncComplete( SteamAPICall_t hReadCall, void *pvBuffer, uint32 cub return false; char *temp = new char[a_read->size]; - int read_data = local_storage->get_data(REMOTE_STORAGE_FOLDER, a_read->file_name, (char* )temp, a_read->size); + int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, a_read->file_name, (char* )temp, a_read->size); if (read_data < a_read->to_read + a_read->offset) { delete[] temp; return false; @@ -175,7 +175,7 @@ bool FileForget( const char *pchFile ) bool FileDelete( const char *pchFile ) { PRINT_DEBUG("Steam_Remote_Storage::FileDelete\n"); - return local_storage->file_delete(REMOTE_STORAGE_FOLDER, pchFile); + return local_storage->file_delete(Local_Storage::remote_storage_folder, pchFile); } STEAM_CALL_RESULT( RemoteStorageFileShareResult_t ) @@ -184,7 +184,7 @@ SteamAPICall_t FileShare( const char *pchFile ) PRINT_DEBUG("Steam_Remote_Storage::FileShare\n"); std::lock_guard lock(global_mutex); RemoteStorageFileShareResult_t data = {}; - if (local_storage->file_exists(REMOTE_STORAGE_FOLDER, pchFile)) { + if (local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile)) { data.m_eResult = k_EResultOK; data.m_hFile = generate_steam_api_call_id(); strncpy(data.m_rgchFilename, pchFile, sizeof(data.m_rgchFilename) - 1); @@ -237,7 +237,7 @@ bool FileWriteStreamClose( UGCFileWriteStreamHandle_t writeHandle ) if (stream_writes.end() == request) return false; - local_storage->store_data(REMOTE_STORAGE_FOLDER, request->file_name, request->file_data.data(), request->file_data.size()); + local_storage->store_data(Local_Storage::remote_storage_folder, request->file_name, request->file_data.data(), request->file_data.size()); stream_writes.erase(request); return true; } @@ -258,25 +258,25 @@ bool FileWriteStreamCancel( UGCFileWriteStreamHandle_t writeHandle ) bool FileExists( const char *pchFile ) { PRINT_DEBUG("Steam_Remote_Storage::FileExists %s\n", pchFile); - return local_storage->file_exists(REMOTE_STORAGE_FOLDER, pchFile); + return local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile); } bool FilePersisted( const char *pchFile ) { PRINT_DEBUG("Steam_Remote_Storage::FilePersisted\n"); - return local_storage->file_exists(REMOTE_STORAGE_FOLDER, pchFile); + return local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile); } int32 GetFileSize( const char *pchFile ) { PRINT_DEBUG("Steam_Remote_Storage::GetFileSize %s\n", pchFile); - return local_storage->file_size(REMOTE_STORAGE_FOLDER, pchFile); + return local_storage->file_size(Local_Storage::remote_storage_folder, pchFile); } int64 GetFileTimestamp( const char *pchFile ) { PRINT_DEBUG("Steam_Remote_Storage::GetFileTimestamp\n"); - return local_storage->file_timestamp(REMOTE_STORAGE_FOLDER, pchFile); + return local_storage->file_timestamp(Local_Storage::remote_storage_folder, pchFile); } ERemoteStoragePlatform GetSyncPlatforms( const char *pchFile ) @@ -290,7 +290,7 @@ ERemoteStoragePlatform GetSyncPlatforms( const char *pchFile ) int32 GetFileCount() { PRINT_DEBUG("Steam_Remote_Storage::GetFileCount\n"); - int32 num = local_storage->count_files(REMOTE_STORAGE_FOLDER); + int32 num = local_storage->count_files(Local_Storage::remote_storage_folder); PRINT_DEBUG("Steam_Remote_Storage::File count: %i\n", num); return num; } @@ -299,7 +299,7 @@ const char *GetFileNameAndSize( int iFile, int32 *pnFileSizeInBytes ) { PRINT_DEBUG("Steam_Remote_Storage::GetFileNameAndSize %i\n", iFile); static char output_filename[MAX_FILENAME_LENGTH]; - if (local_storage->iterate_file(REMOTE_STORAGE_FOLDER, iFile, output_filename, pnFileSizeInBytes)) { + if (local_storage->iterate_file(Local_Storage::remote_storage_folder, iFile, output_filename, pnFileSizeInBytes)) { PRINT_DEBUG("Steam_Remote_Storage::Name: |%s|, size: %i\n", output_filename, pnFileSizeInBytes ? *pnFileSizeInBytes : 0); return output_filename; } else { @@ -372,7 +372,7 @@ SteamAPICall_t UGCDownload( UGCHandle_t hContent, uint32 unPriority ) data.m_eResult = k_EResultOK; data.m_hFile = hContent; data.m_nAppID = settings->get_local_game_id().AppID(); - data.m_nSizeInBytes = local_storage->file_size(REMOTE_STORAGE_FOLDER, shared_files[hContent]); + data.m_nSizeInBytes = local_storage->file_size(Local_Storage::remote_storage_folder, shared_files[hContent]); shared_files[hContent].copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1); data.m_ulSteamIDOwner = settings->get_local_steam_id().ConvertToUint64(); downloaded_files[hContent].file = shared_files[hContent]; @@ -426,7 +426,7 @@ int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 c } Downloaded_File f = downloaded_files[hContent]; - int read_data = local_storage->get_data(REMOTE_STORAGE_FOLDER, f.file, (char* )pvData, cubDataToRead, cOffset); + int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, f.file, (char* )pvData, cubDataToRead, cOffset); if (eAction == k_EUGCRead_Close || (eAction == k_EUGCRead_ContinueReadingUntilFinished && (read_data < cubDataToRead || (cOffset + cubDataToRead) >= f.total_size))) { downloaded_files.erase(hContent); diff --git a/dll/steam_user.h b/dll/steam_user.h index a49c65c..8e2d35c 100644 --- a/dll/steam_user.h +++ b/dll/steam_user.h @@ -149,7 +149,7 @@ bool GetUserDataFolder( char *pchBuffer, int cubBuffer ) PRINT_DEBUG("GetUserDataFolder\n"); if (!cubBuffer) return false; - std::string user_data = local_storage->get_path(USER_DATA_FOLDER); + std::string user_data = local_storage->get_path(Local_Storage::user_data_storage); strncpy(pchBuffer, user_data.c_str(), cubBuffer - 1); pchBuffer[cubBuffer - 1] = 0; return true; diff --git a/dll/steam_user_stats.h b/dll/steam_user_stats.h index 57bb57a..4b578bb 100644 --- a/dll/steam_user_stats.h +++ b/dll/steam_user_stats.h @@ -38,14 +38,19 @@ public ISteamUserStats009, public ISteamUserStats010, public ISteamUserStats { +public: + static constexpr auto achievements_user_file = "achievements.json"; + +private: + Local_Storage *local_storage; Settings *settings; SteamCallResults *callback_results; class SteamCallBacks *callbacks; std::vector leaderboards; - std::string db_file_path; - nlohmann::json achievements; + nlohmann::json defined_achievements; + nlohmann::json user_achievements; unsigned int find_leaderboard(std::string name) { @@ -58,9 +63,10 @@ unsigned int find_leaderboard(std::string name) return 0; } -void load_achievements() +void load_achievements_db() { - std::ifstream achs_file(db_file_path); + std::string file_path = Local_Storage::get_game_settings_path() + achievements_user_file; + std::ifstream achs_file(file_path); if (achs_file) { @@ -73,26 +79,34 @@ void load_achievements() achs_file.read(&buffer[0], size); achs_file.close(); - try - { - achievements = nlohmann::json::parse(buffer); - } - catch (std::exception &e) - { - PRINT_DEBUG("(Achievements): Error while parsing json: %s\n", e.what()); + try { + defined_achievements = std::move(nlohmann::json::parse(buffer)); + } catch (std::exception &e) { + PRINT_DEBUG("Error while parsing json: \"%s\" : %s\n", file_path.c_str(), e.what()); } } + else + { + PRINT_DEBUG("Couldn't open file \"%s\" to read achievements definition\n", file_path.c_str()); + } +} + +void load_achievements() +{ + local_storage->load_inventory_file(user_achievements, achievements_user_file); } public: -Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, std::string const&achievements_db_file_path): +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), - db_file_path(achievements_db_file_path) + defined_achievements(nlohmann::json::object()), + user_achievements(nlohmann::json::object()) { - load_achievements(); + load_achievements_db(); // achievements db + load_achievements(); // achievements per user } // Ask the server to send down this user's data and achievements for this game @@ -118,7 +132,7 @@ bool GetStat( const char *pchName, int32 *pData ) if (!pchName || !pData) return false; std::lock_guard lock(global_mutex); - int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )pData, sizeof(*pData)); + int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char* )pData, sizeof(*pData)); if (read_data == sizeof(int32)) return true; @@ -131,7 +145,7 @@ bool GetStat( const char *pchName, float *pData ) if (!pchName || !pData) return false; std::lock_guard lock(global_mutex); - int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )pData, sizeof(*pData)); + int read_data = local_storage->get_data(Local_Storage::stats_storage_folder, pchName, (char* )pData, sizeof(*pData)); if (read_data == sizeof(int32)) return true; @@ -146,7 +160,7 @@ bool SetStat( const char *pchName, int32 nData ) if (!pchName) return false; std::lock_guard lock(global_mutex); - return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, (char* )&nData, sizeof(nData)) == sizeof(nData); + return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, (char* )&nData, sizeof(nData)) == sizeof(nData); } bool SetStat( const char *pchName, float fData ) @@ -155,7 +169,7 @@ bool SetStat( const char *pchName, float fData ) if (!pchName) return false; std::lock_guard lock(global_mutex); - return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, (char* )&fData, sizeof(fData)) == sizeof(fData); + return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, (char* )&fData, sizeof(fData)) == sizeof(fData); } bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dSessionLength ) @@ -164,7 +178,7 @@ bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dS std::lock_guard lock(global_mutex); char data[sizeof(float) + sizeof(float) + sizeof(double)]; - int read_data = local_storage->get_data(STATS_STORAGE_FOLDER, pchName, (char* )data, sizeof(*data)); + 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)) { @@ -180,30 +194,28 @@ bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dS memcpy(data + sizeof(float), &oldcount, sizeof(oldcount)); memcpy(data + sizeof(float) * 2, &oldsessionlength, sizeof(oldsessionlength)); - return local_storage->store_data(STATS_STORAGE_FOLDER, pchName, data, sizeof(data)) == sizeof(data); + return local_storage->store_data(Local_Storage::stats_storage_folder, pchName, data, sizeof(data)) == sizeof(data); } // Achievement flag accessors bool GetAchievement( const char *pchName, bool *pbAchieved ) { - //TODO: these achievement functions need to load a list of achievements from somewhere, return false so that kf2 doesn't loop endlessly PRINT_DEBUG("GetAchievement %s\n", pchName); - try - { - auto it = std::find_if(achievements.begin(), achievements.end(), [pchName]( nlohmann::json &item ) { - return static_cast(item["name"]) == 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() == pchName; }); - if (it != achievements.end()) - { - *pbAchieved = it.value()["earned"]; + auto ach = user_achievements.find(pchName); + if (it != defined_achievements.end() && ach != user_achievements.end()) { + if(pbAchieved != nullptr) *pbAchieved = (*ach)["earned"]; return true; } - } - catch (...) - { + } catch (...) {} - } + if (pbAchieved != nullptr)* pbAchieved = false; return false; } @@ -211,21 +223,18 @@ bool GetAchievement( const char *pchName, bool *pbAchieved ) bool SetAchievement( const char *pchName ) { PRINT_DEBUG("SetAchievement %s\n", pchName); - try - { - auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) { - return static_cast(item["name"]) == 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() == pchName; }); - if (it != achievements.end()) - { - it.value()["earned"] = 1; + if (it != defined_achievements.end()) { + user_achievements[pchName]["earned"] = true; + user_achievements[pchName]["earned_time"] = static_cast(std::time(nullptr)); return true; } - } - catch (...) - { - - } + } catch (...) {} return false; } @@ -233,20 +242,18 @@ bool SetAchievement( const char *pchName ) bool ClearAchievement( const char *pchName ) { PRINT_DEBUG("ClearAchievement %s\n", pchName); - try - { - auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) { + if (pchName == nullptr) return false; + + try { + auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) { return static_cast(item["name"]) == pchName; - }); - if (it != achievements.end()) - { - it.value()["earned"] = 0; + }); + if (it != defined_achievements.end()) { + user_achievements[pchName]["earned"] = false; + user_achievements[pchName]["earned_time"] = static_cast(0); return true; } - } - catch (...) - { - } + } catch (...) {} return false; } @@ -258,26 +265,22 @@ bool ClearAchievement( const char *pchName ) bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime ) { PRINT_DEBUG("GetAchievementAndUnlockTime\n"); - try - { - auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) { + if (pchName == nullptr) return false; + + try { + auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) { return static_cast(item["name"]) == pchName; - }); - if (it != achievements.end()) - { - *pbAchieved = it.value()["earned"].get(); - *punUnlockTime = std::time(NULL); - //*punUnlockTime = it.value()["time_earned"].get(); + }); + 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 (...) - { + } catch (...) {} - } - - *pbAchieved = false; - *punUnlockTime = 0; + if(pbAchieved != nullptr) *pbAchieved = false; + if(punUnlockTime != nullptr) *punUnlockTime = 0; return true; } @@ -294,11 +297,7 @@ bool StoreStats() PRINT_DEBUG("StoreStats\n"); std::lock_guard lock(global_mutex); - std::ofstream achiev_file(db_file_path, std::ios::trunc | std::ios::out); - if (achiev_file) - { - achiev_file << std::setw(2) << achievements; - } + local_storage->write_inventory_file(user_achievements, achievements_user_file); UserStatsStored_t data; data.m_nGameID = settings->get_local_game_id().ToUint64(); @@ -317,6 +316,8 @@ bool StoreStats() int GetAchievementIcon( const char *pchName ) { PRINT_DEBUG("GetAchievementIcon\n"); + if (pchName == nullptr) return 0; + return 0; } @@ -327,56 +328,40 @@ int GetAchievementIcon( const char *pchName ) const char * GetAchievementDisplayAttribute( const char *pchName, const char *pchKey ) { PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey); + if (pchName == nullptr) return ""; + if (pchKey == nullptr) return ""; if (strcmp (pchKey, "name") == 0) { - try - { - auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) { + try { + auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) { return static_cast(item["name"]) == pchName; - }); - if (it != achievements.end()) - { + }); + if (it != defined_achievements.end()) { return it.value()["displayName"].get().c_str(); } - } - catch (...) - { - - } + } catch (...) {} } if (strcmp (pchKey, "desc") == 0) { - try - { - auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) { + try { + auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) { return static_cast(item["name"]) == pchName; - }); - if (it != achievements.end()) - { + }); + if (it != defined_achievements.end()) { return it.value()["description"].get().c_str(); } - } - catch (...) - { - - } + } catch (...) {} } if (strcmp (pchKey, "hidden") == 0) { - try - { - auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) { + try { + auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) { return static_cast(item["name"]) == pchName; - }); - if (it != achievements.end()) - { - return (it.value()["hidden"].get() ? "1" : "0"); + }); + if (it != defined_achievements.end()) { + return it.value()["description"].get().c_str(); } - } - catch (...) - { - - } + } catch (...) {} } return ""; @@ -388,6 +373,8 @@ const char * GetAchievementDisplayAttribute( const char *pchName, const char *pc bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint32 nMaxProgress ) { PRINT_DEBUG("IndicateAchievementProgress\n"); + if (pchName == nullptr) return false; + } @@ -396,21 +383,16 @@ bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint uint32 GetNumAchievements() { PRINT_DEBUG("GetNumAchievements\n"); - return achievements.size(); + return defined_achievements.size(); } // Get achievement name iAchievement in [0,GetNumAchievements) const char * GetAchievementName( uint32 iAchievement ) { PRINT_DEBUG("GetAchievementName\n"); - try - { - return static_cast(achievements[iAchievement]["name"]).c_str(); - } - catch (...) - { - - } + try { + return defined_achievements[iAchievement]["name"].get().c_str(); + } catch (...) {} return ""; } @@ -428,7 +410,10 @@ SteamAPICall_t RequestUserStats( CSteamID steamIDUser ) std::lock_guard lock(global_mutex); // Enable this to allow hot reload achievements status - //load_achievements(); + //if (steamIDUser == settings->get_local_steam_id()) { + // load_achievements(); + //} + UserStatsReceived_t data; data.m_nGameID = settings->get_local_game_id().ToUint64(); @@ -442,6 +427,8 @@ SteamAPICall_t RequestUserStats( CSteamID steamIDUser ) bool 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 lock(global_mutex); if (steamIDUser == settings->get_local_steam_id()) { @@ -456,6 +443,8 @@ bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData ) bool 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 lock(global_mutex); if (steamIDUser == settings->get_local_steam_id()) { @@ -470,6 +459,12 @@ bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData ) bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved ) { PRINT_DEBUG("GetUserAchievement %s\n", pchName); + if (pchName == nullptr) return false; + + if (steamIDUser == settings->get_local_steam_id()) { + return GetAchievement(pchName, pbAchieved); + } + return false; } @@ -477,6 +472,11 @@ bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchi bool GetUserAchievementAndUnlockTime( CSteamID steamIDUser, const char *pchName, bool *pbAchieved, uint32 *punUnlockTime ) { PRINT_DEBUG("GetUserAchievementAndUnlockTime %s\n", pchName); + if (pchName == nullptr) return false; + + if (steamIDUser == settings->get_local_steam_id()) { + return GetAchievementAndUnlockTime(pchName, pbAchieved, punUnlockTime); + } return false; } @@ -486,6 +486,13 @@ bool ResetAllStats( bool bAchievementsToo ) { PRINT_DEBUG("ResetAllStats\n"); //TODO + if (bAchievementsToo) { + std::for_each(user_achievements.begin(), user_achievements.end(), [](nlohmann::json& v) { + v["earned"] = false; + v["earned_time"] = 0; + }); + } + return true; }