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

@ -69,8 +69,8 @@ If you want to set custom ips (or domains) which the emulator will send broadcas
If the custom ips/domains are specific for one game only you can put the custom_broadcasts.txt in the steam_settings\ folder.
An example is provided in steam_settings.EXAMPLE\custom_broadcasts.EXAMPLE.txt
Items or Inventory:
Create a folder named steam_settings right beside steam_api.dll if there isn't one already. In that folder, create a file named items.json which will contain every item you want to have in your game.
Achievements, Items or Inventory:
Create a folder named steam_settings right beside steam_api.dll if there isn't one already. In that folder, create a file named items.json and/or achievements.json which will contain every item/achievement you want to have in your game.
An example can be found in steam_settings.EXAMPLE that works with Killing Floor 2.
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.

0
build_env_x64.bat Normal file → Executable file
View File

0
build_env_x86.bat Normal file → Executable file
View File

0
build_linux.sh Normal file → Executable file
View File

0
build_set_protobuf_directories.bat Normal file → Executable file
View File

0
build_steamos.sh Normal file → Executable file
View File

0
build_win_debug_experimental.bat Normal file → Executable file
View File

0
build_win_find_interfaces.bat Normal file → Executable file
View File

0
build_win_lobby_connect.bat Normal file → Executable file
View File

0
build_win_release.bat Normal file → Executable file
View File

0
build_win_release_experimental.bat Normal file → Executable file
View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
{
"2001": 1,
"2002": 1
}

View File

@ -0,0 +1,456 @@
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <cstdint>
#include <fstream>
#include <iomanip>
#include <curl/curl.h>
#include <json/json.hpp>
class CurlGlobal
{
bool _init;
CurlGlobal() :_init(false) {}
~CurlGlobal() { cleanup(); }
public:
static CurlGlobal& Inst()
{
static CurlGlobal _this;
return _this;
}
CURLcode init(long flags = CURL_GLOBAL_DEFAULT) { return curl_global_init(flags); }
void cleanup()
{
if (_init)
{
curl_global_cleanup();
_init = false;
}
}
};
class CurlEasy
{
CURL* _me;
bool _init;
std::string _buffer;
static int writer(char* data, size_t size, size_t nmemb,
CurlEasy *_this)
{
if (_this == nullptr)
return 0;
_this->_buffer.append(data, size * nmemb);
return size * nmemb;
}
public:
CurlEasy() :_me(nullptr), _init(false) {}
~CurlEasy() { cleanup(); }
bool init()
{
_init = (_me = curl_easy_init()) != nullptr;
if (_init)
{
if (curl_easy_setopt(_me, CURLOPT_WRITEFUNCTION, writer) != CURLE_OK)
{
cleanup();
return false;
}
if (curl_easy_setopt(_me, CURLOPT_WRITEDATA, this) != CURLE_OK)
{
cleanup();
return false;
}
}
return _init;
}
void cleanup()
{
if (_init)
{
curl_easy_cleanup(_me);
}
}
CURLcode set_url(const std::string& url)
{
return curl_easy_setopt(_me, CURLOPT_URL, url.c_str());
}
CURLcode skip_verifypeer(bool skip = true)
{
return curl_easy_setopt(_me, CURLOPT_SSL_VERIFYPEER, skip ? 0L : 1L);
}
CURLcode skip_verifhost(bool skip = true)
{
return curl_easy_setopt(_me, CURLOPT_SSL_VERIFYHOST, skip ? 0L : 1L);
}
CURLcode connect_only(bool connect = true)
{
return curl_easy_setopt(_me, CURLOPT_CONNECT_ONLY, connect ? 1L : 0L);
}
CURLcode perform()
{
_buffer.clear();
return curl_easy_perform(_me);
}
CURLcode recv(void *buffer, size_t buflen, size_t* read_len)
{
return curl_easy_recv(_me, buffer, buflen, read_len);
}
CURLcode get_html_code(long &code)
{
return curl_easy_getinfo(_me, CURLINFO_RESPONSE_CODE, &code);
}
std::string const& get_answer() const { return _buffer; }
};
// Get all steam appid with their name: http://api.steampowered.com/ISteamApps/GetAppList/v2/
// Steam storefront webapi: https://wiki.teamfortress.com/wiki/User:RJackson/StorefrontAPI
// http://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2/?key=<key>&appid=<appid>
/*
{
"game" : {
"gameName" : "<name>",
"availableGameStats" : {
"achievements" : {
("<id>" : {
"name" : "achievement_name",
"displayName" : "achievement name on screen",
"hidden" : (0|1),
["description" : "<desc>",]
"icon" : "<url to icon when achievement is earned>",
"icongray" : "<url to icon when achievement is not earned>"
},
...)
}
}
}
}
*/
// Get appid infos: http://store.steampowered.com/api/appdetails/?appids=218620
/*
"appid" : {
"success" : (true|false),
(success == true "data" : {
...
"name" : "<name>",
"steam_appid" : <appid>,
(OPT "dlc" : [<dlc id>, <dlc id>]),
"header_image" : "<miniature url>" <-- Use this in the overlay ?
(OPT "achievements" : {
"total" : <num of achievements>
}),
"background" : "<background url>" <-- Use this as the overlay background ?
(OPT "packages" : [<package id>, <package id>])
})
}
*/
// ---------------------------------
// -- Special thanks to psychonic --
// ---------------------------------
// Get game items definition digest (Phase1): https://api.steampowered.com/IInventoryService/GetItemDefMeta/v1?key=<webapi_key>&appid=218620
/*
{
"response": {
"modified": 1566848385,
"digest": "3CDFC1CC1AC2B0D55D12C1C130F4294BDD6DF8D0"
}
}
*/
// Get game items definition: https://api.steampowered.com/IGameInventory/GetItemDefArchive/v0001?appid=218620&digest=<digest>
/*
[
{
"appid":"218620",
"itemdefid":"0",
"Timestamp":"2016-04-08T18:00:21.3643085Z",
"modified":"20160408T180021Z",
"date_created":"20160408T180021Z",
"type":"",
"display_type":"",
"name":"",
"quantity":0,
"description":"",
"tradable":false,
"marketable":false,
"commodity":false,
"drop_interval":0,
"drop_max_per_window":0,
"workshopid":"0"
},
{
"appid":"218620",
"itemdefid":"50002",
"Timestamp":"2015-11-13T16:01:18.0338618Z",
"modified":"20151113T160117Z",
"date_created":"20151113T160117Z",
"type":"item",
"display_type":"",
"name":"Sputnik Safe",
"quantity":0,
"description":"[color=#2360D8]THE JUDGE SHOTGUN | Pixel [/color]\n[color=#2360D8]KOBUS 90 SUBMACHINE GUN | Red Stars[/color]\n[color=#2360D8]PLAINSRIDER BOW | Arctic Plains[/color]\n[color=#2360D8]GRUBER KURZ PISTOL | Little Leopard[/color]\n[color=#2360D8]HRL-7 ROCKET LAUNCHER | Headline[/color]\n[color=#2360D8]LOCOMOTIVE 12G SHOTGUN | Cosmonaut[/color]\n[color=#9900FF]FLAMETHROWER | St. Basil[/color]\n[color=#9900FF]JP36 RIFLE | Ice Leopard [/color]\n[color=#9900FF]CAR-4 RIFLE | Stripe On[/color]\n[color=#9900FF]BRONCO .44 REVOLVER | Black Bull[/color]\n[color=#FF00FF]BERNETTI 9 PISTOL | Angry Bear[/color]\n[color=#FF00FF]THANATOS .50 CAL SNIPER RIFLE | Matrjoschka[/color]\n[color=#FF00FF]M308 RIFLE | Helmet Space Program[/color]\n[color=#FF0000]CLARION RIFLE | Breaching Owl[/color]\n[color=#FF0000]MOSCONI 12G SHOTGUN | Bullet Bear Gun[/color]\n[color=#FFAA00]or an exceedingly rare special item![/color]",
"icon_url":"http://media.overkillsoftware.com/economy42gF2Y/safes_weapon_01.png",
"icon_url_large":"http://media.overkillsoftware.com/economy42gF2Y/safes_weapon_01.png",
"store_tags":"safe;sputnik safe;",
"tradable":true,
"marketable":true,
"commodity":false,
"drop_interval":0,
"drop_max_per_window":0,
"workshopid":"0",
"dsl_bonus":"false",
"item_name":"weapon_01",
"item_slot":"safes"
}
*/
#ifdef max
#undef max
#endif
std::string steam_apikey;
std::string app_id;
#if defined(WIN32) || defined(_WIN32)
#include <windows.h>
static bool create_directory(std::string const& strPath)
{
DWORD dwAttrib = GetFileAttributesA(strPath.c_str());
if (dwAttrib != INVALID_FILE_ATTRIBUTES && dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
return true;
return CreateDirectoryA(strPath.c_str(), NULL);
}
#elif defined(__linux__)
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
static bool create_directory(std::string const& strPath)
{
struct stat sb;
if (stat(strPath.c_str(), &sb) != 0)
{
return mkdir(strPath.c_str(), 0755) == 0;
}
if (S_ISDIR(sb.st_mode))
return true;
return false;
}
#endif
static void generate_achievements(CurlEasy &easy)
{
std::string url = "http://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2/?key=";
url += steam_apikey;
url += "&appid=";
url += app_id;
easy.set_url(url);
easy.perform();
try
{
std::ofstream ach_file("achievements.json", std::ios::trunc | std::ios::out);
nlohmann::json json = nlohmann::json::parse(easy.get_answer());
nlohmann::json output_json = nlohmann::json::array();
bool first = true;
int i = 0;
for (auto& item : json["game"]["availableGameStats"]["achievements"].items())
{
output_json[i]["name"] = item.value()["name"];
output_json[i]["displayName"] = item.value()["displayName"];
output_json[i]["hidden"] = std::to_string(item.value()["hidden"].get<int>());
try
{
if( !item.value()["description"].is_null() )
output_json[i]["description"] = item.value()["description"];
else
output_json[i]["description"] = "";
}
catch (...)
{
output_json[i]["description"] = "";
}
{
std::string icon_path = "images/" + item.value()["name"].get<std::string>() + ".jpg";
std::ofstream achievement_icon(icon_path, std::ios::out | std::ios::trunc | std::ios::binary);
if (!achievement_icon)
{
std::cerr << "Cannot create achievement icon \"" << icon_path << "\"" << std::endl;
return;
}
easy.set_url(item.value()["icon"]);
easy.perform();
std::string picture = easy.get_answer();
achievement_icon.write(picture.c_str(), picture.length());
output_json[i]["icon"] = icon_path;
}
{
std::string icon_path = "images/" + item.value()["name"].get<std::string>() + "_gray.jpg";
std::ofstream achievement_icon(icon_path, std::ios::out | std::ios::trunc | std::ios::binary);
if (!achievement_icon)
{
std::cerr << "Cannot create achievement icon \"" << icon_path << "\"" << std::endl;
return;
}
easy.set_url(item.value()["icongray"]);
easy.perform();
std::string picture = easy.get_answer();
achievement_icon.write(picture.c_str(), picture.length());
output_json[i]["icongray"] = icon_path;
}
++i;
}
ach_file << std::setw(2) << output_json;
}
catch (std::exception& e)
{
std::cerr << "Failed to get infos: ";
long code;
if (easy.get_html_code(code) == CURLE_OK && code == 403)
{
std::cerr << "Error in webapi key";
}
else
{
std::cerr << "Error while parsing json. Try to go at " << url << " and see what you can do to build your achivements.json";
}
std::cerr << std::endl;
}
}
static void generate_items(CurlEasy& easy)
{
std::string url = "https://api.steampowered.com/IInventoryService/GetItemDefMeta/v1?key=";
url += steam_apikey;
url += "&appid=";
url += app_id;
easy.set_url(url);
easy.perform();
try
{
nlohmann::json json = nlohmann::json::parse(easy.get_answer());
std::string digest = json["response"]["digest"];
url = "https://api.steampowered.com/IGameInventory/GetItemDefArchive/v0001?appid=";
url += app_id;
url += "&digest=";
url += digest;
easy.set_url(url);
easy.perform();
nlohmann::json item_json = nlohmann::json::object();
json = nlohmann::json::parse(easy.get_answer());
std::ofstream items_file("items.json", std::ios::trunc | std::ios::out);
for (auto &i : json)
{
for (auto j = i.begin(); j != i.end(); ++j)
{
//if (j.key() == "itemdefid")
//{
// j.value() = std::stoll(j.value().get<std::string>());
//}
//else
{
nlohmann::json& v = j.value();
switch (v.type())
{
case nlohmann::json::value_t::boolean:
v = (v.get<bool>() ? "true" : "false");
break;
case nlohmann::json::value_t::number_float:
v = std::to_string(v.get<double>());
break;
case nlohmann::json::value_t::number_integer:
v = std::to_string(v.get<int64_t>());
break;
case nlohmann::json::value_t::number_unsigned:
v = std::to_string(v.get<uint64_t>());
break;
}
}
}
item_json[i["itemdefid"].get<std::string>()] = i;
}
items_file << std::setw(2) << item_json;
}
catch (std::exception& e)
{
std::cerr << "Failed to get infos: ";
long code;
if (easy.get_html_code(code) == CURLE_OK && code == 403)
{
std::cerr << "Error in webapi key";
}
else
{
std::cerr << "Error while parsing json. Try to go at " << url << " and see what you can do to build your items.json";
}
std::cerr << std::endl;
}
}
int main()
{
if (!create_directory("images"))
{
std::cerr << "Cannot create directory \"images\"" << std::endl;
return -1;
}
CurlGlobal& cglobal = CurlGlobal::Inst();
cglobal.init();
CurlEasy easy;
if (easy.init())
{
std::cout << "Enter the game appid: ";
std::cin >> app_id;
std::cout << "Enter your webapi key: ";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin >> steam_apikey;
generate_achievements(easy);
generate_items(easy);
}
}