Inventory/achivements bug fixes/improvements/cleanups.

This commit is contained in:
Mr_Goldberg 2019-10-19 12:02:30 -04:00
parent 3982ad31e6
commit 828e5d456c
No known key found for this signature in database
GPG Key ID: 8597D87419DEF278
5 changed files with 141 additions and 133 deletions

View File

@ -75,6 +75,7 @@ An example can be found in steam_settings.EXAMPLE that works with Killing Floor
The items.json syntax is simple, you SHOULD validate your .json file before trying to run your game or you won't have any item in your inventory. Just look for "online json validator" on your web brower to valide your file.
You can use https://steamdb.info/ to list items and attributes they have and put them into your .json.
Keep in mind that some item are not valid to have in your inventory. For example, in PayDay2 all items below item_id 50000 will make your game crash.
items.json should contain all the item definitions for the game, default_items.json is the quantity of each item that you want a user to have initially in their inventory. By default the user will have no items.
Leaderboards:
By default the emulator assumes all leaderboards queried by the game (FindLeaderboard()) exist and creates them with the most common options (sort method descending, display type numeric)

View File

@ -128,14 +128,19 @@ bool Local_Storage::update_save_filenames(std::string folder)
return true;
}
bool Local_Storage::load_json(std::string full_path, nlohmann::json& json)
{
return false;
}
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
{
return true;
return false;
}
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
{
return true;
return false;
}
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
@ -691,16 +696,8 @@ bool Local_Storage::update_save_filenames(std::string folder)
return true;
}
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
bool Local_Storage::load_json(std::string full_path, nlohmann::json& json)
{
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string inv_path = std::move(save_directory + appid + folder);
std::string full_path = inv_path + file;
create_directory(inv_path);
std::ifstream inventory_file(full_path);
// If there is a file and we opened it
if (inventory_file)
@ -730,6 +727,17 @@ bool Local_Storage::load_json_file(std::string folder, std::string const&file, n
return false;
}
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
{
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string inv_path = std::move(save_directory + appid + folder);
std::string full_path = inv_path + file;
return load_json(full_path, json);
}
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
{
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {

View File

@ -63,6 +63,7 @@ public:
bool update_save_filenames(std::string folder);
bool load_json(std::string full_path, nlohmann::json& json);
bool load_json_file(std::string folder, std::string const& file, nlohmann::json& json);
bool write_json_file(std::string folder, std::string const& file, nlohmann::json const& json);
};

View File

@ -60,9 +60,7 @@ private:
bool inventory_loaded;
bool call_definition_update;
bool call_inventory_update;
bool definition_update_called;
bool full_update_called;
bool item_definitions_loaded;
struct Steam_Inventory_Requests* new_inventory_result(bool full_query=true, const SteamItemInstanceID_t* pInstanceIDs = NULL, uint32 unCountInstanceIDs = 0)
{
@ -96,29 +94,7 @@ 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());
}
}
local_storage->load_json(items_db_path, defined_items);
}
void read_inventory_db()
@ -129,29 +105,7 @@ void read_inventory_db()
// Try to load a default one
std::string items_db_path = Local_Storage::get_game_settings_path() + items_default_file;
PRINT_DEBUG("Default 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
{
user_items = std::move(nlohmann::json::parse(buffer));
PRINT_DEBUG("Loaded default inventory. Loaded %u items.\n", user_items.size());
}
catch (std::exception& e)
{
PRINT_DEBUG("Error while parsing inventory json: %s\n", e.what());
}
}
local_storage->load_json(items_db_path, user_items);
}
}
@ -175,9 +129,7 @@ Steam_Inventory(class Settings *settings, class SteamCallResults *callback_resul
user_items(nlohmann::json::object()),
inventory_loaded(false),
call_definition_update(false),
call_inventory_update(false),
definition_update_called(false),
full_update_called(false)
item_definitions_loaded(false)
{
this->run_every_runcb->add(&Steam_Inventory::run_every_runcb_cb, this);
}
@ -230,6 +182,7 @@ bool GetResultItems( SteamInventoryResult_t resultHandle,
if (pOutItemsArray != nullptr)
{
SteamItemDetails_t *items_array_base = pOutItemsArray;
uint32 max_items = *punOutItemsArraySize;
if (request->full_query) {
@ -249,29 +202,38 @@ bool GetResultItems( SteamInventoryResult_t resultHandle,
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
}
*punOutItemsArraySize = std::min(*punOutItemsArraySize, static_cast<uint32>(user_items.size()));
} else {
for (auto &itemid : request->instance_ids) {
if (!max_items) break;
pOutItemsArray->m_iDefinition = itemid;
pOutItemsArray->m_itemId = itemid;
try
{
pOutItemsArray->m_unQuantity = user_items[itemid].get<int>();
auto it = user_items.find(std::to_string(itemid));
if (it != user_items.end()) {
pOutItemsArray->m_iDefinition = itemid;
pOutItemsArray->m_itemId = itemid;
try
{
pOutItemsArray->m_unQuantity = it->get<int>();
}
catch (...)
{
pOutItemsArray->m_unQuantity = 0;
}
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
--max_items;
}
catch (...)
{
pOutItemsArray->m_unQuantity = 0;
}
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
--max_items;
}
}
*punOutItemsArraySize = pOutItemsArray - items_array_base;
}
else if (punOutItemsArraySize != nullptr)
{
*punOutItemsArraySize = user_items.size();
if (request->full_query) {
*punOutItemsArraySize = user_items.size();
} else {
*punOutItemsArraySize = std::count_if(request->instance_ids.begin(), request->instance_ids.end(), [this](auto item_id){ return user_items.find(std::to_string(item_id)) != user_items.end();});
}
}
PRINT_DEBUG("GetResultItems good\n");
@ -359,8 +321,6 @@ bool GetAllItems( SteamInventoryResult_t *pResultHandle )
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests* request = new_inventory_result();
call_inventory_update = true;
if (pResultHandle != nullptr)
*pResultHandle = request->inventory_result;
@ -383,7 +343,6 @@ bool GetItemsByID( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT( unC
std::lock_guard<std::recursive_mutex> 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;
}
@ -613,7 +572,7 @@ bool LoadItemDefinitions()
PRINT_DEBUG("LoadItemDefinitions\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!definition_update_called) {
if (!item_definitions_loaded) {
call_definition_update = true;
}
@ -715,6 +674,7 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope
{
*punValueBufferSizeOut = 0;
PRINT_DEBUG("Attr %s not found for item %d\n", pchPropertyName, iDefinition);
return false;
}
}
else // Pass a NULL pointer for pchPropertyName to get a comma - separated list of available property names.
@ -750,8 +710,11 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope
}
}
}
return true;
}
return true;
return false;
}
@ -877,39 +840,37 @@ bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventory
void RunCallbacks()
{
if (call_definition_update && !definition_update_called) {
definition_update_called = true;
read_items_db();
if (call_definition_update || inventory_requests.size()) {
if (!item_definitions_loaded) {
read_items_db();
item_definitions_loaded = true;
//only gets called once
//also gets called when getting items
SteamInventoryDefinitionUpdate_t data = {};
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
SteamInventoryDefinitionUpdate_t data = {};
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
call_definition_update = false;
}
if (call_inventory_update) {
if (inventory_requests.size() && !inventory_loaded) {
read_inventory_db();
inventory_loaded = true;
call_definition_update = true;
call_inventory_update = false;
}
if (definition_update_called && inventory_loaded)
if (inventory_loaded)
{
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
for (auto& r : inventory_requests) {
if (!r.done && std::chrono::duration_cast<std::chrono::duration<double>>(now - r.time_created).count() > r.timeout) {
if (r.full_query) {
if (!full_update_called) {
// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems
// successfully returns a result which is newer / fresher than the last
// known result.
//TODO: should this always be returned for each get all item calls?
struct SteamInventoryFullUpdate_t data;
data.m_handle = r.inventory_result;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
full_update_called = true;
}
// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems
// successfully returns a result which is newer / fresher than the last
// known result.
struct SteamInventoryFullUpdate_t data;
data.m_handle = r.inventory_result;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
{

View File

@ -66,29 +66,7 @@ unsigned int find_leaderboard(std::string name)
void load_achievements_db()
{
std::string file_path = Local_Storage::get_game_settings_path() + achievements_user_file;
std::ifstream achs_file(file_path);
if (achs_file)
{
achs_file.seekg(0, std::ios::end);
size_t size = achs_file.tellg();
std::string buffer(size, '\0');
achs_file.seekg(0);
// Read it entirely, if the .json file gets too big,
// I should look into this and split reads into smaller parts.
achs_file.read(&buffer[0], size);
achs_file.close();
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());
}
local_storage->load_json(file_path, defined_achievements);
}
void load_achievements()
@ -96,6 +74,11 @@ void load_achievements()
local_storage->load_json_file("", achievements_user_file, user_achievements);
}
void save_achievements()
{
local_storage->write_json_file("", achievements_user_file, user_achievements);
}
public:
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks):
settings(settings),
@ -223,6 +206,7 @@ bool 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 ) {
@ -244,16 +228,19 @@ bool SetAchievement( const char *pchName )
{
PRINT_DEBUG("SetAchievement %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;
});
if (it != defined_achievements.end()) {
if (user_achievements[pchName]["earned"] == false) {
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"] = static_cast<uint32>(std::time(nullptr));
user_achievements[pchName]["earned_time"] = std::chrono::duration_cast<std::chrono::duration<uint32>>(std::chrono::system_clock::now().time_since_epoch()).count();
save_achievements();
}
return true;
}
} catch (...) {}
@ -265,6 +252,7 @@ bool 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) {
@ -273,6 +261,7 @@ bool ClearAchievement( const char *pchName )
if (it != defined_achievements.end()) {
user_achievements[pchName]["earned"] = false;
user_achievements[pchName]["earned_time"] = static_cast<uint32>(0);
save_achievements();
return true;
}
} catch (...) {}
@ -288,6 +277,7 @@ bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32
{
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) {
@ -319,8 +309,6 @@ bool StoreStats()
PRINT_DEBUG("StoreStats\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
local_storage->write_json_file("", achievements_user_file, user_achievements);
UserStatsStored_t data;
data.m_nGameID = settings->get_local_game_id().ToUint64();
data.m_eResult = k_EResultOK;
@ -339,6 +327,7 @@ int GetAchievementIcon( const char *pchName )
{
PRINT_DEBUG("GetAchievementIcon\n");
if (pchName == nullptr) return 0;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
@ -353,13 +342,17 @@ const char * GetAchievementDisplayAttribute( const char *pchName, const char *pc
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<std::string>().c_str();
static std::string display_name;
display_name = it.value()["displayName"].get<std::string>();
return display_name.c_str();
}
} catch (...) {}
}
@ -370,7 +363,9 @@ const char * GetAchievementDisplayAttribute( const char *pchName, const char *pc
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["description"].get<std::string>().c_str();
static std::string description;
description = it.value()["description"].get<std::string>();
return description.c_str();
}
} catch (...) {}
}
@ -381,7 +376,9 @@ const char * GetAchievementDisplayAttribute( const char *pchName, const char *pc
return static_cast<std::string const&>(item["name"]) == pchName;
});
if (it != defined_achievements.end()) {
return it.value()["description"].get<std::string>().c_str();
static std::string hidden;
hidden = it.value()["hidden"].get<std::string>();
return hidden.c_str();
}
} catch (...) {}
}
@ -396,7 +393,41 @@ bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint
{
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;
}
@ -405,6 +436,7 @@ bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint
uint32 GetNumAchievements()
{
PRINT_DEBUG("GetNumAchievements\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return defined_achievements.size();
}
@ -413,7 +445,9 @@ const char * GetAchievementName( uint32 iAchievement )
{
PRINT_DEBUG("GetAchievementName\n");
try {
return defined_achievements[iAchievement]["name"].get<std::string>().c_str();
static std::string achievement_name;
achievement_name = defined_achievements[iAchievement]["name"].get<std::string>();
return achievement_name.c_str();
} catch (...) {}
return "";
}
@ -482,6 +516,7 @@ bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchi
{
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);
@ -495,6 +530,7 @@ bool GetUserAchievementAndUnlockTime( CSteamID steamIDUser, const char *pchName,
{
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);
@ -507,6 +543,7 @@ bool GetUserAchievementAndUnlockTime( CSteamID steamIDUser, const char *pchName,
bool 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) {