From a08954c1171a2baa631e7cb28d2bd143ed2909a9 Mon Sep 17 00:00:00 2001 From: Gleb Sinyavskiy Date: Sun, 14 May 2023 12:27:32 +0200 Subject: [PATCH 01/11] Add SteamInput's SetInputActionManifestFilePath --- library/Makefile | 3 ++- library/SteamworksPy.cpp | 10 +++++++++- steamworks/interfaces/input.py | 3 +++ steamworks/methods.py | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/library/Makefile b/library/Makefile index 416dc31..dbac57f 100644 --- a/library/Makefile +++ b/library/Makefile @@ -1,4 +1,5 @@ -makeall: +SteamworksPy.so: SteamworksPy.cpp g++ -std=c++11 -o SteamworksPy.so -shared -fPIC SteamworksPy.cpp -l steam_api -L. + clean: rm SteamworksPy.so diff --git a/library/SteamworksPy.cpp b/library/SteamworksPy.cpp index 359ed7a..b195939 100644 --- a/library/SteamworksPy.cpp +++ b/library/SteamworksPy.cpp @@ -13,6 +13,7 @@ #include "TargetConditionals.h" #define SW_PY extern "C" __attribute__ ((visibility("default"))) #elif defined( __linux__ ) +#include #include "sdk/steam/steam_api.h" #define SW_PY extern "C" __attribute__ ((visibility("default"))) #else @@ -702,7 +703,7 @@ SW_PY uint64_t GetCurrentActionSet(uint64_t controllerHandle){ } return (uint64_t) SteamInput()->GetCurrentActionSet((InputHandle_t) controllerHandle); } -// Get the input type (device model) for the specified controller. +// Get the input type (device model) for the specified controller. SW_PY uint64_t GetInputTypeForHandle(uint64_t controllerHandle){ if(SteamInput() == NULL){ return 0; @@ -773,6 +774,13 @@ SW_PY bool ControllerInit(bool bExplicitlyCallRunFrame) { return SteamInput()->Init(bExplicitlyCallRunFrame); } +SW_PY bool SetInputActionManifestFilePath(const char *path) { + if (SteamInput() == NULL) { + return false; + } + return SteamInput()->SetInputActionManifestFilePath(path); +} + // Syncronize controllers. SW_PY void RunFrame() { if (SteamInput() == NULL) { diff --git a/steamworks/interfaces/input.py b/steamworks/interfaces/input.py index e5820e5..0aa9af6 100644 --- a/steamworks/interfaces/input.py +++ b/steamworks/interfaces/input.py @@ -12,6 +12,9 @@ def __init__(self, steam: object): def Init(self, explicitlyCallRunFrame=False): return self.steam.ControllerInit(explicitlyCallRunFrame) + def SetInputActionManifestFilePath(self, path): + return self.steam.SetInputActionManifestFilePath(path.encode()) + def RunFrame(self): return self.steam.RunFrame() diff --git a/steamworks/methods.py b/steamworks/methods.py index b6f4452..bca69e9 100644 --- a/steamworks/methods.py +++ b/steamworks/methods.py @@ -58,6 +58,7 @@ class InputDigitalActionData_t(Structure): "ActivateGameOverlayToWebPage": {"restype": None, "argtypes": [c_char_p]}, "ActivateGameOverlayToStore": {"restype": None, "argtypes": [c_uint32]}, "ActivateGameOverlayInviteDialog": {"restype": None, "argtypes": [c_uint64]}, + "SetInputActionManifestFilePath": {"restype": bool, "argtypes": [c_char_p]}, "ActivateActionSet": {"restype": None, "argtypes": [c_uint64, c_uint64]}, "GetActionSetHandle": {"restype": c_uint64, "argtypes": [c_char_p]}, "GetAnalogActionHandle": {"restype": c_uint64, "argtypes": [c_char_p]}, From 2949c27feace49a6d5d159a76ab8b4ef440e3cd0 Mon Sep 17 00:00:00 2001 From: DaFluffyPotato Date: Sun, 29 Sep 2024 18:53:44 -0400 Subject: [PATCH 02/11] corrected function link for Friends.ActivateGameOverlayToStore --- steamworks/interfaces/friends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/steamworks/interfaces/friends.py b/steamworks/interfaces/friends.py index 3e22a2f..3de9b26 100644 --- a/steamworks/interfaces/friends.py +++ b/steamworks/interfaces/friends.py @@ -129,7 +129,7 @@ def ActivateGameOverlayToStore(self, app_id: int) -> None: :param app_id: int :return: None """ - self.steam.ActivateGameOverlayToWebPage(app_id) + self.steam.ActivateGameOverlayToStore(app_id) def ActivateGameOverlayInviteDialog(self, steam_lobby_id: int) -> None: From d4b6e7a347cea2e571a13899edd61be15f9627ba Mon Sep 17 00:00:00 2001 From: Alexander Jordan Date: Sun, 30 Mar 2025 10:56:56 -0500 Subject: [PATCH 03/11] Fixes basic compatibility with Steamworks SDK 162, and fixes access violation error. This is a janky fix, but it gets this working again with SDK 162. First change is an early return after checking isInitSuccess. Not ideal, but it prevents the access violation error when the appid is not present in the persons library. Other change is to comment out deprecated RequestCurrentStats(); call. Unclear to me if line 1066 should remain. --- library/SteamworksPy.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/library/SteamworksPy.cpp b/library/SteamworksPy.cpp index b195939..9d60a8f 100644 --- a/library/SteamworksPy.cpp +++ b/library/SteamworksPy.cpp @@ -302,10 +302,15 @@ SW_PY int SteamInit() { bool isInitSuccess = SteamAPI_Init(); // Set the default status response int status = FAILED; + // Steamworks initialized with no problems if (isInitSuccess) { status = OK; + }else + { + return status; } + // The Steam client is not running if (!SteamAPI_IsSteamRunning()) { status = ERR_NO_CLIENT; @@ -315,9 +320,11 @@ SW_PY int SteamInit() { status = ERR_NO_CONNECTION; } // Steam is connected and active, so load the stats and achievements - if (status == OK && SteamUserStats() != NULL) { - SteamUserStats()->RequestCurrentStats(); - } + // FULLY DEPRECATED, WILL NOT COMPILE + //if (status == OK && SteamUserStats() != NULL) { + //SteamUserStats()->RequestCurrentStats(); + //} + // Return the Steamworks status return status; } @@ -1029,7 +1036,7 @@ SW_PY int GetAuthSessionTicket(char* buffer) { return 0; } uint32 size{}; - SteamUser()->GetAuthSessionTicket(buffer, 1024, &size); + SteamUser()->GetAuthSessionTicket(buffer, 1024, &size, nullptr); return size; } @@ -1097,7 +1104,7 @@ SW_PY bool RequestCurrentStats() { if (SteamUser() == NULL) { return false; } - return SteamUserStats()->RequestCurrentStats(); + return true; } SW_PY bool SetAchievement(const char *name) { From 1c358c0d6db490dbf2fb8539063396239d7ee01d Mon Sep 17 00:00:00 2001 From: buzz Date: Sat, 17 May 2025 14:36:43 +0200 Subject: [PATCH 04/11] Add support for querying Steam Workshop items Implement functions to handle UGC queries and retrieve results. --- library/SteamworksPy.cpp | 43 +++++++++++++++++++++++ steamworks/interfaces/workshop.py | 58 +++++++++++++++++++++++++++++++ steamworks/methods.py | 13 +++++++ steamworks/structs.py | 42 ++++++++++++++++++++++ 4 files changed, 156 insertions(+) diff --git a/library/SteamworksPy.cpp b/library/SteamworksPy.cpp index 9d60a8f..acf70da 100644 --- a/library/SteamworksPy.cpp +++ b/library/SteamworksPy.cpp @@ -105,6 +105,7 @@ typedef void(*RemoteStorageSubscribeFileResultCallback_t)(SubscriptionResult); typedef void(*RemoteStorageUnsubscribeFileResultCallback_t)(SubscriptionResult); typedef void(*LeaderboardFindResultCallback_t)(LeaderboardFindResult_t); typedef void(*MicroTxnAuthorizationResponseCallback_t)(MicroTxnAuthorizationResponse_t); +typedef void(*SteamUGCQueryCompletedCallback_t)(SteamUGCQueryCompleted_t); //----------------------------------------------- // Workshop Class @@ -117,12 +118,14 @@ class Workshop { GetAppDependenciesResultCallback_t _pyGetAppDependenciesCallback; RemoteStorageSubscribeFileResultCallback_t _pyItemSubscribedCallback; RemoteStorageUnsubscribeFileResultCallback_t _pyItemUnsubscribedCallback; + SteamUGCQueryCompletedCallback_t _pyQueryCompletedCallback; CCallResult _itemCreatedCallback; CCallResult _itemUpdatedCallback; CCallResult _getAppDependenciesCallback; CCallResult _itemSubscribedCallback; CCallResult _itemUnsubscribedCallback; + CCallResult _queryCompletedCallback; CCallback _itemInstalledCallback; @@ -156,6 +159,10 @@ class Workshop { _pyItemUnsubscribedCallback = callback; } + void SetQueryCompletedCallback(SteamUGCQueryCompletedCallback_t callback) { + _pyQueryCompletedCallback = callback; + } + void CreateItem(AppId_t consumerAppId, EWorkshopFileType fileType) { //TODO: Check if fileType is a valid value? SteamAPICall_t createItemCall = SteamUGC()->CreateItem(consumerAppId, fileType); @@ -182,6 +189,11 @@ class Workshop { _itemUnsubscribedCallback.Set(unsubscribeItemCall, this, &Workshop::OnItemUnsubscribed); } + void SendQueryRequest(UGCQueryHandle_t queryHandle) { + SteamAPICall_t queryRequestCall = SteamUGC()->SendQueryUGCRequest(queryHandle); + _queryCompletedCallback.Set(queryRequestCall, this, &Workshop::OnQueryCompleted); + } + private: void OnWorkshopItemCreated(CreateItemResult_t *createItemResult, bool bIOFailure) { if (_pyItemCreatedCallback != nullptr) { @@ -230,6 +242,12 @@ class Workshop { _pyItemUnsubscribedCallback(result); } } + + void OnQueryCompleted(SteamUGCQueryCompleted_t *queryCompletedResult, bool bIOFailure) { + if (_pyQueryCompletedCallback != nullptr) { + _pyQueryCompletedCallback(*queryCompletedResult); + } + } }; static Workshop workshop; @@ -1508,6 +1526,31 @@ SW_PY void Workshop_SuspendDownloads(bool bSuspend) { SteamUGC()->SuspendDownloads(bSuspend); } +SW_PY UGCQueryHandle_t Workshop_CreateQueryUGCDetailsRequest(PublishedFileId_t * pvecPublishedFileID, uint32 unNumPublishedFileIDs) { + return SteamUGC()->CreateQueryUGCDetailsRequest(pvecPublishedFileID, unNumPublishedFileIDs); +} + +SW_PY void Workshop_SetQueryCompletedCallback(SteamUGCQueryCompletedCallback_t callback) { + if (SteamUGC() == NULL) { + return; + } + workshop.SetQueryCompletedCallback(callback); +} + +SW_PY void Workshop_SendQueryUGCRequest(UGCQueryHandle_t handle) { + if(SteamUGC() == NULL){ + return; + } + workshop.SendQueryRequest(handle); +} + +SW_PY bool Workshop_GetQueryUGCResult(UGCQueryHandle_t handle, uint32 index, SteamUGCDetails_t * pDetails) { + if(SteamUGC() == NULL){ + return false; + } + return SteamUGC()->GetQueryUGCResult(handle, index, pDetails); +} + //----------------------------------------------- // Steam Leaderboard //----------------------------------------------- diff --git a/steamworks/interfaces/workshop.py b/steamworks/interfaces/workshop.py index 635b1ab..f24fc3d 100644 --- a/steamworks/interfaces/workshop.py +++ b/steamworks/interfaces/workshop.py @@ -14,6 +14,7 @@ class SteamWorkshop(object): _GetAppDependenciesResult_t = CFUNCTYPE(None, GetAppDependenciesResult) _RemoteStorageSubscribePublishedFileResult_t = CFUNCTYPE(None, SubscriptionResult) _RemoteStorageUnsubscribePublishedFileResult_t = CFUNCTYPE(None, SubscriptionResult) + _SteamUGCQueryCompleted_t = CFUNCTYPE(None, SteamUGCQueryCompleted_t) _CreateItemResult = None _SubmitItemUpdateResult = None @@ -21,6 +22,7 @@ class SteamWorkshop(object): _GetAppDependenciesResult = None _RemoteStorageSubscribePublishedFileResult = None _RemoteStorageUnsubscribePublishedFileResult = None + _SteamUGCQueryCompleted = None def __init__(self, steam: object): self.steam = steam @@ -438,3 +440,59 @@ def GetItemDownloadInfo(self, published_file_id: int) -> dict: } return {} + + + def CreateQueryUGCDetailsRequest(self, published_file_ids: list) -> int: + """Create UGC item details query request + + :param published_file_ids: int list + :return: int + """ + published_files_c = (c_uint64 * len(published_file_ids))() + for index, published_file_id in enumerate(published_file_ids): + published_files_c[index] = c_uint64(published_file_id) + + return self.steam.Workshop_CreateQueryUGCDetailsRequest(published_files_c, len(published_file_ids)) + + + def SetQueryUGCRequestCallback(self, callback: object) -> bool: + """Set callback for UGC query + + :param callback: callable + :return: bool + """ + self._SteamUGCQueryCompleted = SteamWorkshop._SteamUGCQueryCompleted_t(callback) + self.steam.Workshop_SetQueryCompletedCallback(self._SteamUGCQueryCompleted) + return True + + + def SendQueryUGCRequest(self, handle: int, callback: object = None, override_callback: bool = False) -> None: + """Create UGC item details query request + + :param handle: query handle + :param callback: callable + :param override_callback: bool + :return: + """ + if override_callback: + self.SetQueryUGCRequestCallback(callback) + + elif callback and not self._SteamUGCQueryCompleted: + self.SetQueryUGCRequestCallback(callback) + + if self._SteamUGCQueryCompleted is None: + raise SetupRequired('Call `SetQueryUGCRequestCallback` first or supply a `callback`') + + self.steam.Workshop_SendQueryUGCRequest(handle) + + + def GetQueryUGCResult(self, handle: int, index: int) -> SteamUGCDetails_t: + """Create UGC item details query request + + :param handle: query handle + :param index: int + :return: SteamUGCDetails_t + """ + details = SteamUGCDetails_t() + self.steam.Workshop_GetQueryUGCResult(handle, index, byref(details)) + return details diff --git a/steamworks/methods.py b/steamworks/methods.py index bca69e9..65135ee 100644 --- a/steamworks/methods.py +++ b/steamworks/methods.py @@ -206,6 +206,19 @@ class InputDigitalActionData_t(Structure): "Workshop_SuspendDownloads": {"restype": None, "argtypes": [c_bool]}, "Workshop_SubscribeItem": {"restype": None, "argtypes": [c_uint64]}, "Workshop_UnsubscribeItem": {"restype": None, "argtypes": [c_uint64]}, + "Workshop_CreateQueryUGCDetailsRequest": { + "restype": c_uint64, + "argtypes": [POINTER(c_uint64), c_uint32], + }, + "Workshop_SetQueryCompletedCallback": { + "restype": None, + "argtypes": [MAKE_CALLBACK(None, structs.SteamUGCQueryCompleted_t)], + }, + "Workshop_SendQueryUGCRequest": {"argtypes": [c_uint64]}, + "Workshop_GetQueryUGCResult": { + "restype": bool, + "argtypes": [c_uint64, c_uint32, POINTER(structs.SteamUGCDetails_t)], + }, "MicroTxn_SetAuthorizationResponseCallback": { "restype": None, "argtypes": [MAKE_CALLBACK(None, structs.MicroTxnAuthorizationResponse_t)], diff --git a/steamworks/structs.py b/steamworks/structs.py index e93a432..c9b33df 100644 --- a/steamworks/structs.py +++ b/steamworks/structs.py @@ -49,5 +49,47 @@ class SubscriptionResult(Structure): _fields_ = [("result", c_int32), ("publishedFileId", c_uint64)] + +class SteamUGCQueryCompleted_t(Structure): + _fields_ = [ + ("handle", c_uint64), + ("result", c_int), + ("numResultsReturned", c_uint32), + ("totalMatchingResults", c_uint32), + ("cachedData", c_bool) + ] + + +class SteamUGCDetails_t(Structure): + _fields_ = [ + ("publishedFileId", c_uint64), + ("result", c_int), + ("fileType", c_int), + ("creatorAppID", c_uint32), + ("consumerAppID", c_uint32), + ("title", c_char * 129), + ("description", c_char * 8000), + ("steamIDOwner", c_uint64), + ("timeCreated", c_uint32), + ("timeUpdated", c_uint32), + ("timeAddedToUserList", c_uint32), + ("visibility", c_int), + ("banned", c_bool), + ("acceptedForUse", c_bool), + ("tagsTruncated", c_bool), + ("tags", c_char * 1025), + ("file", c_uint64), + ("previewFile", c_uint64), + ("fileName", c_char * 260), + ("fileSize", c_uint32), + ("previewFileSize", c_uint32), + ("URL", c_char * 256), + ("votesUp", c_uint32), + ("votesDown", c_uint32), + ("score", c_float), + ("numChildren", c_uint32), + ] + + class MicroTxnAuthorizationResponse_t(Structure): _fields_ = [("appId", c_uint32), ("orderId", c_uint64), ("authorized", c_bool)] From c48518b3a6936bcfa087c149bfa690c182c597b1 Mon Sep 17 00:00:00 2001 From: Anten Skrabec Date: Fri, 26 Dec 2025 00:49:20 -0700 Subject: [PATCH 05/11] add GetAppDependencies and DownloadItem to the workshop interface --- .gitignore | 3 +- library/SteamworksPy.cpp | 65 +++++++++++++++++++++++++++++ steamworks/interfaces/workshop.py | 68 +++++++++++++++++++++++++++++++ steamworks/methods.py | 10 +++++ steamworks/structs.py | 28 +++++++++++++ 5 files changed, 173 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9ab794a..a993d83 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ *.dylib !redist/windows/*.dll !redist/osx/*.dylib -!redist/linux/*.so \ No newline at end of file +!redist/linux/*.so +library/sdk/steam/ diff --git a/library/SteamworksPy.cpp b/library/SteamworksPy.cpp index acf70da..ed08799 100644 --- a/library/SteamworksPy.cpp +++ b/library/SteamworksPy.cpp @@ -106,6 +106,8 @@ typedef void(*RemoteStorageUnsubscribeFileResultCallback_t)(SubscriptionResult); typedef void(*LeaderboardFindResultCallback_t)(LeaderboardFindResult_t); typedef void(*MicroTxnAuthorizationResponseCallback_t)(MicroTxnAuthorizationResponse_t); typedef void(*SteamUGCQueryCompletedCallback_t)(SteamUGCQueryCompleted_t); +typedef void(*GetAppDependenciesResultCallback_t)(GetAppDependenciesResult_t); +typedef void(*DownloadItemResultCallback_t)(DownloadItemResult_t); //----------------------------------------------- // Workshop Class @@ -119,6 +121,8 @@ class Workshop { RemoteStorageSubscribeFileResultCallback_t _pyItemSubscribedCallback; RemoteStorageUnsubscribeFileResultCallback_t _pyItemUnsubscribedCallback; SteamUGCQueryCompletedCallback_t _pyQueryCompletedCallback; + GetAppDependenciesResultCallback_t _pyGetAppDependenciesCallback; + DownloadItemResultCallback_t _pyDownloadItemCallback; CCallResult _itemCreatedCallback; CCallResult _itemUpdatedCallback; @@ -126,6 +130,8 @@ class Workshop { CCallResult _itemSubscribedCallback; CCallResult _itemUnsubscribedCallback; CCallResult _queryCompletedCallback; + CCallResult _getAppDependenciesCallback; + CCallResult _downloadItemCallback; CCallback _itemInstalledCallback; @@ -163,6 +169,14 @@ class Workshop { _pyQueryCompletedCallback = callback; } + void SetGetAppDependenciesCallback(GetAppDependenciesResultCallback_t callback) { + _pyGetAppDependenciesCallback = callback; + } + + void SetDownloadItemCallback(DownloadItemResultCallback_t callback) { + _pyDownloadItemCallback = callback; + } + void CreateItem(AppId_t consumerAppId, EWorkshopFileType fileType) { //TODO: Check if fileType is a valid value? SteamAPICall_t createItemCall = SteamUGC()->CreateItem(consumerAppId, fileType); @@ -194,6 +208,17 @@ class Workshop { _queryCompletedCallback.Set(queryRequestCall, this, &Workshop::OnQueryCompleted); } + void GetAppDependencies(PublishedFileId_t publishedFileID) { + SteamAPICall_t getAppDependenciesCall = SteamUGC()->GetAppDependencies(publishedFileID); + _getAppDependenciesCallback.Set(getAppDependenciesCall, this, &Workshop::OnGetAppDependencies); + } + + bool DownloadItem(PublishedFileId_t publishedFileID, bool bHighPriority) { + SteamAPICall_t downloadItemCall = SteamUGC()->DownloadItem(publishedFileID, bHighPriority); + _downloadItemCallback.Set(downloadItemCall, this, &Workshop::OnDownloadItem); + return true; // Returns true if successfully queued + } + private: void OnWorkshopItemCreated(CreateItemResult_t *createItemResult, bool bIOFailure) { if (_pyItemCreatedCallback != nullptr) { @@ -248,6 +273,18 @@ class Workshop { _pyQueryCompletedCallback(*queryCompletedResult); } } + + void OnGetAppDependencies(GetAppDependenciesResult_t *result, bool bIOFailure) { + if (_pyGetAppDependenciesCallback != nullptr) { + _pyGetAppDependenciesCallback(*result); + } + } + + void OnDownloadItem(DownloadItemResult_t *result, bool bIOFailure) { + if (_pyDownloadItemCallback != nullptr) { + _pyDownloadItemCallback(*result); + } + } }; static Workshop workshop; @@ -1551,6 +1588,34 @@ SW_PY bool Workshop_GetQueryUGCResult(UGCQueryHandle_t handle, uint32 index, Ste return SteamUGC()->GetQueryUGCResult(handle, index, pDetails); } +SW_PY void Workshop_SetGetAppDependenciesCallback(GetAppDependenciesResultCallback_t callback) { + if (SteamUGC() == NULL) { + return; + } + workshop.SetGetAppDependenciesCallback(callback); +} + +SW_PY void Workshop_GetAppDependencies(PublishedFileId_t publishedFileID) { + if (SteamUGC() == NULL) { + return; + } + workshop.GetAppDependencies(publishedFileID); +} + +SW_PY void Workshop_SetDownloadItemCallback(DownloadItemResultCallback_t callback) { + if (SteamUGC() == NULL) { + return; + } + workshop.SetDownloadItemCallback(callback); +} + +SW_PY bool Workshop_DownloadItem(PublishedFileId_t publishedFileID, bool bHighPriority) { + if (SteamUGC() == NULL) { + return false; + } + return workshop.DownloadItem(publishedFileID, bHighPriority); +} + //----------------------------------------------- // Steam Leaderboard //----------------------------------------------- diff --git a/steamworks/interfaces/workshop.py b/steamworks/interfaces/workshop.py index f24fc3d..a69a12a 100644 --- a/steamworks/interfaces/workshop.py +++ b/steamworks/interfaces/workshop.py @@ -15,6 +15,8 @@ class SteamWorkshop(object): _RemoteStorageSubscribePublishedFileResult_t = CFUNCTYPE(None, SubscriptionResult) _RemoteStorageUnsubscribePublishedFileResult_t = CFUNCTYPE(None, SubscriptionResult) _SteamUGCQueryCompleted_t = CFUNCTYPE(None, SteamUGCQueryCompleted_t) + _GetAppDependenciesResult_t = CFUNCTYPE(None, GetAppDependenciesResult_t) + _DownloadItemResult_t = CFUNCTYPE(None, DownloadItemResult_t) _CreateItemResult = None _SubmitItemUpdateResult = None @@ -23,6 +25,8 @@ class SteamWorkshop(object): _RemoteStorageSubscribePublishedFileResult = None _RemoteStorageUnsubscribePublishedFileResult = None _SteamUGCQueryCompleted = None + _GetAppDependenciesResult = None + _DownloadItemResult = None def __init__(self, steam: object): self.steam = steam @@ -496,3 +500,67 @@ def GetQueryUGCResult(self, handle: int, index: int) -> SteamUGCDetails_t: details = SteamUGCDetails_t() self.steam.Workshop_GetQueryUGCResult(handle, index, byref(details)) return details + + + def SetGetAppDependenciesCallback(self, callback: object) -> bool: + """Set callback for GetAppDependencies result + + :param callback: callable + :return: bool + """ + self._GetAppDependenciesResult = self._GetAppDependenciesResult_t(callback) + self.steam.Workshop_SetGetAppDependenciesCallback(self._GetAppDependenciesResult) + return True + + + def GetAppDependencies(self, published_file_id: int, callback: object = None, + override_callback: bool = False) -> None: + """Get app dependencies for a workshop item + + Returns a list of app IDs that the workshop item depends on. + These are "soft" dependencies shown on the Steam Workshop web page. + The callback may be called multiple times if there are more than 32 dependencies. + + :param published_file_id: int + :param callback: callable - receives GetAppDependenciesResult_t + :param override_callback: bool + :return: None + """ + if override_callback: + self.SetGetAppDependenciesCallback(callback) + + elif callback and not self._GetAppDependenciesResult: + self.SetGetAppDependenciesCallback(callback) + + if self._GetAppDependenciesResult is None: + raise SetupRequired('Call `SetGetAppDependenciesCallback` first or supply a `callback`') + + self.steam.Workshop_GetAppDependencies(published_file_id) + + + def DownloadItem(self, published_file_id: int, high_priority: bool = False, + callback: object = None, override_callback: bool = False) -> bool: + """Initiate or prioritize download of a workshop item + + Downloads or updates a workshop item. If high_priority is True, this item will + be downloaded before any other items. The function returns immediately, and you + should wait for the callback before accessing the item on disk. + + NOTE: The callback will be triggered for all item downloads regardless of the + running application, so check the appID in the callback result. + + :param published_file_id: int + :param high_priority: bool - Set to True to pause other downloads and prioritize this one + :param callback: callable - receives DownloadItemResult_t when download completes (REQUIRED) + :param override_callback: bool + :return: bool - True if download initiated successfully + """ + # Callback is required - set it up internally + if callback is None: + raise ValueError('callback parameter is required for DownloadItem') + + if override_callback or not self._DownloadItemResult: + self._DownloadItemResult = self._DownloadItemResult_t(callback) + self.steam.Workshop_SetDownloadItemCallback(self._DownloadItemResult) + + return self.steam.Workshop_DownloadItem(published_file_id, high_priority) diff --git a/steamworks/methods.py b/steamworks/methods.py index 65135ee..1e35e7f 100644 --- a/steamworks/methods.py +++ b/steamworks/methods.py @@ -219,6 +219,16 @@ class InputDigitalActionData_t(Structure): "restype": bool, "argtypes": [c_uint64, c_uint32, POINTER(structs.SteamUGCDetails_t)], }, + "Workshop_SetGetAppDependenciesCallback": { + "restype": None, + "argtypes": [MAKE_CALLBACK(None, structs.GetAppDependenciesResult_t)], + }, + "Workshop_GetAppDependencies": {"restype": None, "argtypes": [c_uint64]}, + "Workshop_SetDownloadItemCallback": { + "restype": None, + "argtypes": [MAKE_CALLBACK(None, structs.DownloadItemResult_t)], + }, + "Workshop_DownloadItem": {"restype": c_bool, "argtypes": [c_uint64, c_bool]}, "MicroTxn_SetAuthorizationResponseCallback": { "restype": None, "argtypes": [MAKE_CALLBACK(None, structs.MicroTxnAuthorizationResponse_t)], diff --git a/steamworks/structs.py b/steamworks/structs.py index c9b33df..b4710df 100644 --- a/steamworks/structs.py +++ b/steamworks/structs.py @@ -93,3 +93,31 @@ class SteamUGCDetails_t(Structure): class MicroTxnAuthorizationResponse_t(Structure): _fields_ = [("appId", c_uint32), ("orderId", c_uint64), ("authorized", c_bool)] + + +class GetAppDependenciesResult_t(Structure): + """Result from GetAppDependencies call + + Returns app dependencies associated with a workshop item. + These are "soft" dependencies shown on the web. + """ + _fields_ = [ + ("result", c_int), # EResult + ("publishedFileId", c_uint64), # PublishedFileId_t + ("rgAppIDs", c_uint32 * 32), # Array of AppId_t (max 32) + ("numAppDependencies", c_uint32), # Count returned in this struct + ("totalNumAppDependencies", c_uint32) # Total dependencies found + ] + + +class DownloadItemResult_t(Structure): + """Result from DownloadItem call + + Callback fired when workshop item has been downloaded. + Contains the app ID associated with the workshop item. + """ + _fields_ = [ + ("appID", c_uint32), # AppId_t - associated app + ("publishedFileId", c_uint64), # PublishedFileId_t + ("result", c_int) # EResult + ] From 592614c9862f340d5a66382d838d7a24962cd154 Mon Sep 17 00:00:00 2001 From: Anten Skrabec Date: Thu, 25 Dec 2025 03:20:07 -0700 Subject: [PATCH 06/11] implement changes from #91 without the formatting changes included in the original --- steamworks/structs.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/steamworks/structs.py b/steamworks/structs.py index b4710df..3f02898 100644 --- a/steamworks/structs.py +++ b/steamworks/structs.py @@ -45,6 +45,24 @@ def get_app_dependencies_list(self) -> list: return dependencies_list +class GetAppDependenciesResult(Structure): + _fields_ = [ + ("result", c_int32), + ("publishedFileId", c_uint64), + ("array_app_dependencies", POINTER(c_int32)), + ("array_num_app_dependencies", c_int32), + ("total_num_app_dependencies", c_int32) + ] + + def get_app_dependencies_list(self) -> list: + dependencies_list = [] + array_size = self.array_num_app_dependencies + array_type = c_uint32 * array_size + array = array_type.from_address(addressof(self.array_app_dependencies.contents)) + dependencies_list.extend(array) + return dependencies_list + + class SubscriptionResult(Structure): _fields_ = [("result", c_int32), ("publishedFileId", c_uint64)] From 27d2bc621f787fb3474f9d87ef8f56032a9a1f5f Mon Sep 17 00:00:00 2001 From: Maic Siemering Date: Sat, 13 Sep 2025 17:44:14 +0200 Subject: [PATCH 07/11] Update README.md fix directory for steam files, which are required in library (with current linux command) --- library/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/README.md b/library/README.md index f32ea35..e1ef502 100644 --- a/library/README.md +++ b/library/README.md @@ -10,4 +10,4 @@ The source files can be downloaded here (log-in required): https://partner.steam Unpack the archive and place the contents of: - /sdk/public/steam in SteamworksPy/library/sdk/steam -- /sdk/redistributable_bin/%your_os% in SteamworksPy/library/sdk/redist +- /sdk/redistributable_bin/%your_os% in SteamworksPy/library From ed16f58b2d8c3b40aa3d627fa15b3338b2910ac7 Mon Sep 17 00:00:00 2001 From: Anten Skrabec Date: Fri, 26 Dec 2025 12:55:39 -0700 Subject: [PATCH 08/11] remove duplicate GetAppDependencies implementation --- library/SteamworksPy.cpp | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/library/SteamworksPy.cpp b/library/SteamworksPy.cpp index ed08799..cac4cfd 100644 --- a/library/SteamworksPy.cpp +++ b/library/SteamworksPy.cpp @@ -106,7 +106,6 @@ typedef void(*RemoteStorageUnsubscribeFileResultCallback_t)(SubscriptionResult); typedef void(*LeaderboardFindResultCallback_t)(LeaderboardFindResult_t); typedef void(*MicroTxnAuthorizationResponseCallback_t)(MicroTxnAuthorizationResponse_t); typedef void(*SteamUGCQueryCompletedCallback_t)(SteamUGCQueryCompleted_t); -typedef void(*GetAppDependenciesResultCallback_t)(GetAppDependenciesResult_t); typedef void(*DownloadItemResultCallback_t)(DownloadItemResult_t); //----------------------------------------------- @@ -121,7 +120,6 @@ class Workshop { RemoteStorageSubscribeFileResultCallback_t _pyItemSubscribedCallback; RemoteStorageUnsubscribeFileResultCallback_t _pyItemUnsubscribedCallback; SteamUGCQueryCompletedCallback_t _pyQueryCompletedCallback; - GetAppDependenciesResultCallback_t _pyGetAppDependenciesCallback; DownloadItemResultCallback_t _pyDownloadItemCallback; CCallResult _itemCreatedCallback; @@ -130,7 +128,6 @@ class Workshop { CCallResult _itemSubscribedCallback; CCallResult _itemUnsubscribedCallback; CCallResult _queryCompletedCallback; - CCallResult _getAppDependenciesCallback; CCallResult _downloadItemCallback; CCallback _itemInstalledCallback; @@ -208,11 +205,6 @@ class Workshop { _queryCompletedCallback.Set(queryRequestCall, this, &Workshop::OnQueryCompleted); } - void GetAppDependencies(PublishedFileId_t publishedFileID) { - SteamAPICall_t getAppDependenciesCall = SteamUGC()->GetAppDependencies(publishedFileID); - _getAppDependenciesCallback.Set(getAppDependenciesCall, this, &Workshop::OnGetAppDependencies); - } - bool DownloadItem(PublishedFileId_t publishedFileID, bool bHighPriority) { SteamAPICall_t downloadItemCall = SteamUGC()->DownloadItem(publishedFileID, bHighPriority); _downloadItemCallback.Set(downloadItemCall, this, &Workshop::OnDownloadItem); @@ -274,12 +266,6 @@ class Workshop { } } - void OnGetAppDependencies(GetAppDependenciesResult_t *result, bool bIOFailure) { - if (_pyGetAppDependenciesCallback != nullptr) { - _pyGetAppDependenciesCallback(*result); - } - } - void OnDownloadItem(DownloadItemResult_t *result, bool bIOFailure) { if (_pyDownloadItemCallback != nullptr) { _pyDownloadItemCallback(*result); @@ -1595,13 +1581,6 @@ SW_PY void Workshop_SetGetAppDependenciesCallback(GetAppDependenciesResultCallba workshop.SetGetAppDependenciesCallback(callback); } -SW_PY void Workshop_GetAppDependencies(PublishedFileId_t publishedFileID) { - if (SteamUGC() == NULL) { - return; - } - workshop.GetAppDependencies(publishedFileID); -} - SW_PY void Workshop_SetDownloadItemCallback(DownloadItemResultCallback_t callback) { if (SteamUGC() == NULL) { return; From 20e0e883b215576dd39d2346e1f44248a4754721 Mon Sep 17 00:00:00 2001 From: Anten Skrabec Date: Fri, 26 Dec 2025 13:43:03 -0700 Subject: [PATCH 09/11] add optional libs dir when initializing --- steamworks/__init__.py | 323 ++++++++++++++++++++++++----------------- 1 file changed, 186 insertions(+), 137 deletions(-) diff --git a/steamworks/__init__.py b/steamworks/__init__.py index 8094a67..05f2269 100644 --- a/steamworks/__init__.py +++ b/steamworks/__init__.py @@ -7,55 +7,133 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -__version__ = "2.0.0" -__author__ = "GP Garcia" +__version__ = '2.0.0' +__author__ = 'GP Garcia' import sys, os, time from ctypes import * from enum import Enum +from pathlib import Path -import steamworks.util as steamworks_util -from steamworks.enums import * -from steamworks.structs import * -from steamworks.exceptions import * -from steamworks.methods import STEAMWORKS_METHODS +import steamworks.util as steamworks_util +from steamworks.enums import * +from steamworks.structs import * +from steamworks.exceptions import * +from steamworks.methods import STEAMWORKS_METHODS -from steamworks.interfaces.apps import SteamApps -from steamworks.interfaces.friends import SteamFriends -from steamworks.interfaces.matchmaking import SteamMatchmaking -from steamworks.interfaces.music import SteamMusic -from steamworks.interfaces.screenshots import SteamScreenshots -from steamworks.interfaces.users import SteamUsers -from steamworks.interfaces.userstats import SteamUserStats -from steamworks.interfaces.utils import SteamUtils -from steamworks.interfaces.workshop import SteamWorkshop -from steamworks.interfaces.microtxn import SteamMicroTxn -from steamworks.interfaces.input import SteamInput +from steamworks.interfaces.apps import SteamApps +from steamworks.interfaces.friends import SteamFriends +from steamworks.interfaces.matchmaking import SteamMatchmaking +from steamworks.interfaces.music import SteamMusic +from steamworks.interfaces.screenshots import SteamScreenshots +from steamworks.interfaces.users import SteamUsers +from steamworks.interfaces.userstats import SteamUserStats +from steamworks.interfaces.utils import SteamUtils +from steamworks.interfaces.workshop import SteamWorkshop +from steamworks.interfaces.microtxn import SteamMicroTxn +from steamworks.interfaces.input import SteamInput -is_nuitka = "__compiled__" in globals() - -os.environ["LD_LIBRARY_PATH"] = os.getcwd() +is_nuitka = '__compiled__' in globals() class STEAMWORKS(object): """ Primary STEAMWORKS class used for fundamental handling of the STEAMWORKS API - """ + :param supported_platforms: Optional list of platforms to support (restricts initialization) + :param lib_path: Optional path to directory containing Steamworks libraries + (libsteam_api.so/dylib/dll and SteamworksPy.so/dylib/dll) + If not provided, searches current directory, package directory, and Nuitka fallback + """ _arch = steamworks_util.get_arch() - _native_supported_platforms = ["linux", "linux2", "darwin", "win32"] - - def __init__(self, supported_platforms: list = [], _libs=None) -> None: + _native_supported_platforms = ['linux', 'linux2', 'darwin', 'win32'] + + def __init__( + self, + supported_platforms: list = [], + lib_path: str | Path | None = None, + _libs=None + ) -> None: self._supported_platforms = supported_platforms - self._loaded = False - self._cdll = None + self._lib_path = lib_path or _libs # _libs for backwards compatibility + self._libs = _libs + self._loaded = False + self._cdll = None - self.app_id = 0 + self.app_id = 0 - self._libs = _libs + # Set LD_LIBRARY_PATH before loading libraries (Linux/macOS only) + if sys.platform in ['linux', 'linux2', 'darwin']: + self._setup_library_path() self._initialize() + def _get_library_search_paths(self) -> list[str]: + """ + Build ordered list of directories to search for libraries. + + Priority order: + 1. Custom lib_path (if provided) + 2. Current working directory + 3. Package directory + 4. Nuitka fallback (2 dirs up from __file__) + + :return: List of directory paths to search + """ + paths = [] + + # Priority 1: Custom lib_path if provided + if self._lib_path is not None: + resolved_path = str(Path(self._lib_path).resolve()) + paths.append(resolved_path) + + # Priority 2: Current working directory + paths.append(os.getcwd()) + + # Priority 3: Package directory + paths.append(os.path.dirname(__file__)) + + # Priority 4: Nuitka fallback (2 dirs up) + if is_nuitka: + nuitka_path = os.path.split(os.path.split(__file__)[0])[0] + paths.append(nuitka_path) + + return paths + + def _find_library(self, filename: str, search_paths: list[str]) -> str | None: + """ + Search for library file in given paths. + + :param filename: Library filename to find + :param search_paths: Ordered list of directories to search + :return: Absolute path to library if found, None otherwise + """ + for path in search_paths: + full_path = os.path.join(path, filename) + if os.path.isfile(full_path): + return full_path + return None + + def _setup_library_path(self) -> None: + """ + Configure LD_LIBRARY_PATH environment variable for library loading. + Only needed on Linux/macOS. + """ + # Build path list (custom path gets priority) + paths_to_add = [] + + if self._lib_path is not None: + paths_to_add.append(str(Path(self._lib_path).resolve())) + + paths_to_add.append(os.getcwd()) + + # Preserve existing LD_LIBRARY_PATH + existing = os.environ.get('LD_LIBRARY_PATH', '') + if existing: + paths_to_add.append(existing) + + os.environ['LD_LIBRARY_PATH'] = ':'.join(paths_to_add) + def _initialize(self) -> bool: """Initialize module by loading STEAMWORKS library @@ -68,135 +146,106 @@ def _initialize(self) -> bool: if platform not in STEAMWORKS._native_supported_platforms: raise UnsupportedPlatformException(f'"{platform}" is not being supported') - library_file_name = "" - if platform in ["linux", "linux2"]: - library_file_name = "SteamworksPy.so" - if self._libs and os.path.exists( - os.path.join(self._libs, "libsteam_api.so") - ): - cdll.LoadLibrary(os.path.join(self._libs, "libsteam_api.so")) - elif os.environ.get("SWPY_PATH") and os.path.exists( - os.path.join(os.environ["SWPY_PATH"], "libsteam_api.so") - ): - cdll.LoadLibrary( - os.path.join(os.environ["SWPY_PATH"], "libsteam_api.so") - ) - elif os.path.isfile(os.path.join(os.getcwd(), "libsteam_api.so")): - cdll.LoadLibrary( - os.path.join(os.getcwd(), "libsteam_api.so") - ) # if i do this then linux works - elif os.path.isfile( - os.path.join(os.path.dirname(__file__), "libsteam_api.so") - ): - cdll.LoadLibrary( - os.path.join(os.path.dirname(__file__), "libsteam_api.so") - ) - elif os.path.isfile( - os.path.join( - os.path.split(os.path.split(__file__)[0])[0], "libsteam_api.so" - ) - ): - cdll.LoadLibrary( - os.path.join( - os.path.split(os.path.split(__file__)[0])[0], "libsteam_api.so" - ) - ) + # Get search paths for library loading + search_paths = self._get_library_search_paths() + + # Determine library filenames based on platform + library_file_name = '' + if platform in ['linux', 'linux2']: + library_file_name = 'SteamworksPy.so' + # Load Steam API library (required on Linux) + steam_api_path = self._find_library('libsteam_api.so', search_paths) + if steam_api_path: + cdll.LoadLibrary(steam_api_path) else: - raise MissingSteamworksLibraryException( - f'Missing library "libsteam_api.so"' - ) - - elif platform == "darwin": - library_file_name = "SteamworksPy.dylib" - elif platform == "win32": - library_file_name = ( - "SteamworksPy.dll" - if STEAMWORKS._arch == Arch.x86 - else "SteamworksPy64.dll" - ) + raise MissingSteamworksLibraryException(f'Missing library "libsteam_api.so"') + + elif platform == 'darwin': + library_file_name = 'SteamworksPy.dylib' + + elif platform == 'win32': + library_file_name = 'SteamworksPy.dll' if STEAMWORKS._arch == Arch.x86 else 'SteamworksPy64.dll' else: # This case is theoretically unreachable raise UnsupportedPlatformException(f'"{platform}" is not being supported') - if self._libs and os.path.exists(os.path.join(self._libs, library_file_name)): - library_path = os.path.join(self._libs, library_file_name) - elif os.environ.get("SWPY_PATH") and os.path.exists( - os.path.join(os.environ["SWPY_PATH"], library_file_name) - ): - library_path = os.path.join(os.environ["SWPY_PATH"], library_file_name) - elif os.path.isfile(os.path.join(os.getcwd(), library_file_name)): - library_path = os.path.join(os.getcwd(), library_file_name) - elif os.path.isfile(os.path.join(os.path.dirname(__file__), library_file_name)): - library_path = os.path.join(os.path.dirname(__file__), library_file_name) - elif is_nuitka and os.path.isfile( - os.path.join( - os.path.split(os.path.split(__file__)[0])[0], library_file_name - ) - ): - library_path = os.path.join( - os.path.split(os.path.split(__file__)[0])[0], library_file_name - ) - else: - raise MissingSteamworksLibraryException( - f"Missing library {library_file_name}" - ) - if is_nuitka and os.path.join( - os.path.split(os.path.split(__file__)[0])[0], "steam_appid.txt" - ): - app_id_file = os.path.join( - os.path.split(os.path.split(__file__)[0])[0], "steam_appid.txt" - ) - else: - app_id_file = os.path.join(os.getcwd(), "steam_appid.txt") - if not os.path.isfile(app_id_file): - raise FileNotFoundError(f"steam_appid.txt missing from {os.getcwd()}") - with open(app_id_file, "r") as f: - self.app_id = int(f.read()) + # Load SteamworksPy wrapper library + library_path = self._find_library(library_file_name, search_paths) + if not library_path: + raise MissingSteamworksLibraryException(f'Missing library {library_file_name}') - self._cdll = CDLL(library_path) # Throw native exception in case of error - self._loaded = True + # Find steam_appid.txt + # Check parent of lib_path first (if custom path provided), then search paths + paths_to_check = [] + if self._lib_path is not None: + lib_parent = str(Path(self._lib_path).resolve().parent) + paths_to_check.append(lib_parent) + paths_to_check.append(str(Path(self._lib_path).resolve())) + + for path in search_paths: + if path not in paths_to_check: + paths_to_check.append(path) + + app_id_file = None + for path in paths_to_check: + candidate = os.path.join(path, 'steam_appid.txt') + if os.path.isfile(candidate): + app_id_file = candidate + break + + if not app_id_file: + raise FileNotFoundError(f'steam_appid.txt missing. Searched: {", ".join(paths_to_check)}') + + with open(app_id_file, 'r') as f: + self.app_id = int(f.read()) + + self._cdll = CDLL(library_path) # Throw native exception in case of error + self._loaded = True self._load_steamworks_api() return self._loaded + def _load_steamworks_api(self) -> None: """Load all methods from steamworks api and assign their correct arg/res types based on method map :return: None """ if not self._loaded: - raise SteamNotLoadedException("STEAMWORKS not yet loaded") + raise SteamNotLoadedException('STEAMWORKS not yet loaded') for method_name, attributes in STEAMWORKS_METHODS.items(): f = getattr(self._cdll, method_name) - if "restype" in attributes: - f.restype = attributes["restype"] + if 'restype' in attributes: + f.restype = attributes['restype'] - if "argtypes" in attributes: - f.argtypes = attributes["argtypes"] + if 'argtypes' in attributes: + f.argtypes = attributes['argtypes'] setattr(self, method_name, f) self._reload_steamworks_interfaces() + def _reload_steamworks_interfaces(self) -> None: """Reload all interface classes :return: None """ - self.Apps = SteamApps(self) - self.Friends = SteamFriends(self) - self.Matchmaking = SteamMatchmaking(self) - self.Music = SteamMusic(self) - self.Screenshots = SteamScreenshots(self) - self.Users = SteamUsers(self) - self.UserStats = SteamUserStats(self) - self.Utils = SteamUtils(self) - self.Workshop = SteamWorkshop(self) - self.MicroTxn = SteamMicroTxn(self) - self.Input = SteamInput(self) + self.Apps = SteamApps(self) + self.Friends = SteamFriends(self) + self.Matchmaking = SteamMatchmaking(self) + self.Music = SteamMusic(self) + self.Screenshots = SteamScreenshots(self) + self.Users = SteamUsers(self) + self.UserStats = SteamUserStats(self) + self.Utils = SteamUtils(self) + self.Workshop = SteamWorkshop(self) + self.MicroTxn = SteamMicroTxn(self) + self.Input = SteamInput(self) + def initialize(self) -> bool: """Initialize Steam API connection @@ -204,23 +253,21 @@ def initialize(self) -> bool: :return: bool """ if not self.loaded(): - raise SteamNotLoadedException("STEAMWORKS not yet loaded") + raise SteamNotLoadedException('STEAMWORKS not yet loaded') if not self.IsSteamRunning(): - raise SteamNotRunningException("Steam is not running") + raise SteamNotRunningException('Steam is not running') # Boot up the Steam API result = self._cdll.SteamInit() if result == 2: - raise SteamNotRunningException("Steam is not running") + raise SteamNotRunningException('Steam is not running') elif result == 3: - raise SteamConnectionException( - "Not logged on or connection to Steam client could not be established" - ) + raise SteamConnectionException('Not logged on or connection to Steam client could not be established') elif result != 0: - raise GenericSteamException("Failed to initialize STEAMWORKS API") + raise GenericSteamException('Failed to initialize STEAMWORKS API') return True @@ -238,15 +285,17 @@ def unload(self) -> None: :return: None """ self._cdll.SteamShutdown() - self._loaded = False - self._cdll = None + self._loaded = False + self._cdll = None + def loaded(self) -> bool: """Is library loaded and everything populated :return: bool """ - return self._loaded and self._cdll + return (self._loaded and self._cdll) + def run_callbacks(self) -> bool: """Execute all callbacks @@ -254,7 +303,7 @@ def run_callbacks(self) -> bool: :return: bool """ if not self.loaded(): - raise SteamNotLoadedException("STEAMWORKS not yet loaded") + raise SteamNotLoadedException('STEAMWORKS not yet loaded') self._cdll.RunCallbacks() return True From 90545889e2a9c03fc387587171d6f2fdb6801597 Mon Sep 17 00:00:00 2001 From: Anten Skrabec Date: Sun, 28 Dec 2025 20:54:04 -0700 Subject: [PATCH 10/11] last bit of cleanup for duplicate implimentations --- steamworks/interfaces/workshop.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/steamworks/interfaces/workshop.py b/steamworks/interfaces/workshop.py index a69a12a..5cfdc5f 100644 --- a/steamworks/interfaces/workshop.py +++ b/steamworks/interfaces/workshop.py @@ -15,7 +15,6 @@ class SteamWorkshop(object): _RemoteStorageSubscribePublishedFileResult_t = CFUNCTYPE(None, SubscriptionResult) _RemoteStorageUnsubscribePublishedFileResult_t = CFUNCTYPE(None, SubscriptionResult) _SteamUGCQueryCompleted_t = CFUNCTYPE(None, SteamUGCQueryCompleted_t) - _GetAppDependenciesResult_t = CFUNCTYPE(None, GetAppDependenciesResult_t) _DownloadItemResult_t = CFUNCTYPE(None, DownloadItemResult_t) _CreateItemResult = None @@ -25,7 +24,6 @@ class SteamWorkshop(object): _RemoteStorageSubscribePublishedFileResult = None _RemoteStorageUnsubscribePublishedFileResult = None _SteamUGCQueryCompleted = None - _GetAppDependenciesResult = None _DownloadItemResult = None def __init__(self, steam: object): From 925336bad39ec7e53a43475ecb40948e50a59da6 Mon Sep 17 00:00:00 2001 From: Anten Skrabec Date: Sun, 28 Dec 2025 20:57:38 -0700 Subject: [PATCH 11/11] fix .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a993d83..827cc52 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ !redist/windows/*.dll !redist/osx/*.dylib !redist/linux/*.so -library/sdk/steam/ +library/sdk/*