Merge branch 'achievements_support' of gitlab.com:Nemirtingas/goldberg_emulator

This commit is contained in:
Mr_Goldberg 2019-10-19 11:58:29 -04:00
commit 815e3de361
No known key found for this signature in database
GPG key ID: 8597D87419DEF278
24 changed files with 3013 additions and 203 deletions

View file

@ -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
<http://www.gnu.org/licenses/>. */
#include "item_db_loader.h"
#include <fstream>
#include "../json/json.hpp"
void read_items_db(std::string items_db, std::map<SteamItemDef_t, std::map<std::string, std::string>> *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<SteamItemDef_t, std::map<std::string, std::string>> 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;
}

View file

@ -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 <atomic>
void read_items_db(std::string items_db, std::map<SteamItemDef_t, std::map<std::string, std::string>> *items, std::atomic_bool *is_loaded);
#endif//__ITEM_DB_LOADER_INCLUDED__

View file

@ -20,6 +20,7 @@
#include <fstream>
#include <algorithm>
#include <iterator>
#include <iomanip>
struct File_Data {
std::string name;
@ -127,6 +128,16 @@ 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)
{
return true;
}
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
{
return true;
}
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
{
return std::vector<std::string>();
@ -378,7 +389,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 +519,7 @@ 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::vector<std::string> Local_Storage::get_filenames_path(std::string path)
@ -680,4 +691,65 @@ 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)
{
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)
{
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 json \"%s\". Loaded %u items.\n", full_path.c_str(), json.size());
return true;
} catch (std::exception& e) {
PRINT_DEBUG("Error while parsing \"%s\" json: %s\n", full_path.c_str(), e.what());
}
}
else
{
PRINT_DEBUG("Couldn't open file \"%s\" to read json\n", full_path.c_str());
}
return false;
}
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& 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::ofstream inventory_file(full_path, 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 json\n", full_path.c_str());
return false;
}
#endif

View file

@ -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 <string>
#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:
@ -59,6 +62,9 @@ public:
std::string get_path(std::string folder);
bool update_save_filenames(std::string folder);
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);
};
#endif

View file

@ -217,7 +217,7 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
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;

View file

@ -43,7 +43,6 @@ Steam_Client::Steam_Client()
{
uint32 appid = create_localstorage_settings(&settings_client, &settings_server, &local_storage);
std::string items_db_file_path = (Local_Storage::get_game_settings_path() + "items.json");
network = new Networking(settings_server->get_local_steam_id(), appid, settings_server->get_port(), &(settings_server->custom_broadcasts), settings_server->disable_networking);
@ -72,7 +71,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);
@ -90,7 +89,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);

View file

@ -15,8 +15,8 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "item_db_loader.h"
#include <thread>
#include "base.h" // For SteamItemDef_t
#include "../json/json.hpp"
struct Steam_Inventory_Requests {
double timeout = 0.1;
@ -42,22 +42,25 @@ class Steam_Inventory :
public ISteamInventory002,
public ISteamInventory
{
public:
static constexpr auto items_user_file = "items.json";
static constexpr auto items_default_file = "default_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<struct Steam_Inventory_Requests> inventory_requests;
std::map<SteamItemDef_t, std::map<std::string, std::string>> items;
// Like typedefs
using item_iterator = std::map<SteamItemDef_t, std::map<std::string, std::string>>::iterator;
using attr_iterator = std::map<std::string, std::string>::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 +92,69 @@ 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()
{
// If we havn't got any inventory
if (!local_storage->load_json_file("", items_user_file, user_items))
{
// 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());
}
}
}
}
public:
static void run_every_runcb_cb(void *object)
@ -99,21 +165,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 +226,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 +234,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<int>();
}
catch (...)
{
pOutItemsArray->m_unQuantity = 0;
}
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
}
*punOutItemsArraySize = std::min(*punOutItemsArraySize, static_cast<uint32>(items.size()));
*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;
pOutItemsArray->m_unQuantity = 1;
try
{
pOutItemsArray->m_unQuantity = user_items[itemid].get<int>();
}
catch (...)
{
pOutItemsArray->m_unQuantity = 0;
}
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
--max_items;
@ -190,7 +271,7 @@ bool GetResultItems( SteamInventoryResult_t resultHandle,
}
else if (punOutItemsArraySize != nullptr)
{
*punOutItemsArraySize = items.size();
*punOutItemsArraySize = user_items.size();
}
PRINT_DEBUG("GetResultItems good\n");
@ -278,7 +359,7 @@ bool GetAllItems( SteamInventoryResult_t *pResultHandle )
std::lock_guard<std::recursive_mutex> 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 +383,7 @@ 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;
}
@ -559,15 +641,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 +670,39 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope
PRINT_DEBUG("GetItemDefinitionProperty %i %s\n", iDefinition, pchPropertyName);
std::lock_guard<std::recursive_mutex> 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<std::string>();
}
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);
*punValueBufferSizeOut = std::min(static_cast<uint32>(val.length() + 1), *punValueBufferSizeOut);
}
else
{
// Set punValueBufferSizeOut to the property size
*punValueBufferSizeOut = val.length() + 1;
}
// Set punValueBufferSizeOut to the property size
*punValueBufferSizeOut = std::min(static_cast<uint32>(val.length() + 1), *punValueBufferSizeOut);
if (pchValueBuffer != nullptr)
{
@ -628,8 +724,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 +733,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<uint32>(i->first.length()));
uint32 x = std::min(len, static_cast<uint32>(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 +877,27 @@ 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 && !definition_update_called) {
definition_update_called = true;
read_items_db();
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_definition_update = 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<std::chrono::duration<double>>(now - r.time_created).count() > r.timeout) {
if (r.full_query) {
if (!full_update_called) {

View file

@ -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
@ -88,7 +88,7 @@ bool FileWrite( const char *pchFile, const void *pvData, int32 cubData )
}
std::lock_guard<std::recursive_mutex> 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;
}
@ -97,7 +97,7 @@ int32 FileRead( const char *pchFile, void *pvData, int32 cubDataToRead )
{
PRINT_DEBUG("Steam_Remote_Storage::FileRead %s %i\n", pchFile, cubDataToRead);
std::lock_guard<std::recursive_mutex> 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;
@ -112,8 +112,7 @@ SteamAPICall_t FileWriteAsync( const char *pchFile, const void *pvData, uint32 c
}
std::lock_guard<std::recursive_mutex> 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 = success ? k_EResultOK : k_EResultFail;
@ -127,7 +126,7 @@ SteamAPICall_t FileReadAsync( const char *pchFile, uint32 nOffset, uint32 cubToR
PRINT_DEBUG("Steam_Remote_Storage::FileReadAsync\n");
std::lock_guard<std::recursive_mutex> 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) {
@ -162,7 +161,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;
@ -184,7 +183,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 )
@ -193,7 +192,7 @@ SteamAPICall_t FileShare( const char *pchFile )
PRINT_DEBUG("Steam_Remote_Storage::FileShare\n");
std::lock_guard<std::recursive_mutex> 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);
@ -246,7 +245,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;
}
@ -267,25 +266,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 )
@ -299,7 +298,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;
}
@ -308,7 +307,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 {
@ -381,7 +380,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];
@ -435,7 +434,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);

View file

@ -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;

View file

@ -17,13 +17,16 @@
#include "base.h"
#include <iomanip>
#include <fstream>
#include "../json/json.hpp"
struct Steam_Leaderboard {
std::string name;
ELeaderboardSortMethod sort_method;
ELeaderboardDisplayType display_type;
};
class Steam_User_Stats :
public ISteamUserStats003,
public ISteamUserStats004,
@ -35,12 +38,20 @@ 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<struct Steam_Leaderboard> leaderboards;
nlohmann::json defined_achievements;
nlohmann::json user_achievements;
unsigned int find_leaderboard(std::string name)
{
unsigned index = 1;
@ -52,13 +63,50 @@ unsigned int find_leaderboard(std::string name)
return 0;
}
public:
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
void load_achievements_db()
{
this->local_storage = local_storage;
this->settings = settings;
this->callback_results = callback_results;
this->callbacks = callbacks;
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());
}
}
void load_achievements()
{
local_storage->load_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),
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
}
// Ask the server to send down this user's data and achievements for this game
@ -89,7 +137,7 @@ bool GetStat( const char *pchName, int32 *pData )
if (stats_data->second.type != Stat_Type::STAT_TYPE_INT) return false;
}
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;
@ -112,7 +160,7 @@ bool GetStat( const char *pchName, float *pData )
if (stats_data->second.type == Stat_Type::STAT_TYPE_INT) return false;
}
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(float))
return true;
@ -132,7 +180,7 @@ bool SetStat( const char *pchName, int32 nData )
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> 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 )
@ -141,7 +189,7 @@ bool SetStat( const char *pchName, float fData )
if (!pchName) return false;
std::lock_guard<std::recursive_mutex> 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 )
@ -150,7 +198,7 @@ bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dS
std::lock_guard<std::recursive_mutex> 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)) {
@ -166,28 +214,69 @@ 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);
*pbAchieved = false;
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;
});
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 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[pchName]["earned"] == false) {
user_achievements[pchName]["earned"] = true;
user_achievements[pchName]["earned_time"] = static_cast<uint32>(std::time(nullptr));
}
return true;
}
} catch (...) {}
return false;
}
bool ClearAchievement( const char *pchName )
{
PRINT_DEBUG("ClearAchievement %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 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;
}
@ -198,7 +287,22 @@ bool ClearAchievement( const char *pchName )
bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
{
PRINT_DEBUG("GetAchievementAndUnlockTime\n");
*pbAchieved = false;
if (pchName == nullptr) return false;
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;
}
@ -215,6 +319,8 @@ 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;
@ -232,6 +338,8 @@ bool StoreStats()
int GetAchievementIcon( const char *pchName )
{
PRINT_DEBUG("GetAchievementIcon\n");
if (pchName == nullptr) return 0;
return 0;
}
@ -242,18 +350,40 @@ int GetAchievementIcon( const char *pchName )
const char * GetAchievementDisplayAttribute( const char *pchName, const char *pchKey )
{
PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey);
return ""; //TODO
if (pchName == nullptr) return "";
if (pchKey == nullptr) return "";
if (strcmp (pchKey, "name") == 0) {
return "Achievement Name";
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();
}
} catch (...) {}
}
if (strcmp (pchKey, "desc") == 0) {
return "Achievement Description";
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<std::string>().c_str();
}
} catch (...) {}
}
if (strcmp (pchKey, "hidden") == 0) {
return "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<std::string>().c_str();
}
} catch (...) {}
}
return "";
@ -265,6 +395,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;
}
@ -273,13 +405,16 @@ bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint
uint32 GetNumAchievements()
{
PRINT_DEBUG("GetNumAchievements\n");
return 0;
return defined_achievements.size();
}
// Get achievement name iAchievement in [0,GetNumAchievements)
const char * GetAchievementName( uint32 iAchievement )
{
PRINT_DEBUG("GetAchievementName\n");
try {
return defined_achievements[iAchievement]["name"].get<std::string>().c_str();
} catch (...) {}
return "";
}
@ -296,6 +431,12 @@ SteamAPICall_t 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;
@ -308,6 +449,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<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
@ -322,6 +465,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<std::recursive_mutex> lock(global_mutex);
if (steamIDUser == settings->get_local_steam_id()) {
@ -336,6 +481,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;
}
@ -343,6 +494,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;
}
@ -352,6 +508,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;
}