* a working impl to bridge ugc/remote_storage as suggested by Detanup01

* edits by Kola124 + other changes in the settings parser

* random ugc mod handle at object creation

* file size using std::filesystem + fix warnings + some print + arg validation
This commit is contained in:
otavepto 2024-01-14 19:39:19 +02:00
parent 4afd10219d
commit 0d1e54e9a2
16 changed files with 1084 additions and 293 deletions

View File

@ -73,6 +73,8 @@
#include <string.h>
#include <stdio.h>
#include <filesystem>
#include <optional>
// OS specific includes + definitions
#if defined(__WINDOWS__)
@ -164,6 +166,9 @@ static inline void reset_LastError()
#include "utfcpp/utf8.h"
#include "controller/gamepad.h"
// common includes
#include "common_helpers/common_helpers.hpp"
// Steamsdk includes
#include "steam/steam_api.h"
#include "steam/steam_gameserver.h"

View File

@ -43,14 +43,14 @@ struct image_t
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 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 leaderboard_storage_folder = "leaderboard";
static constexpr auto user_data_storage = "local";
static constexpr auto screenshots_folder = "screenshots";
static constexpr auto game_settings_folder = "steam_settings";
static constexpr auto user_data_storage = "local";
static constexpr auto screenshots_folder = "screenshots";
static constexpr auto game_settings_folder = "steam_settings";
private:
std::string save_directory;

View File

@ -46,8 +46,8 @@ struct Mod_entry {
bool tagsTruncated;
std::string tags;
// file/url information
UGCHandle_t handleFile = k_UGCHandleInvalid;
UGCHandle_t handlePreviewFile = k_UGCHandleInvalid;
UGCHandle_t handleFile = generate_file_handle();
UGCHandle_t handlePreviewFile = generate_file_handle();
std::string primaryFileName;
int32 primaryFileSize;
std::string previewFileName;
@ -59,6 +59,18 @@ struct Mod_entry {
float score;
// collection details
uint32 numChildren;
private:
UGCHandle_t generate_file_handle()
{
static UGCHandle_t val = 0;
++val;
if (val == 0 || val == k_UGCHandleInvalid) val = 1;
return val;
}
};
struct Leaderboard_config {

View File

@ -30,6 +30,7 @@
#include "steam_http.h"
#include "steam_controller.h"
#include "steam_ugc.h"
#include "ugc_remote_storage_bridge.h"
#include "steam_applist.h"
#include "steam_music.h"
#include "steam_musicremote.h"
@ -84,6 +85,8 @@ public:
Local_Storage *local_storage;
RunEveryRunCB *run_every_runcb;
Ugc_Remote_Storage_Bridge *ugc_bridge;
Steam_User *steam_user;
Steam_Friends *steam_friends;
Steam_Utils *steam_utils;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
#ifndef __INCLUDED_UGC_REMOTE_STORAGE_BRIDGE_H__
#define __INCLUDED_UGC_REMOTE_STORAGE_BRIDGE_H__
#include "base.h"
class Ugc_Remote_Storage_Bridge
{
public:
struct QueryInfo {
PublishedFileId_t mod_id; // mod id
bool is_primary_file; // was this query for the primary mod file or preview file
};
private:
// key: UGCHandle_t which is the file handle (primary or preview)
// value: the mod id, true if UGCHandle_t of primary file | false if UGCHandle_t of preview file
std::map<UGCHandle_t, QueryInfo> steam_ugc_queries{};
public:
// called from Steam_UGC::SendQueryUGCRequest() after a successful query
void add_ugc_query_result(UGCHandle_t file_handle, PublishedFileId_t fileid, bool handle_of_primary_file);
bool remove_ugc_query_result(UGCHandle_t file_handle);
std::optional<QueryInfo> get_ugc_query_result(UGCHandle_t file_handle);
~Ugc_Remote_Storage_Bridge();
};
#endif // __INCLUDED_UGC_REMOTE_STORAGE_BRIDGE_H__

View File

@ -157,7 +157,7 @@ void Settings::addMod(PublishedFileId_t id, std::string title, std::string path)
return;
}
Mod_entry new_entry;
Mod_entry new_entry{};
new_entry.id = id;
new_entry.title = title;
new_entry.path = path;
@ -168,7 +168,8 @@ void Settings::addModDetails(PublishedFileId_t id, Mod_entry details)
{
auto f = std::find_if(mods.begin(), mods.end(), [&id](Mod_entry const& item) { return item.id == id; });
if (f != mods.end()) {
f->previewURL = details.previewURL;
// don't copy files handles, they're auto generated
f->fileType = details.fileType;
f->description = details.description;
f->steamIDOwner = details.steamIDOwner;
@ -180,10 +181,6 @@ void Settings::addModDetails(PublishedFileId_t id, Mod_entry details)
f->acceptedForUse = details.acceptedForUse;
f->tagsTruncated = details.tagsTruncated;
f->tags = details.tags;
// - should we set the handles here instead of Invalid?
f->handleFile = details.handleFile;
f->handlePreviewFile = details.handlePreviewFile;
// -
f->primaryFileName = details.primaryFileName;
f->primaryFileSize = details.primaryFileSize;
f->previewFileName = details.previewFileName;
@ -193,6 +190,7 @@ void Settings::addModDetails(PublishedFileId_t id, Mod_entry details)
f->votesDown = details.votesDown;
f->score = details.score;
f->numChildren = details.numChildren;
f->previewURL = details.previewURL;
}
}

View File

@ -779,6 +779,8 @@ static void parse_force_branch_name(class Settings *settings_client, Settings *s
// steam_settings/mods
static void parse_mods_folder(class Settings *settings_client, Settings *settings_server, class Local_Storage *local_storage)
{
std::chrono::system_clock::time_point one_week_ago = std::chrono::system_clock::now() - std::chrono::hours(24 * 7);
auto one_week_ago_epoch = std::chrono::duration_cast<std::chrono::seconds>(one_week_ago.time_since_epoch()).count();
std::string mod_path = Local_Storage::get_game_settings_path() + "mods";
nlohmann::json mod_items = nlohmann::json::object();
static constexpr auto mods_json_file = "mods.json";
@ -786,6 +788,7 @@ static void parse_mods_folder(class Settings *settings_client, Settings *setting
if (local_storage->load_json(mods_json_path, mod_items)) {
for (auto mod = mod_items.begin(); mod != mod_items.end(); ++mod) {
try {
std::string mod_images_folder = Local_Storage::get_game_settings_path() + "mod_images" + PATH_SEPARATOR + std::string(mod.key());
Mod_entry newMod;
newMod.id = std::stoull(mod.key());
newMod.title = mod.value().value("title", std::string(mod.key()));
@ -795,47 +798,90 @@ static void parse_mods_folder(class Settings *settings_client, Settings *setting
}
newMod.fileType = k_EWorkshopFileTypeCommunity;
newMod.description = mod.value().value("description", std::string(""));
newMod.steamIDOwner = mod.value().value("steam_id_owner", (uint64)0);
newMod.timeCreated = mod.value().value("time_created", (uint32)1554997000);
newMod.timeUpdated = mod.value().value("time_updated", (uint32)1554997000);
newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)1554997000);
newMod.steamIDOwner = mod.value().value("steam_id_owner", settings_client->get_local_steam_id().ConvertToUint64());
newMod.timeCreated = mod.value().value("time_created", (uint32)std::chrono::system_clock::now().time_since_epoch().count());
newMod.timeUpdated = mod.value().value("time_updated", (uint32)one_week_ago.time_since_epoch().count());
newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)one_week_ago_epoch);
newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic;
newMod.banned = false;
newMod.acceptedForUse = true;
newMod.tagsTruncated = false;
newMod.tags = mod.value().value("tags", std::string(""));
constexpr static auto get_file_size = [](
const std::string &filepath,
const std::string &basepath,
int32 default_val = 0) -> size_t {
try
{
const auto file_p = common_helpers::to_absolute(filepath, basepath);
if (file_p.empty()) return default_val;
size_t size = 0;
if (common_helpers::file_size(file_p, size)) {
return size;
}
} catch(...) {}
return default_val;
};
newMod.primaryFileName = mod.value().value("primary_filename", std::string(""));
if(newMod.primaryFileName!=""){
long begin = 0, end = 0;
const char* name = newMod.primaryFileName.c_str();
std::fstream file(name);
begin = file.tellg();
file.seekg(0, std::ios::end);
end = file.tellg();
file.close();
newMod.primaryFileSize = mod.value().value("primary_filesize", (end-begin));
}
else
{
newMod.primaryFileSize = mod.value().value("primary_filesize", (int32)1000000);
int32 primary_filesize = 0;
if (!newMod.primaryFileName.empty()) {
primary_filesize = (int32)get_file_size(newMod.primaryFileName, newMod.path, primary_filesize);
}
newMod.primaryFileSize = mod.value().value("primary_filesize", primary_filesize);
newMod.previewFileName = mod.value().value("preview_filename", std::string(""));
newMod.previewFileSize = mod.value().value("preview_filesize", (int32)1000000);
newMod.workshopItemURL = mod.value().value("workshop_item_url", std::string(""));
int32 preview_filesize = 0;
if (!newMod.previewFileName.empty()) {
preview_filesize = (int32)get_file_size(
newMod.previewFileName,
mod_images_folder,
preview_filesize);
}
newMod.previewFileSize = mod.value().value("preview_filesize", preview_filesize);
newMod.workshopItemURL = mod.value().value("workshop_item_url", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + std::string(mod.key()));
newMod.votesUp = mod.value().value("upvotes", (uint32)1);
newMod.votesDown = mod.value().value("downvotes", (uint32)0);
newMod.score = mod.value().value("score", 1.0f);
float score = 1.0f;
try
{
score = newMod.votesUp / (float)(newMod.votesUp + newMod.votesDown);
} catch(...) {}
newMod.score = mod.value().value("score", score);
newMod.numChildren = mod.value().value("num_children", (uint32)0);
newMod.previewURL = mod.value().value("preview_url", std::string(""));
if (newMod.previewURL.empty()) {
newMod.previewURL = newMod.previewFileName.empty()
? ""
: "file://" + Local_Storage::get_game_settings_path() + "mod_images/" + newMod.previewFileName;
if (newMod.previewFileName.empty()) {
newMod.previewURL = std::string();
} else {
auto settings_folder = std::string(Local_Storage::get_game_settings_path());
std::replace(settings_folder.begin(), settings_folder.end(), '\\', '/');
newMod.previewURL = "file://" + settings_folder + "mod_images/" + std::string(mod.key()) + "/" + newMod.previewFileName;
}
}
settings_client->addMod(newMod.id, newMod.title, newMod.path);
settings_server->addMod(newMod.id, newMod.title, newMod.path);
settings_client->addModDetails(newMod.id, newMod);
settings_server->addModDetails(newMod.id, newMod);
PRINT_DEBUG(" parsed mod '%s':\n", std::string(mod.key()).c_str());
PRINT_DEBUG(" path (will be used for primary file): '%s'\n", newMod.path.c_str());
PRINT_DEBUG(" images path (will be used for preview file): '%s'\n", mod_images_folder.c_str());
PRINT_DEBUG(" primary_filename: '%s'\n", newMod.primaryFileName.c_str());
PRINT_DEBUG(" primary_filesize: %i bytes\n", newMod.primaryFileSize);
PRINT_DEBUG(" primary file handle: %llu\n", settings_client->getMod(newMod.id).handleFile);
PRINT_DEBUG(" preview_filename: '%s'\n", newMod.previewFileName.c_str());
PRINT_DEBUG(" preview_filesize: %i bytes\n", newMod.previewFileSize);
PRINT_DEBUG(" preview file handle: %llu\n", settings_client->getMod(newMod.id).handlePreviewFile);
PRINT_DEBUG(" workshop_item_url: '%s'\n", newMod.workshopItemURL.c_str());
PRINT_DEBUG(" preview_url: '%s'\n", newMod.previewURL.c_str());
} catch (std::exception& e) {
PRINT_DEBUG("MODLOADER ERROR: %s\n", e.what());
}
@ -852,18 +898,18 @@ static void parse_mods_folder(class Settings *settings_client, Settings *setting
newMod.fileType = k_EWorkshopFileTypeCommunity;
newMod.description = "";
newMod.steamIDOwner = (uint64)0;
newMod.timeCreated = (uint32)1554997000;
newMod.timeUpdated = (uint32)1554997000;
newMod.timeAddedToUserList = (uint32)1554997000;
newMod.timeCreated = (uint32)one_week_ago_epoch;
newMod.timeUpdated = (uint32)one_week_ago_epoch;
newMod.timeAddedToUserList = (uint32)one_week_ago_epoch;
newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic;
newMod.banned = false;
newMod.acceptedForUse = true;
newMod.tagsTruncated = false;
newMod.tags = "";
newMod.primaryFileName = "";
newMod.primaryFileSize = (int32)1000000;
newMod.primaryFileSize = (int32)0;
newMod.previewFileName = "";
newMod.previewFileSize = (int32)1000000;
newMod.previewFileSize = (int32)0;
newMod.workshopItemURL = "";
newMod.votesUp = (uint32)1;
newMod.votesDown = (uint32)0;

View File

@ -74,16 +74,18 @@ Steam_Client::Steam_Client()
steam_friends = new Steam_Friends(settings_client, network, callback_results_client, callbacks_client, run_every_runcb, steam_overlay);
steam_utils = new Steam_Utils(settings_client, callback_results_client, steam_overlay);
ugc_bridge = new Ugc_Remote_Storage_Bridge();
steam_matchmaking = new Steam_Matchmaking(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_matchmaking_servers = new Steam_Matchmaking_Servers(settings_client, network);
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client, steam_overlay);
steam_apps = new Steam_Apps(settings_client, callback_results_client);
steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb);
steam_remote_storage = new Steam_Remote_Storage(settings_client, local_storage, callback_results_client);
steam_remote_storage = new Steam_Remote_Storage(settings_client, ugc_bridge, local_storage, callback_results_client);
steam_screenshots = new Steam_Screenshots(local_storage, callbacks_client);
steam_http = new Steam_HTTP(settings_client, network, callback_results_client, callbacks_client);
steam_controller = new Steam_Controller(settings_client, callback_results_client, callbacks_client, run_every_runcb);
steam_ugc = new Steam_UGC(settings_client, callback_results_client, callbacks_client);
steam_ugc = new Steam_UGC(settings_client, ugc_bridge, callback_results_client, callbacks_client);
steam_applist = new Steam_Applist();
steam_music = new Steam_Music(callbacks_client);
steam_musicremote = new Steam_MusicRemote();
@ -109,7 +111,7 @@ Steam_Client::Steam_Client()
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, local_storage);
steam_gameserver_ugc = new Steam_UGC(settings_server, callback_results_server, callbacks_server);
steam_gameserver_ugc = new Steam_UGC(settings_server, ugc_bridge, 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, steam_networking_sockets->get_shared_between_client_server());
steam_gameserver_networking_sockets_serialized = new Steam_Networking_Sockets_Serialized(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);

View File

@ -0,0 +1,33 @@
#include "dll/ugc_remote_storage_bridge.h"
void Ugc_Remote_Storage_Bridge::add_ugc_query_result(UGCHandle_t file_handle, PublishedFileId_t fileid, bool handle_of_primary_file)
{
std::lock_guard lock(global_mutex);
steam_ugc_queries[file_handle].mod_id = fileid;
steam_ugc_queries[file_handle].is_primary_file = handle_of_primary_file;
}
bool Ugc_Remote_Storage_Bridge::remove_ugc_query_result(UGCHandle_t file_handle)
{
std::lock_guard lock(global_mutex);
return !!steam_ugc_queries.erase(file_handle);
}
std::optional<Ugc_Remote_Storage_Bridge::QueryInfo> Ugc_Remote_Storage_Bridge::get_ugc_query_result(UGCHandle_t file_handle)
{
std::lock_guard lock(global_mutex);
auto it = steam_ugc_queries.find(file_handle);
if (steam_ugc_queries.end() == it) return std::nullopt;
return it->second;
}
Ugc_Remote_Storage_Bridge::~Ugc_Remote_Storage_Bridge()
{
std::lock_guard lock(global_mutex);
steam_ugc_queries.clear();
}

View File

@ -163,7 +163,7 @@ std::wstring common_helpers::to_absolute(const std::wstring &path, const std::ws
return path_abs.wstring();
}
bool common_helpers::file_exist(std::filesystem::path &filepath)
bool common_helpers::file_exist(const std::filesystem::path &filepath)
{
if (std::filesystem::is_directory(filepath)) {
return false;
@ -188,7 +188,28 @@ bool common_helpers::file_exist(const std::wstring &filepath)
return file_exist(path);
}
bool common_helpers::dir_exist(std::filesystem::path &dirpath)
bool common_helpers::file_size(const std::filesystem::path &filepath, size_t &size)
{
if (common_helpers::file_exist(filepath)) {
size = std::filesystem::file_size(filepath);
return true;
}
return false;
}
bool common_helpers::file_size(const std::string &filepath, size_t &size)
{
const auto file_p = std::filesystem::path(filepath);
return file_size(file_p, size);
}
bool common_helpers::file_size(const std::wstring &filepath, size_t &size)
{
const auto file_p = std::filesystem::path(filepath);
return file_size(file_p, size);
}
bool common_helpers::dir_exist(const std::filesystem::path &dirpath)
{
if (std::filesystem::is_directory(dirpath)) {
return true;

View File

@ -36,13 +36,19 @@ std::string to_absolute(const std::string &path, const std::string &base = std::
std::wstring to_absolute(const std::wstring &path, const std::wstring &base = std::wstring());
bool file_exist(std::filesystem::path &filepath);
bool file_exist(const std::filesystem::path &filepath);
bool file_exist(const std::string &filepath);
bool file_exist(const std::wstring &filepath);
bool dir_exist(std::filesystem::path &dirpath);
bool file_size(const std::filesystem::path &filepath, size_t &size);
bool file_size(const std::string &filepath, size_t &size);
bool file_size(const std::wstring &filepath, size_t &size);
bool dir_exist(const std::filesystem::path &dirpath);
bool dir_exist(const std::string &dirpath);

View File

@ -0,0 +1 @@
Put here the files whose names are specified by the JSON key "preview_filename" inside mods.json

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -12,8 +12,8 @@
"preview_filename": "test.png",
"preview_filesize": 1000000,
"workshop_item_url": "https://steamcommunity.com/sharedfiles/filedetails/?id=111111111",
"upvotes": 1,
"downvotes": 0,
"upvotes": 10,
"downvotes": 1,
"num_children": 0,
"path": "C:\\games\\my_game\\steam_settings\\mods_data\\mod_111111111_data_folder",
"preview_url": "file://C:/games/my_game/steam_settings/mod_images/my_preview.jpg",