Merge pull request #103 from otavepto/patch-mods-access-tags-count-fix

another fix for accessing mod details struct + querying tags count
This commit is contained in:
Detanup01 2024-12-01 19:56:40 +01:00 committed by GitHub
commit 5ec0435a45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 119 additions and 80 deletions

View File

@ -82,7 +82,7 @@ private:
std::optional<std::string> get_query_ugc_tag(UGCQueryHandle_t handle, uint32 index, uint32 indexTag); std::optional<std::string> get_query_ugc_tag(UGCQueryHandle_t handle, uint32 index, uint32 indexTag);
std::optional<std::vector<std::string>> get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index); std::vector<std::string> get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index);
void set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails, IUgcItfVersion ver); void set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails, IUgcItfVersion ver);

View File

@ -220,6 +220,8 @@ void Settings::addModDetails(PublishedFileId_t id, const Mod_entry &details)
f->numChildren = details.numChildren; f->numChildren = details.numChildren;
f->previewURL = details.previewURL; f->previewURL = details.previewURL;
f->total_files_sizes = details.total_files_sizes; f->total_files_sizes = details.total_files_sizes;
f->min_game_branch = details.min_game_branch;
f->max_game_branch = details.max_game_branch;
} }
} }

View File

@ -940,6 +940,12 @@ static void parse_installed_app_Ids(class Settings *settings_client, class Setti
static const auto one_week_ago_epoch = std::chrono::duration_cast<std::chrono::seconds>( static const auto one_week_ago_epoch = std::chrono::duration_cast<std::chrono::seconds>(
( startup_time - std::chrono::hours(24 * 7) ).time_since_epoch() ( startup_time - std::chrono::hours(24 * 7) ).time_since_epoch()
).count(); ).count();
static const auto two_week_ago_epoch = std::chrono::duration_cast<std::chrono::seconds>(
( startup_time - std::chrono::hours(24 * 7 * 2) ).time_since_epoch()
).count();
static const auto three_week_ago_epoch = std::chrono::duration_cast<std::chrono::seconds>(
( startup_time - std::chrono::hours(24 * 7 * 3) ).time_since_epoch()
).count();
static size_t get_file_size_safe(const std::string &filepath, const std::string &basepath, int32 default_val = 0) static size_t get_file_size_safe(const std::string &filepath, const std::string &basepath, int32 default_val = 0)
{ {
@ -963,7 +969,16 @@ static std::string get_mod_preview_url(const std::string &previewFileName, const
} else { } else {
auto settings_folder = std::string(Local_Storage::get_game_settings_path()); auto settings_folder = std::string(Local_Storage::get_game_settings_path());
std::replace(settings_folder.begin(), settings_folder.end(), '\\', '/'); std::replace(settings_folder.begin(), settings_folder.end(), '\\', '/');
return "file://" + settings_folder + "mod_images/" + mod_id + "/" + previewFileName;
return
#if defined(__WINDOWS__)
"file:///"
#else // on Linux absolute paths start like this: /my/path, so the 3rd slash is already appended
"file://"
#endif
+ settings_folder + "mod_images/" + mod_id + "/" + previewFileName;
} }
} }
@ -992,8 +1007,8 @@ static void try_parse_mods_file(class Settings *settings_client, Settings *setti
newMod.fileType = k_EWorkshopFileTypeCommunity; newMod.fileType = k_EWorkshopFileTypeCommunity;
newMod.description = mod.value().value("description", std::string("")); newMod.description = mod.value().value("description", std::string(""));
newMod.steamIDOwner = mod.value().value("steam_id_owner", settings_client->get_local_steam_id().ConvertToUint64()); newMod.steamIDOwner = mod.value().value("steam_id_owner", settings_client->get_local_steam_id().ConvertToUint64());
newMod.timeCreated = mod.value().value("time_created", (uint32)one_week_ago_epoch); newMod.timeCreated = mod.value().value("time_created", (uint32)three_week_ago_epoch);
newMod.timeUpdated = mod.value().value("time_updated", (uint32)one_week_ago_epoch); newMod.timeUpdated = mod.value().value("time_updated", (uint32)two_week_ago_epoch);
newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)one_week_ago_epoch); newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)one_week_ago_epoch);
newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic;
newMod.banned = false; newMod.banned = false;
@ -1076,8 +1091,8 @@ static void try_detect_mods_folder(class Settings *settings_client, Settings *se
newMod.fileType = k_EWorkshopFileTypeCommunity; newMod.fileType = k_EWorkshopFileTypeCommunity;
newMod.description = "mod #" + mod_folder; newMod.description = "mod #" + mod_folder;
newMod.steamIDOwner = settings_client->get_local_steam_id().ConvertToUint64(); newMod.steamIDOwner = settings_client->get_local_steam_id().ConvertToUint64();
newMod.timeCreated = (uint32)one_week_ago_epoch; newMod.timeCreated = (uint32)three_week_ago_epoch;
newMod.timeUpdated = (uint32)one_week_ago_epoch; newMod.timeUpdated = (uint32)two_week_ago_epoch;
newMod.timeAddedToUserList = (uint32)one_week_ago_epoch; newMod.timeAddedToUserList = (uint32)one_week_ago_epoch;
newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic;
newMod.banned = false; newMod.banned = false;

View File

@ -446,7 +446,6 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownload( UGCHandle_t hContent, uint32 u
data.m_eResult = k_EResultOK; data.m_eResult = k_EResultOK;
data.m_ulSteamIDOwner = mod.steamIDOwner; data.m_ulSteamIDOwner = mod.steamIDOwner;
data.m_nSizeInBytes = mod_size; data.m_nSizeInBytes = mod_size;
data.m_ulSteamIDOwner = mod.steamIDOwner;
mod_name.copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1); mod_name.copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1);
PRINT_DEBUG(" QueryUGCRequest data.m_pchFileName = '%s'", data.m_pchFileName); PRINT_DEBUG(" QueryUGCRequest data.m_pchFileName = '%s'", data.m_pchFileName);
@ -1157,7 +1156,6 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownloadToLocation( UGCHandle_t hContent
data.m_nAppID = settings->get_local_game_id().AppID(); data.m_nAppID = settings->get_local_game_id().AppID();
data.m_ulSteamIDOwner = mod.steamIDOwner; data.m_ulSteamIDOwner = mod.steamIDOwner;
data.m_nSizeInBytes = mod_size; data.m_nSizeInBytes = mod_size;
data.m_ulSteamIDOwner = mod.steamIDOwner;
mod_name.copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1); mod_name.copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1);

View File

@ -48,19 +48,25 @@ std::optional<Mod_entry> Steam_UGC::get_query_ugc(UGCQueryHandle_t handle, uint3
return settings->getMod(file_id); return settings->getMod(file_id);
} }
std::optional<std::vector<std::string>> Steam_UGC::get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index) std::vector<std::string> Steam_UGC::get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index)
{ {
auto res = get_query_ugc(handle, index); auto res = get_query_ugc(handle, index);
if (!res.has_value()) return std::nullopt; if (!res.has_value()) return {};
std::string tmp = res.value().tags;
auto tags_tokens = std::vector<std::string>{}; auto tags_tokens = std::vector<std::string>{};
std::stringstream ss(res.value().tags); size_t start = 0;
std::string tmp{}; while (true) {
while(ss >> tmp) { auto end = tmp.find(',', start);
if (tmp.back() == ',') tmp = tmp.substr(0, tmp.size() - 1); if (end == std::string::npos) break;
tags_tokens.push_back(tmp);
tags_tokens.push_back(tmp.substr(start, end - start));
start = end + 1;
} }
tags_tokens.push_back(tmp.substr(start));
return tags_tokens; return tags_tokens;
} }
@ -68,8 +74,6 @@ std::optional<std::vector<std::string>> Steam_UGC::get_query_ugc_tags(UGCQueryHa
void Steam_UGC::set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails, IUgcItfVersion ver) void Steam_UGC::set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails, IUgcItfVersion ver)
{ {
if (pDetails) { if (pDetails) {
memset(pDetails, 0, sizeof(SteamUGCDetails_t));
pDetails->m_nPublishedFileId = id; pDetails->m_nPublishedFileId = id;
if (settings->isModInstalled(id)) { if (settings->isModInstalled(id)) {
@ -90,18 +94,29 @@ void Steam_UGC::set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails, I
pDetails->m_nPreviewFileSize = mod.previewFileSize; pDetails->m_nPreviewFileSize = mod.previewFileSize;
pDetails->m_rtimeCreated = mod.timeCreated; pDetails->m_rtimeCreated = mod.timeCreated;
pDetails->m_rtimeUpdated = mod.timeUpdated; pDetails->m_rtimeUpdated = mod.timeUpdated;
pDetails->m_ulSteamIDOwner = settings->get_local_steam_id().ConvertToUint64(); pDetails->m_ulSteamIDOwner = mod.steamIDOwner;
pDetails->m_rtimeAddedToUserList = mod.timeAddedToUserList; pDetails->m_rtimeAddedToUserList = mod.timeAddedToUserList;
pDetails->m_unVotesUp = mod.votesUp; pDetails->m_unVotesUp = mod.votesUp;
pDetails->m_unVotesDown = mod.votesDown; pDetails->m_unVotesDown = mod.votesDown;
pDetails->m_flScore = mod.score; pDetails->m_flScore = mod.score;
mod.primaryFileName.copy(pDetails->m_pchFileName, sizeof(pDetails->m_pchFileName) - 1); // real steamclient64.dll may set this to null! (ex: item id 3366485326)
mod.description.copy(pDetails->m_rgchDescription, sizeof(pDetails->m_rgchDescription) - 1); auto copied_chars = mod.primaryFileName.copy(pDetails->m_pchFileName, sizeof(pDetails->m_pchFileName) - 1);
mod.tags.copy(pDetails->m_rgchTags, sizeof(pDetails->m_rgchTags) - 1); pDetails->m_pchFileName[copied_chars] = 0;
mod.title.copy(pDetails->m_rgchTitle, sizeof(pDetails->m_rgchTitle) - 1);
mod.workshopItemURL.copy(pDetails->m_rgchURL, sizeof(pDetails->m_rgchURL) - 1); copied_chars = mod.description.copy(pDetails->m_rgchDescription, sizeof(pDetails->m_rgchDescription) - 1);
pDetails->m_rgchDescription[copied_chars] = 0;
copied_chars = mod.tags.copy(pDetails->m_rgchTags, sizeof(pDetails->m_rgchTags) - 1);
pDetails->m_rgchTags[copied_chars] = 0;
copied_chars = mod.title.copy(pDetails->m_rgchTitle, sizeof(pDetails->m_rgchTitle) - 1);
pDetails->m_rgchTitle[copied_chars] = 0;
// real steamclient64.dll may set this to null! (ex: item id 3366485326)
copied_chars = mod.workshopItemURL.copy(pDetails->m_rgchURL, sizeof(pDetails->m_rgchURL) - 1);
pDetails->m_rgchURL[copied_chars] = 0;
// TODO should we enable this? // TODO should we enable this?
// pDetails->m_unNumChildren = mod.numChildren; // pDetails->m_unNumChildren = mod.numChildren;
@ -362,11 +377,11 @@ bool Steam_UGC::GetQueryUGCResult_old( UGCQueryHandle_t handle, uint32 index, St
std::optional<std::string> Steam_UGC::get_query_ugc_tag(UGCQueryHandle_t handle, uint32 index, uint32 indexTag) std::optional<std::string> Steam_UGC::get_query_ugc_tag(UGCQueryHandle_t handle, uint32 index, uint32 indexTag)
{ {
auto res = get_query_ugc_tags(handle, index); auto res = get_query_ugc_tags(handle, index);
if (!res.has_value()) return std::nullopt; if (res.empty()) return std::nullopt;
if (indexTag >= res.value().size()) return std::nullopt; if (indexTag >= res.size()) return std::nullopt;
std::string tmp = res.value()[indexTag]; std::string tmp = res[indexTag];
if (tmp.back() == ',') { if (!tmp.empty() && tmp.back() == ',') {
tmp = tmp.substr(0, tmp.size() - 1); tmp = tmp.substr(0, tmp.size() - 1);
} }
return tmp; return tmp;
@ -380,7 +395,7 @@ uint32 Steam_UGC::GetQueryUGCNumTags( UGCQueryHandle_t handle, uint32 index )
if (handle == k_UGCQueryHandleInvalid) return 0; if (handle == k_UGCQueryHandleInvalid) return 0;
auto res = get_query_ugc_tags(handle, index); auto res = get_query_ugc_tags(handle, index);
return res.has_value() ? static_cast<uint32>(res.value().size()) : 0; return static_cast<uint32>(res.size());
} }
bool Steam_UGC::GetQueryUGCTag( UGCQueryHandle_t handle, uint32 index, uint32 indexTag, STEAM_OUT_STRING_COUNT( cchValueSize ) char* pchValue, uint32 cchValueSize ) bool Steam_UGC::GetQueryUGCTag( UGCQueryHandle_t handle, uint32 index, uint32 indexTag, STEAM_OUT_STRING_COUNT( cchValueSize ) char* pchValue, uint32 cchValueSize )
@ -994,9 +1009,7 @@ bool Steam_UGC::SetItemVisibility( UGCUpdateHandle_t handle, ERemoteStoragePubli
bool Steam_UGC::SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags ) bool Steam_UGC::SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags )
{ {
PRINT_DEBUG("old"); PRINT_DEBUG("old");
std::lock_guard<std::recursive_mutex> lock(global_mutex); return SetItemTags(updateHandle, pTags, false);
return false;
} }
bool Steam_UGC::SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags, bool bAllowAdminTags ) bool Steam_UGC::SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags, bool bAllowAdminTags )
@ -1368,7 +1381,7 @@ bool Steam_UGC::GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *
} }
if (punSizeOnDisk) *punSizeOnDisk = mod.primaryFileSize; if (punSizeOnDisk) *punSizeOnDisk = mod.primaryFileSize;
if (punTimeStamp) *punTimeStamp = mod.timeUpdated; if (punTimeStamp) *punTimeStamp = mod.timeAddedToUserList;
if (pchFolder && cchFolderSize) { if (pchFolder && cchFolderSize) {
// human fall flat doesn't send a nulled buffer, and won't recognize the proper mod path because of that // human fall flat doesn't send a nulled buffer, and won't recognize the proper mod path because of that
memset(pchFolder, 0, cchFolderSize); memset(pchFolder, 0, cchFolderSize);

View File

@ -82,3 +82,14 @@ This dll is meant to be injected during **startup** only, it must **not** be pla
## `GameOverlayRenderer` ## `GameOverlayRenderer`
Some apps verify the existence of this dll, either on disk, or inside their memory space, that's why this dll exists. Some apps verify the existence of this dll, either on disk, or inside their memory space, that's why this dll exists.
It is **NOT** recommended to ignore this dll. It is **NOT** recommended to ignore this dll.
## Mods paths (source-engine games on Windows)
On Windows, the registry key `SourceModInstallPath` is changed to the folder containing the loader.
```
Registry path: HKEY_CURRENT_USER\SOFTWARE\Valve\Steam
Registry key: SourceModInstallPath
Original value: C:\Program Files (x86)\Steam\steamapps\sourcemods
New value: <FOLDER CONTAINING THE LOADER>
```
This affects source-engine mods

View File

@ -1,51 +1,51 @@
{ {
"9422": { "9422": {
"title": "Some Workshop Item", "title": "Some Workshop Item",
"description": "This is the prefered way of specifying mod details, primary file must exist in steam_settings/mods/9422 (along with any other mod files), and preview file must exist in steam_settings/mod_images/9422", "description": "This is the prefered way of specifying mod details, primary file must exist in steam_settings/mods/9422 (along with any other mod files), and preview file must exist in steam_settings/mod_images/9422",
"primary_filename": "metadata.json", "primary_filename": "metadata.json",
"preview_filename": "thumbnail.png" "preview_filename": "thumbnail.png"
}, },
"111111111": { "111111111": {
"title": "Example Workshop Item", "title": "Example Workshop Item",
"description": "Example Workshop Item with all Details", "description": "Example Workshop Item with all Details",
"steam_id_owner": 11111111111111111, "steam_id_owner": 11111111111111111,
"time_created": 1554997000, "time_created": 1554997000,
"time_updated": 1554997000, "time_updated": 1554997000,
"time_added": 1554997000, "time_added": 1554997000,
"tags": "Maps, exampleTag, exampleTag2", "tags": "Maps,exampleTag,exampleTag2",
"primary_filename": "test.sav", "primary_filename": "test.sav",
"primary_filesize": 1000000, "primary_filesize": 1000000,
"preview_filename": "test.png", "preview_filename": "test.png",
"preview_filesize": 1000000, "preview_filesize": 1000000,
"total_files_sizes": 9977664411, "total_files_sizes": 9977664411,
"min_game_branch": "1.4.2", "min_game_branch": "1.4.2",
"max_game_branch": "1.5.0", "max_game_branch": "1.5.0",
"workshop_item_url": "https://steamcommunity.com/sharedfiles/filedetails/?id=111111111", "workshop_item_url": "https://steamcommunity.com/sharedfiles/filedetails/?id=111111111",
"upvotes": 10, "upvotes": 10,
"downvotes": 1, "downvotes": 1,
"num_children": 0, "num_children": 0,
"path": "C:\\games\\my_game\\steam_settings\\mods_data\\mod_111111111_data_folder", "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", "preview_url": "file:///C:/games/my_game/steam_settings/mod_images/my_preview.jpg",
"score": 0.7 "score": 0.7
}, },
"222222222": { "222222222": {
"title": "Example Workshop Item", "title": "Example Workshop Item",
"description": "Example Workshop Item with some Details", "description": "Example Workshop Item with some Details",
"preview_url": "https://commons.wikimedia.org/wiki/File:Tree_in_Mississippi.jpg", "preview_url": "https://commons.wikimedia.org/wiki/File:Tree_in_Mississippi.jpg",
"score": 1.0 "score": 1.0
}, },
"333333333": { "333333333": {
"title": "Example Workshop Item" "title": "Example Workshop Item"
}, },
"444444444": { "444444444": {
"title": "Example Workshop Item", "title": "Example Workshop Item",
"description": "Example Workshop Item" "description": "Example Workshop Item"
}, },
"555555555": { "555555555": {
} }
} }