From fd410f8c5fff9d4cd05a869e7c0a29d2b58b21c4 Mon Sep 17 00:00:00 2001 From: mordentral Date: Thu, 23 Feb 2017 15:58:07 -0500 Subject: [PATCH 1/3] Started adding in Steam Workshop support today Former-commit-id: 61ccb9268f557fc3b9be093fde7b157d9a926bfd --- .../SteamFuncs/AdvancedSteamWorkshopLibrary.h | 55 +++++++++++ .../SteamWSRequestUGCDetailsCallbackProxy.h | 69 ++++++++++++++ .../Private/AdvancedFriendsLibrary.cpp | 2 + .../AdvancedSteamWorkshopLibrary.cpp | 95 +++++++++++++++++++ .../SteamWSRequestUGCDetailsCallbackProxy.cpp | 89 +++++++++++++++++ 5 files changed, 310 insertions(+) create mode 100644 Source/AdvancedSessions/Classes/SteamFuncs/AdvancedSteamWorkshopLibrary.h create mode 100644 Source/AdvancedSessions/Classes/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h create mode 100644 Source/AdvancedSessions/Private/SteamFuncs/AdvancedSteamWorkshopLibrary.cpp create mode 100644 Source/AdvancedSessions/Private/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.cpp diff --git a/Source/AdvancedSessions/Classes/SteamFuncs/AdvancedSteamWorkshopLibrary.h b/Source/AdvancedSessions/Classes/SteamFuncs/AdvancedSteamWorkshopLibrary.h new file mode 100644 index 0000000..e317380 --- /dev/null +++ b/Source/AdvancedSessions/Classes/SteamFuncs/AdvancedSteamWorkshopLibrary.h @@ -0,0 +1,55 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once +#include "OnlineSubSystemHeader.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Online.h" +#include "OnlineSubsystem.h" +#include "OnlineSessionInterface.h" + +#include "AdvancedSteamWorkshopLibrary.generated.h" + + +//General Advanced Sessions Log +DECLARE_LOG_CATEGORY_EXTERN(AdvancedSteamWorkshopLog, Log, All); + + +// Using a custom struct because uint32 isn't blueprint supported and I don't want to cast to int32 +// due to the size of the workshop it could end up overflowing? +USTRUCT(BlueprintType) +struct FBPSteamWorkshopID +{ + GENERATED_USTRUCT_BODY() + +public: + + uint32 SteamWorkshopID; + + FBPSteamWorkshopID() + { + + } + + FBPSteamWorkshopID(uint32 ID) + { + SteamWorkshopID = ID; + } +}; + + +UCLASS() +class UAdvancedSteamWorkshopLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() +public: + + //********* Steam Functions *************// + + // Returns IDs for subscribed workshop items, TArray length dictates how many + UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSteamWorkshop") + static TArray GetSubscribedWorkshopItems(int32 & NumberOfItems); + + UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSteamWorkshop") + static void GetNumSubscribedWorkshopItems(int32 & NumberOfItems); + +}; diff --git a/Source/AdvancedSessions/Classes/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h b/Source/AdvancedSessions/Classes/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h new file mode 100644 index 0000000..b0a0339 --- /dev/null +++ b/Source/AdvancedSessions/Classes/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h @@ -0,0 +1,69 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#pragma once + +// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro + +// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just +// disable the warnings locally. Remove when this is fixed in the SDK +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) +#endif + +#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX + +#pragma push_macro("ARRAY_COUNT") +#undef ARRAY_COUNT + +#include + +#pragma pop_macro("ARRAY_COUNT") + +#endif + +// @todo Steam: See above +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "SteamWSRequestUGCDetailsCallbackProxy.generated.h" + +UCLASS(MinimalAPI) +class USteamWSRequestUGCDetailsCallbackProxy : public UOnlineBlueprintCallProxyBase +{ + GENERATED_UCLASS_BODY() + + // Called when there is a successful results return + UPROPERTY(BlueprintAssignable) + FEmptyOnlineDelegate OnSuccess; + + // Called when there is an unsuccessful results return + UPROPERTY(BlueprintAssignable) + FEmptyOnlineDelegate OnFailure; + + // Ends the current session + UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedSteamWorkshop") + static USteamWSRequestUGCDetailsCallbackProxy* GetWorkshopItemDetails(UObject* WorldContextObject, FBPSteamWorkshopID WorkShopID, int32 NumSecondsBeforeTimeout); + + // UOnlineBlueprintCallProxyBase interface + virtual void Activate() override; + // End of UOnlineBlueprintCallProxyBase interface + +private: + +#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX + // Internal callback when the operation completes, calls out to the public success/failure callbacks + /* Steam UGC details */ + //STEAM_CALLBACK(USteamWSRequestUGCDetailsCallbackProxy, OnUGCRequestUGCDetails, SteamUGCRequestUGCDetailsResult_t, OnUGCRequestUGCDetailsCallback); + void OnUGCRequestUGCDetails(SteamUGCRequestUGCDetailsResult_t *pResult, bool bIOFailure); + CCallResult m_callResultUGCRequestDetails; +#endif + +private: + + FBPSteamWorkshopID WorkShopID; + + int32 NumSecondsBeforeTimeout; + + UObject* WorldContextObject; +}; diff --git a/Source/AdvancedSessions/Private/AdvancedFriendsLibrary.cpp b/Source/AdvancedSessions/Private/AdvancedFriendsLibrary.cpp index 7d9844f..7c2ca94 100644 --- a/Source/AdvancedSessions/Private/AdvancedFriendsLibrary.cpp +++ b/Source/AdvancedSessions/Private/AdvancedFriendsLibrary.cpp @@ -115,6 +115,8 @@ UTexture2D * UAdvancedFriendsLibrary::GetSteamFriendAvatar(const FBPUniqueNetId SteamUtils()->GetImageSize(Picture, &Width, &Height); // STOLEN FROM ANSWERHUB :p, then fixed because answerhub wasn't releasing the memory O.o + // Also fixed image pixel format and switched to a memcpy instead of manual iteration. + // At some point I should probably reply to that answerhub post with these fixes to prevent people killing their games..... if (Width > 0 && Height > 0) { diff --git a/Source/AdvancedSessions/Private/SteamFuncs/AdvancedSteamWorkshopLibrary.cpp b/Source/AdvancedSessions/Private/SteamFuncs/AdvancedSteamWorkshopLibrary.cpp new file mode 100644 index 0000000..7856800 --- /dev/null +++ b/Source/AdvancedSessions/Private/SteamFuncs/AdvancedSteamWorkshopLibrary.cpp @@ -0,0 +1,95 @@ +// Fill out your copyright notice in the Description page of Project Settings. +#include "OnlineSubSystemHeader.h" +#include "SteamFuncs/AdvancedSteamWorkshopLibrary.h" + + +// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro +// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just +// disable the warnings locally. Remove when this is fixed in the SDK +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) +#endif + +#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX + +#pragma push_macro("ARRAY_COUNT") +#undef ARRAY_COUNT + +#include + +#pragma pop_macro("ARRAY_COUNT") + +#endif + +// @todo Steam: See above +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +//General Log +DEFINE_LOG_CATEGORY(AdvancedSteamWorkshopLog); + + +void UAdvancedSteamWorkshopLibrary::GetNumSubscribedWorkshopItems(int32 & NumberOfItems) +{ + NumberOfItems = 0; +#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX + + if (SteamAPI_Init()) + { + NumberOfItems = SteamUGC()->GetNumSubscribedItems(); + return; + } + else + { + UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetNumSubscribedWorkshopItemCount : SteamAPI is not Inited!")); + return; + } +#endif + + UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetNumSubscribedWorkshopItemCount : Called on an incompatible platform")); + return; +} + +TArray UAdvancedSteamWorkshopLibrary::GetSubscribedWorkshopItems(int32 & NumberOfItems) +{ + TArray outArray; + NumberOfItems = 0; + +#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX + + if (SteamAPI_Init()) + { + uint32 NumItems = SteamUGC()->GetNumSubscribedItems(); + + if (NumItems == 0) + return outArray; + + // Not using the actual variable above in case someone somehow goes past int32 limits + // Don't want to go negative on the iteration. + NumberOfItems = NumItems; + + PublishedFileId_t *fileIds = new PublishedFileId_t[NumItems]; + + uint32 subItems = SteamUGC()->GetSubscribedItems(fileIds, NumItems); + + for (uint32 i = 0; i < subItems; ++i) + { + outArray.Add(FBPSteamWorkshopID(fileIds[i])); + } + + delete fileIds; + + return outArray; + } + else + { + UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetSubscribedWorkshopItemCount : SteamAPI is not Inited!")); + return outArray; + } +#endif + + UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetSubscribedWorkshopItemCount : Called on an incompatible platform")); + return outArray; +} diff --git a/Source/AdvancedSessions/Private/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.cpp b/Source/AdvancedSessions/Private/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.cpp new file mode 100644 index 0000000..511833e --- /dev/null +++ b/Source/AdvancedSessions/Private/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.cpp @@ -0,0 +1,89 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "OnlineSubSystemHeader.h" +#include "SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h" + +////////////////////////////////////////////////////////////////////////// +// UEndSessionCallbackProxy + +USteamWSRequestUGCDetailsCallbackProxy::USteamWSRequestUGCDetailsCallbackProxy(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} + + +USteamWSRequestUGCDetailsCallbackProxy* USteamWSRequestUGCDetailsCallbackProxy::GetWorkshopItemDetails(UObject* WorldContextObject, FBPSteamWorkshopID WorkShopID, int32 NumSecondsBeforeTimeout) +{ + USteamWSRequestUGCDetailsCallbackProxy* Proxy = NewObject(); + + Proxy->WorkShopID = WorkShopID; + Proxy->NumSecondsBeforeTimeout = NumSecondsBeforeTimeout; + return Proxy; +} + +void USteamWSRequestUGCDetailsCallbackProxy::Activate() +{ +#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX + if (SteamAPI_Init()) + { + SteamAPICall_t hSteamAPICall = SteamUGC()->RequestUGCDetails(WorkShopID.SteamWorkshopID, NumSecondsBeforeTimeout); + m_callResultUGCRequestDetails.Set(hSteamAPICall, this, &USteamWSRequestUGCDetailsCallbackProxy::OnUGCRequestUGCDetails); + return; + } +#endif + OnFailure.Broadcast(); +} + +void USteamWSRequestUGCDetailsCallbackProxy::OnUGCRequestUGCDetails(SteamUGCRequestUGCDetailsResult_t *pResult, bool bIOFailure) +{ +#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX + + if (bIOFailure) + { + OnFailure.Broadcast(); + return; + } + + SteamUGCDetails_t hUGCDetails = pResult->m_details; + GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Green, hUGCDetails.m_rgchTitle); + GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Green, hUGCDetails.m_rgchDescription); + + OnSuccess.Broadcast(); + return; +#endif + + OnFailure.Broadcast(); + /* + PublishedFileId_t m_nPublishedFileId; + EResult m_eResult; // The result of the operation. + EWorkshopFileType m_eFileType; // Type of the file + AppId_t m_nCreatorAppID; // ID of the app that created this file. + AppId_t m_nConsumerAppID; // ID of the app that will consume this file. + char m_rgchTitle[k_cchPublishedDocumentTitleMax]; // title of document + char m_rgchDescription[k_cchPublishedDocumentDescriptionMax]; // description of document + uint64 m_ulSteamIDOwner; // Steam ID of the user who created this content. + uint32 m_rtimeCreated; // time when the published file was created + uint32 m_rtimeUpdated; // time when the published file was last updated + uint32 m_rtimeAddedToUserList; // time when the user added the published file to their list (not always applicable) + ERemoteStoragePublishedFileVisibility m_eVisibility; // visibility + bool m_bBanned; // whether the file was banned + bool m_bAcceptedForUse; // developer has specifically flagged this item as accepted in the Workshop + bool m_bTagsTruncated; // whether the list of tags was too long to be returned in the provided buffer + char m_rgchTags[k_cchTagListMax]; // comma separated list of all tags associated with this file + // file/url information + UGCHandle_t m_hFile; // The handle of the primary file + UGCHandle_t m_hPreviewFile; // The handle of the preview file + char m_pchFileName[k_cchFilenameMax]; // The cloud filename of the primary file + int32 m_nFileSize; // Size of the primary file + int32 m_nPreviewFileSize; // Size of the preview file + char m_rgchURL[k_cchPublishedFileURLMax]; // URL (for a video or a website) + // voting information + uint32 m_unVotesUp; // number of votes up + uint32 m_unVotesDown; // number of votes down + float m_flScore; // calculated score + uint32 m_unNumChildren; // if m_eFileType == k_EWorkshopFileTypeCollection, then this number will be the number of children contained within the collection + + + */ +} + From de94bf66481e0062e1bd3d061a7e8cd6f635c069 Mon Sep 17 00:00:00 2001 From: mordentral Date: Fri, 24 Feb 2017 12:19:27 -0500 Subject: [PATCH 2/3] Fixed AllServers enum option when Finding servers, now searches twice, once for clients, once for dedicated servers. This is because Steam splits the two queries up and doesn't allow combining them. Added initial Workshop Items support for viewing subscribed items details and information. Former-commit-id: 3153b6dbabf811e864a16408e816df5a03f5c0a3 --- .../FindSessionsCallbackProxyAdvanced.h | 5 + .../SteamFuncs/AdvancedSteamWorkshopLibrary.h | 242 ++++++++++++++++++ .../SteamWSRequestUGCDetailsCallbackProxy.h | 7 +- .../FindSessionsCallbackProxyAdvanced.cpp | 107 ++++---- .../AdvancedSteamWorkshopLibrary.cpp | 26 -- .../SteamWSRequestUGCDetailsCallbackProxy.cpp | 47 +--- 6 files changed, 314 insertions(+), 120 deletions(-) diff --git a/Source/AdvancedSessions/Classes/FindSessionsCallbackProxyAdvanced.h b/Source/AdvancedSessions/Classes/FindSessionsCallbackProxyAdvanced.h index 6b09526..4d12ca8 100644 --- a/Source/AdvancedSessions/Classes/FindSessionsCallbackProxyAdvanced.h +++ b/Source/AdvancedSessions/Classes/FindSessionsCallbackProxyAdvanced.h @@ -51,6 +51,10 @@ private: // Internal callback when the session search completes, calls out to the public success/failure callbacks void OnCompleted(bool bSuccess); + bool bRunSecondSearch; + + TArray SessionSearchResults; + private: // The player controller triggering things TWeakObjectPtr PlayerControllerWeakPtr; @@ -63,6 +67,7 @@ private: // Object to track search results TSharedPtr SearchObject; + TSharedPtr SearchObjectDedicated; // Whether or not to search LAN bool bUseLAN; diff --git a/Source/AdvancedSessions/Classes/SteamFuncs/AdvancedSteamWorkshopLibrary.h b/Source/AdvancedSessions/Classes/SteamFuncs/AdvancedSteamWorkshopLibrary.h index e317380..635d223 100644 --- a/Source/AdvancedSessions/Classes/SteamFuncs/AdvancedSteamWorkshopLibrary.h +++ b/Source/AdvancedSessions/Classes/SteamFuncs/AdvancedSteamWorkshopLibrary.h @@ -7,6 +7,30 @@ #include "OnlineSubsystem.h" #include "OnlineSessionInterface.h" +// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just +// disable the warnings locally. Remove when this is fixed in the SDK +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) +#endif + +#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX + +#pragma push_macro("ARRAY_COUNT") +#undef ARRAY_COUNT + +#include + +#pragma pop_macro("ARRAY_COUNT") + +#endif + +// @todo Steam: See above +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + #include "AdvancedSteamWorkshopLibrary.generated.h" @@ -37,6 +61,224 @@ public: }; +// General result codes - Copying steams version over +// Check these to future proof +UENUM(BlueprintType) +enum class FBPSteamResult : uint8 +{ + k_EResultOK = 1, // success + k_EResultFail = 2, // generic failure + k_EResultNoConnection = 3, // no/failed network connection + // k_EResultNoConnectionRetry = 4, // OBSOLETE - removed + k_EResultInvalidPassword = 5, // password/ticket is invalid + k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere + k_EResultInvalidProtocolVer = 7, // protocol version is incorrect + k_EResultInvalidParam = 8, // a parameter is incorrect + k_EResultFileNotFound = 9, // file was not found + k_EResultBusy = 10, // called method busy - action not taken + k_EResultInvalidState = 11, // called object was in an invalid state + k_EResultInvalidName = 12, // name is invalid + k_EResultInvalidEmail = 13, // email is invalid + k_EResultDuplicateName = 14, // name is not unique + k_EResultAccessDenied = 15, // access is denied + k_EResultTimeout = 16, // operation timed out + k_EResultBanned = 17, // VAC2 banned + k_EResultAccountNotFound = 18, // account not found + k_EResultInvalidSteamID = 19, // steamID is invalid + k_EResultServiceUnavailable = 20, // The requested service is currently unavailable + k_EResultNotLoggedOn = 21, // The user is not logged on + k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party) + k_EResultEncryptionFailure = 23, // Encryption or Decryption failed + k_EResultInsufficientPrivilege = 24, // Insufficient privilege + k_EResultLimitExceeded = 25, // Too much of a good thing + k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes) + k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired + k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again + k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time + k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user + k_EResultIPNotFound = 31, // IP address not found + k_EResultPersistFailed = 32, // failed to write change to the data store + k_EResultLockingFailed = 33, // failed to acquire access lock for this operation + k_EResultLogonSessionReplaced = 34, + k_EResultConnectFailed = 35, + k_EResultHandshakeFailed = 36, + k_EResultIOFailure = 37, + k_EResultRemoteDisconnect = 38, + k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested + k_EResultBlocked = 40, // a user didn't allow it + k_EResultIgnored = 41, // target is ignoring sender + k_EResultNoMatch = 42, // nothing matching the request found + k_EResultAccountDisabled = 43, + k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now + k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available + k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin + k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol. + k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another. + k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed. + k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait + k_EResultSuspended = 51, // Long running operation (content download) suspended/paused + k_EResultCancelled = 52, // Operation canceled (typically by user: content download) + k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable + k_EResultDiskFull = 54, // Operation canceled - not enough disk space. + k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed + k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side + k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account + k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid + k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first + k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files + k_EResultIllegalPassword = 61, // The requested new password is not legal + k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer ) + k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure + k_EResultCannotUseOldPassword = 64, // The requested new password is not legal + k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid + k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent + k_EResultHardwareNotCapableOfIPT = 67, // + k_EResultIPTInitError = 68, // + k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user + k_EResultFacebookQueryError = 70, // Facebook query returned an error + k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired + k_EResultIPLoginRestrictionFailed = 72, + k_EResultAccountLockedDown = 73, + k_EResultAccountLogonDeniedVerifiedEmailRequired = 74, + k_EResultNoMatchingURL = 75, + k_EResultBadResponse = 76, // parse failure, missing field, etc. + k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password + k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range + k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen + k_EResultDisabled = 80, // The requested service has been configured to be unavailable + k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid ! + k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action + k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted + k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent + k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login + k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted + k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker + k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch + k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match + k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners + k_EResultNotModified = 91, // data not modified +}; + +// Check these to future proof +UENUM(BlueprintType) +enum class FBPWorkshopFileType : uint8 +{ + k_EWorkshopFileTypeCommunity = 0, + k_EWorkshopFileTypeMicrotransaction = 1, + k_EWorkshopFileTypeCollection = 2, + k_EWorkshopFileTypeArt = 3, + k_EWorkshopFileTypeVideo = 4, + k_EWorkshopFileTypeScreenshot = 5, + k_EWorkshopFileTypeGame = 6, + k_EWorkshopFileTypeSoftware = 7, + k_EWorkshopFileTypeConcept = 8, + k_EWorkshopFileTypeWebGuide = 9, + k_EWorkshopFileTypeIntegratedGuide = 10, + k_EWorkshopFileTypeMerch = 11, + k_EWorkshopFileTypeControllerBinding = 12, + k_EWorkshopFileTypeSteamworksAccessInvite = 13, + k_EWorkshopFileTypeSteamVideo = 14, + + // Update k_EWorkshopFileTypeMax if you add values. + k_EWorkshopFileTypeMax = 15 +}; + +// WorkshopItemDetails Struct +USTRUCT(BlueprintType) +struct FBPSteamWorkshopItemDetails +{ + GENERATED_USTRUCT_BODY() + +public: + FBPSteamWorkshopItemDetails() + { + + } + + FBPSteamWorkshopItemDetails(SteamUGCDetails_t &hUGCDetails) + { + ResultOfRequest = (FBPSteamResult)hUGCDetails.m_eResult; + FileType = (FBPWorkshopFileType)hUGCDetails.m_eFileType; + CreatorAppID = (int32)hUGCDetails.m_nCreatorAppID; + ConsumerAppID = (int32)hUGCDetails.m_nConsumerAppID; + Title = FString(hUGCDetails.m_rgchTitle, k_cchPublishedDocumentTitleMax); + Description = FString(hUGCDetails.m_rgchDescription, k_cchPublishedDocumentDescriptionMax); + ItemUrl = FString(hUGCDetails.m_rgchURL, k_cchPublishedFileURLMax); + VotesUp = (int32)hUGCDetails.m_unVotesUp; + VotesDown = (int32)hUGCDetails.m_unVotesDown; + CalculatedScore = hUGCDetails.m_flScore; + bBanned = hUGCDetails.m_bBanned; + bAcceptedForUse = hUGCDetails.m_bAcceptedForUse; + bTagsTruncated = hUGCDetails.m_bTagsTruncated; + } + + // Result of obtaining the details + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + FBPSteamResult ResultOfRequest; + + // Type of file + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + FBPWorkshopFileType FileType; + + // These two are listed as baked to an int, but is stored as a uint, think its safe to keep int + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + int32 CreatorAppID; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + int32 ConsumerAppID; + + // Title of item + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + FString Title; + + // Description of item + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + FString Description; + + //Url for a video of website + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + FString ItemUrl; + + // Votes will be unlikely to go above signed limited + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + int32 VotesUp; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + int32 VotesDown; + + // Calculated score + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + float CalculatedScore; + + // whether the file was banned + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + bool bBanned; + + // developer has specifically flagged this item as accepted in the Workshop + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + bool bAcceptedForUse; + + // whether the list of tags was too long to be returned in the provided buffer + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop") + bool bTagsTruncated; + + /* + PublishedFileId_t m_nPublishedFileId; + uint64 m_ulSteamIDOwner; // Steam ID of the user who created this content. + uint32 m_rtimeCreated; // time when the published file was created + uint32 m_rtimeUpdated; // time when the published file was last updated + uint32 m_rtimeAddedToUserList; // time when the user added the published file to their list (not always applicable) + ERemoteStoragePublishedFileVisibility m_eVisibility; // visibility + char m_rgchTags[k_cchTagListMax]; // comma separated list of all tags associated with this file + // file/url information + UGCHandle_t m_hFile; // The handle of the primary file + UGCHandle_t m_hPreviewFile; // The handle of the preview file + char m_pchFileName[k_cchFilenameMax]; // The cloud filename of the primary file + int32 m_nFileSize; // Size of the primary file + int32 m_nPreviewFileSize; // Size of the preview file + uint32 m_unNumChildren; // if m_eFileType == k_EWorkshopFileTypeCollection, then this number will be the number of children contained within the collection + */ + +}; + UCLASS() class UAdvancedSteamWorkshopLibrary : public UBlueprintFunctionLibrary { diff --git a/Source/AdvancedSessions/Classes/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h b/Source/AdvancedSessions/Classes/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h index b0a0339..bdbe618 100644 --- a/Source/AdvancedSessions/Classes/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h +++ b/Source/AdvancedSessions/Classes/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.h @@ -28,6 +28,9 @@ #include "SteamWSRequestUGCDetailsCallbackProxy.generated.h" + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintWorkshopDetailsDelegate, const FBPSteamWorkshopItemDetails&, WorkShopDetails); + UCLASS(MinimalAPI) class USteamWSRequestUGCDetailsCallbackProxy : public UOnlineBlueprintCallProxyBase { @@ -35,11 +38,11 @@ class USteamWSRequestUGCDetailsCallbackProxy : public UOnlineBlueprintCallProxyB // Called when there is a successful results return UPROPERTY(BlueprintAssignable) - FEmptyOnlineDelegate OnSuccess; + FBlueprintWorkshopDetailsDelegate OnSuccess; // Called when there is an unsuccessful results return UPROPERTY(BlueprintAssignable) - FEmptyOnlineDelegate OnFailure; + FBlueprintWorkshopDetailsDelegate OnFailure; // Ends the current session UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedSteamWorkshop") diff --git a/Source/AdvancedSessions/Private/FindSessionsCallbackProxyAdvanced.cpp b/Source/AdvancedSessions/Private/FindSessionsCallbackProxyAdvanced.cpp index d4e68fd..302bbdb 100644 --- a/Source/AdvancedSessions/Private/FindSessionsCallbackProxyAdvanced.cpp +++ b/Source/AdvancedSessions/Private/FindSessionsCallbackProxyAdvanced.cpp @@ -12,6 +12,7 @@ UFindSessionsCallbackProxyAdvanced::UFindSessionsCallbackProxyAdvanced(const FOb , Delegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted)) , bUseLAN(false) { + bRunSecondSearch = false; } UFindSessionsCallbackProxyAdvanced* UFindSessionsCallbackProxyAdvanced::FindSessionsAdvanced(UObject* WorldContextObject, class APlayerController* PlayerController, int MaxResults, bool bUseLAN, EBPServerPresenceSearchType ServerTypeToSearch, const TArray &Filters, bool bEmptyServersOnly, bool bNonEmptyServersOnly, bool bSecureServersOnly, int MinSlotsAvailable) @@ -40,6 +41,9 @@ void UFindSessionsCallbackProxyAdvanced::Activate() auto Sessions = Helper.OnlineSub->GetSessionInterface(); if (Sessions.IsValid()) { + // Re-initialize here, otherwise I think there might be issues with people re-calling search for some reason before it is destroyed + bRunSecondSearch = false; + DelegateHandle = Sessions->AddOnFindSessionsCompleteDelegate_Handle(Delegate); SearchObject = MakeShareable(new FOnlineSessionSearch); @@ -81,6 +85,18 @@ void UFindSessionsCallbackProxyAdvanced::Activate() if (MinSlotsAvailable != 0) tem.Set(SEARCH_MINSLOTSAVAILABLE, MinSlotsAvailable, EOnlineComparisonOp::GreaterThanEquals); + + + // Filter results + if (SearchSettings.Num() > 0) + { + for (int i = 0; i < SearchSettings.Num(); i++) + { + // Function that was added to make directly adding a FVariant possible + tem.HardSet(SearchSettings[i].PropertyKeyPair.Key, SearchSettings[i].PropertyKeyPair.Data, SearchSettings[i].ComparisonOp); + } + } + switch (ServerSearchType) { @@ -99,28 +115,26 @@ void UFindSessionsCallbackProxyAdvanced::Activate() case EBPServerPresenceSearchType::AllServers: default: { - // tem.Set(SEARCH_DEDICATED_ONLY, false, EOnlineComparisonOp::Equals); - // tem.Set(SEARCH_PRESENCE, false, EOnlineComparisonOp::Equals); + bRunSecondSearch = true; + + SearchObjectDedicated = MakeShareable(new FOnlineSessionSearch); + SearchObjectDedicated->MaxSearchResults = MaxResults; + SearchObjectDedicated->bIsLanQuery = bUseLAN; + + FOnlineSearchSettingsEx DedicatedOnly = tem; + tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals); + + DedicatedOnly.Set(SEARCH_DEDICATED_ONLY, true, EOnlineComparisonOp::Equals); + SearchObjectDedicated->QuerySettings = DedicatedOnly; } break; - - } - - // Filter results - if (SearchSettings.Num() > 0) - { - for (int i = 0; i < SearchSettings.Num(); i++) - { - // Function that was added to make directly adding a FVariant possible - tem.HardSet(SearchSettings[i].PropertyKeyPair.Key, SearchSettings[i].PropertyKeyPair.Data, SearchSettings[i].ComparisonOp); - } } // Copy the derived temp variable over to it's base class SearchObject->QuerySettings = tem; Sessions->FindSessions(*Helper.UserID, SearchObject.ToSharedRef()); - + // OnQueryCompleted will get called, nothing more to do now return; } @@ -131,8 +145,7 @@ void UFindSessionsCallbackProxyAdvanced::Activate() } // Fail immediately - TArray Results; - OnFailure.Broadcast(Results); + OnFailure.Broadcast(SessionSearchResults); } void UFindSessionsCallbackProxyAdvanced::OnCompleted(bool bSuccess) @@ -140,7 +153,7 @@ void UFindSessionsCallbackProxyAdvanced::OnCompleted(bool bSuccess) FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("FindSessionsCallback"), GEngine->GetWorldFromContextObject(WorldContextObject)); Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get()); - if (Helper.IsValid()) + if (!bRunSecondSearch && Helper.IsValid()) { auto Sessions = Helper.OnlineSub->GetSessionInterface(); if (Sessions.IsValid()) @@ -149,51 +162,43 @@ void UFindSessionsCallbackProxyAdvanced::OnCompleted(bool bSuccess) } } - TArray Results; - if (bSuccess && SearchObject.IsValid()) { // Just log the results for now, will need to add a blueprint-compatible search result struct for (auto& Result : SearchObject->SearchResults) { - /* bool bAddResult = true; + FString ResultText = FString::Printf(TEXT("Found a session. Ping is %d"), Result.PingInMs); - // Filter results - if (SearchSettings.Num() > 0) - { - FOnlineSessionSetting * setting; - for (int i = 0; i < SearchSettings.Num(); i++) - { - setting = Result.Session.SessionSettings.Settings.Find(SearchSettings[i].PropertyKeyPair.Key); + FFrame::KismetExecutionMessage(*ResultText, ELogVerbosity::Log); - // Couldn't find this key - if (!setting) - continue; - - if (!CompareVariants(setting->Data, SearchSettings[i].PropertyKeyPair.Data, SearchSettings[i].ComparisonOp)) - { - bAddResult = false; - break; - } - } - }*/ - - //if (bAddResult) - //{ - FString ResultText = FString::Printf(TEXT("Found a session. Ping is %d"), Result.PingInMs); - - FFrame::KismetExecutionMessage(*ResultText, ELogVerbosity::Log); - - FBlueprintSessionResult BPResult; - BPResult.OnlineResult = Result; - Results.Add(BPResult); - //} + FBlueprintSessionResult BPResult; + BPResult.OnlineResult = Result; + SessionSearchResults.Add(BPResult); + } + if (!bRunSecondSearch) + { + OnSuccess.Broadcast(SessionSearchResults); + return; } - OnSuccess.Broadcast(Results); } else { - OnFailure.Broadcast(Results); + if (!bRunSecondSearch) + { + // Need to account for only one of the searches failing + if(SessionSearchResults.Num() > 0) + OnSuccess.Broadcast(SessionSearchResults); + else + OnFailure.Broadcast(SessionSearchResults); + return; + } + } + + if (bRunSecondSearch && ServerSearchType == EBPServerPresenceSearchType::AllServers) + { + bRunSecondSearch = false; + auto Sessions = Helper.OnlineSub->GetSessionInterface(); + Sessions->FindSessions(*Helper.UserID, SearchObjectDedicated.ToSharedRef()); } } diff --git a/Source/AdvancedSessions/Private/SteamFuncs/AdvancedSteamWorkshopLibrary.cpp b/Source/AdvancedSessions/Private/SteamFuncs/AdvancedSteamWorkshopLibrary.cpp index 7856800..cb59c47 100644 --- a/Source/AdvancedSessions/Private/SteamFuncs/AdvancedSteamWorkshopLibrary.cpp +++ b/Source/AdvancedSessions/Private/SteamFuncs/AdvancedSteamWorkshopLibrary.cpp @@ -1,32 +1,6 @@ // Fill out your copyright notice in the Description page of Project Settings. #include "OnlineSubSystemHeader.h" #include "SteamFuncs/AdvancedSteamWorkshopLibrary.h" - - -// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro -// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just -// disable the warnings locally. Remove when this is fixed in the SDK -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4996) -#endif - -#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX - -#pragma push_macro("ARRAY_COUNT") -#undef ARRAY_COUNT - -#include - -#pragma pop_macro("ARRAY_COUNT") - -#endif - -// @todo Steam: See above -#ifdef _MSC_VER -#pragma warning(pop) -#endif - //General Log DEFINE_LOG_CATEGORY(AdvancedSteamWorkshopLog); diff --git a/Source/AdvancedSessions/Private/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.cpp b/Source/AdvancedSessions/Private/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.cpp index 511833e..e3c7035 100644 --- a/Source/AdvancedSessions/Private/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.cpp +++ b/Source/AdvancedSessions/Private/SteamFuncs/SteamWSRequestUGCDetailsCallbackProxy.cpp @@ -9,6 +9,7 @@ USteamWSRequestUGCDetailsCallbackProxy::USteamWSRequestUGCDetailsCallbackProxy(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { + NumSecondsBeforeTimeout = 4.0f; } @@ -31,59 +32,23 @@ void USteamWSRequestUGCDetailsCallbackProxy::Activate() return; } #endif - OnFailure.Broadcast(); + OnFailure.Broadcast(FBPSteamWorkshopItemDetails()); } void USteamWSRequestUGCDetailsCallbackProxy::OnUGCRequestUGCDetails(SteamUGCRequestUGCDetailsResult_t *pResult, bool bIOFailure) { #if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX - if (bIOFailure) + if (bIOFailure || !pResult) { - OnFailure.Broadcast(); + OnFailure.Broadcast(FBPSteamWorkshopItemDetails()); return; } - SteamUGCDetails_t hUGCDetails = pResult->m_details; - GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Green, hUGCDetails.m_rgchTitle); - GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Green, hUGCDetails.m_rgchDescription); - - OnSuccess.Broadcast(); + OnSuccess.Broadcast(FBPSteamWorkshopItemDetails(pResult->m_details)); return; #endif - OnFailure.Broadcast(); - /* - PublishedFileId_t m_nPublishedFileId; - EResult m_eResult; // The result of the operation. - EWorkshopFileType m_eFileType; // Type of the file - AppId_t m_nCreatorAppID; // ID of the app that created this file. - AppId_t m_nConsumerAppID; // ID of the app that will consume this file. - char m_rgchTitle[k_cchPublishedDocumentTitleMax]; // title of document - char m_rgchDescription[k_cchPublishedDocumentDescriptionMax]; // description of document - uint64 m_ulSteamIDOwner; // Steam ID of the user who created this content. - uint32 m_rtimeCreated; // time when the published file was created - uint32 m_rtimeUpdated; // time when the published file was last updated - uint32 m_rtimeAddedToUserList; // time when the user added the published file to their list (not always applicable) - ERemoteStoragePublishedFileVisibility m_eVisibility; // visibility - bool m_bBanned; // whether the file was banned - bool m_bAcceptedForUse; // developer has specifically flagged this item as accepted in the Workshop - bool m_bTagsTruncated; // whether the list of tags was too long to be returned in the provided buffer - char m_rgchTags[k_cchTagListMax]; // comma separated list of all tags associated with this file - // file/url information - UGCHandle_t m_hFile; // The handle of the primary file - UGCHandle_t m_hPreviewFile; // The handle of the preview file - char m_pchFileName[k_cchFilenameMax]; // The cloud filename of the primary file - int32 m_nFileSize; // Size of the primary file - int32 m_nPreviewFileSize; // Size of the preview file - char m_rgchURL[k_cchPublishedFileURLMax]; // URL (for a video or a website) - // voting information - uint32 m_unVotesUp; // number of votes up - uint32 m_unVotesDown; // number of votes down - float m_flScore; // calculated score - uint32 m_unNumChildren; // if m_eFileType == k_EWorkshopFileTypeCollection, then this number will be the number of children contained within the collection - - - */ + OnFailure.Broadcast(FBPSteamWorkshopItemDetails()); } From 9a71751e7e90b76cd0078cf3a51d09d32679d9da Mon Sep 17 00:00:00 2001 From: J Date: Fri, 24 Feb 2017 12:20:02 -0500 Subject: [PATCH 3/3] Close branch SteamWorkshop - WIP Branch Former-commit-id: c7a2cad79943ec93d87aaf93ba811683985a94e1