From e2e2554e8823bf5f196ae0814e332e8a80715e27 Mon Sep 17 00:00:00 2001 From: Garry Newman Date: Fri, 26 Apr 2019 13:46:12 +0100 Subject: [PATCH] Ugc Queries --- .../Facepunch.Steamworks.Test.csproj | 2 +- .../{UgcTest.cs => UgcQuery.cs} | 44 ++- .../Generated/Interfaces/ISteamUGC.cs | 12 +- Facepunch.Steamworks/Generated/SteamEnums.cs | 2 +- Facepunch.Steamworks/Structs/SteamId.cs | 2 + Facepunch.Steamworks/Structs/UgcDetails.cs | 69 ++++ Facepunch.Steamworks/Structs/UgcQuery.cs | 342 +++++++----------- Facepunch.Steamworks/Structs/UgcQueryUser.cs | 181 +++++++++ Facepunch.Steamworks/Structs/UgcResultPage.cs | 39 ++ Generator/Cleanup.cs | 2 + 10 files changed, 464 insertions(+), 231 deletions(-) rename Facepunch.Steamworks.Test/{UgcTest.cs => UgcQuery.cs} (60%) create mode 100644 Facepunch.Steamworks/Structs/UgcDetails.cs create mode 100644 Facepunch.Steamworks/Structs/UgcQueryUser.cs create mode 100644 Facepunch.Steamworks/Structs/UgcResultPage.cs diff --git a/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj b/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj index efda689..e5daa38 100644 --- a/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj +++ b/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj @@ -91,7 +91,7 @@ - + diff --git a/Facepunch.Steamworks.Test/UgcTest.cs b/Facepunch.Steamworks.Test/UgcQuery.cs similarity index 60% rename from Facepunch.Steamworks.Test/UgcTest.cs rename to Facepunch.Steamworks.Test/UgcQuery.cs index e5e55e0..6cc821c 100644 --- a/Facepunch.Steamworks.Test/UgcTest.cs +++ b/Facepunch.Steamworks.Test/UgcQuery.cs @@ -10,12 +10,12 @@ namespace Steamworks { [TestClass] [DeploymentItem( "steam_api64.dll" )] - public class UgcTests + public class UgcQueryTests { [TestMethod] public async Task QueryAll() { - var q = UgcQuery.All(); + var q = Ugc.Query.All; var result = await q.GetPageAsync( 1 ); Assert.IsNotNull( result ); @@ -27,7 +27,7 @@ public async Task QueryAll() [TestMethod] public async Task QueryWithTags() { - var q = UgcQuery.All() + var q = Ugc.Query.All .WithTag( "Fun" ) .WithTag( "Movie" ) .MatchAllTags(); @@ -49,7 +49,7 @@ public async Task QueryWithTags() [TestMethod] public async Task QueryAllFromFriends() { - var q = UgcQuery.All() + var q = Ugc.Query.All .CreatedByFriends(); var result = await q.GetPageAsync( 1 ); @@ -63,6 +63,42 @@ public async Task QueryAllFromFriends() Console.WriteLine( $" {entry.Title}" ); } } + + [TestMethod] + public async Task QueryUserOwn() + { + var q = Ugc.UserQuery.All + .FromSelf(); + + var result = await q.GetPageAsync( 1 ); + Assert.IsNotNull( result ); + + Console.WriteLine( $"ResultCount: {result?.ResultCount}" ); + Console.WriteLine( $"TotalCount: {result?.TotalCount}" ); + + foreach ( var entry in result.Value.Entries ) + { + Console.WriteLine( $" {entry.Title}" ); + } + } + + [TestMethod] + public async Task QueryFoohy() + { + var q = Ugc.UserQuery.All + .FromUser( 76561197997689747 ); + + var result = await q.GetPageAsync( 1 ); + Assert.IsNotNull( result ); + + Console.WriteLine( $"ResultCount: {result?.ResultCount}" ); + Console.WriteLine( $"TotalCount: {result?.TotalCount}" ); + + foreach ( var entry in result.Value.Entries ) + { + Console.WriteLine( $" {entry.Title}" ); + } + } } } diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs index f9daf7d..2a132e8 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs @@ -97,33 +97,33 @@ public override void InitInternals() #region FunctionMeta [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate UGCQueryHandle_t FCreateQueryUserUGCRequest( IntPtr self, AccountID_t unAccountID, UserUGCList eListType, UGCMatchingUGCType eMatchingUGCType, UserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint unPage ); + private delegate UGCQueryHandle_t FCreateQueryUserUGCRequest( IntPtr self, AccountID_t unAccountID, UserUGCList eListType, UgcType eMatchingUGCType, UserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint unPage ); private FCreateQueryUserUGCRequest _CreateQueryUserUGCRequest; #endregion - internal UGCQueryHandle_t CreateQueryUserUGCRequest( AccountID_t unAccountID, UserUGCList eListType, UGCMatchingUGCType eMatchingUGCType, UserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint unPage ) + internal UGCQueryHandle_t CreateQueryUserUGCRequest( AccountID_t unAccountID, UserUGCList eListType, UgcType eMatchingUGCType, UserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint unPage ) { return _CreateQueryUserUGCRequest( Self, unAccountID, eListType, eMatchingUGCType, eSortOrder, nCreatorAppID, nConsumerAppID, unPage ); } #region FunctionMeta [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate UGCQueryHandle_t FCreateQueryAllUGCRequest1( IntPtr self, UGCQuery eQueryType, UGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint unPage ); + private delegate UGCQueryHandle_t FCreateQueryAllUGCRequest1( IntPtr self, UGCQuery eQueryType, UgcType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint unPage ); private FCreateQueryAllUGCRequest1 _CreateQueryAllUGCRequest1; #endregion - internal UGCQueryHandle_t CreateQueryAllUGCRequest1( UGCQuery eQueryType, UGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint unPage ) + internal UGCQueryHandle_t CreateQueryAllUGCRequest1( UGCQuery eQueryType, UgcType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint unPage ) { return _CreateQueryAllUGCRequest1( Self, eQueryType, eMatchingeMatchingUGCTypeFileType, nCreatorAppID, nConsumerAppID, unPage ); } #region FunctionMeta [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate UGCQueryHandle_t FCreateQueryAllUGCRequest2( IntPtr self, UGCQuery eQueryType, UGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, string pchCursor ); + private delegate UGCQueryHandle_t FCreateQueryAllUGCRequest2( IntPtr self, UGCQuery eQueryType, UgcType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, string pchCursor ); private FCreateQueryAllUGCRequest2 _CreateQueryAllUGCRequest2; #endregion - internal UGCQueryHandle_t CreateQueryAllUGCRequest2( UGCQuery eQueryType, UGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, string pchCursor ) + internal UGCQueryHandle_t CreateQueryAllUGCRequest2( UGCQuery eQueryType, UgcType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, string pchCursor ) { return _CreateQueryAllUGCRequest2( Self, eQueryType, eMatchingeMatchingUGCTypeFileType, nCreatorAppID, nConsumerAppID, pchCursor ); } diff --git a/Facepunch.Steamworks/Generated/SteamEnums.cs b/Facepunch.Steamworks/Generated/SteamEnums.cs index b5f3c26..4383f57 100644 --- a/Facepunch.Steamworks/Generated/SteamEnums.cs +++ b/Facepunch.Steamworks/Generated/SteamEnums.cs @@ -1774,7 +1774,7 @@ internal enum SteamControllerLEDFlag : int // // EUGCMatchingUGCType // - internal enum UGCMatchingUGCType : int + public enum UgcType : int { Items = 0, Items_Mtx = 1, diff --git a/Facepunch.Steamworks/Structs/SteamId.cs b/Facepunch.Steamworks/Structs/SteamId.cs index 762f7f5..62bdce3 100644 --- a/Facepunch.Steamworks/Structs/SteamId.cs +++ b/Facepunch.Steamworks/Structs/SteamId.cs @@ -21,5 +21,7 @@ public static implicit operator ulong( SteamId value ) } public override string ToString() => Value.ToString(); + + public uint AccountId => (uint) (Value & 0xFFFFFFFFul); } } \ No newline at end of file diff --git a/Facepunch.Steamworks/Structs/UgcDetails.cs b/Facepunch.Steamworks/Structs/UgcDetails.cs new file mode 100644 index 0000000..faf4815 --- /dev/null +++ b/Facepunch.Steamworks/Structs/UgcDetails.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; +using Steamworks.Data; + +namespace Steamworks.Ugc +{ + public struct Result + { + public PublishedFileId Id; + + public string Title; + public string Description; + public string[] Tags; + + + // + // TODO; + // + //internal Steamworks.Result Result; // m_eResult enum EResult + internal WorkshopFileType FileType; // m_eFileType enum EWorkshopFileType + internal uint CreatorAppID; // m_nCreatorAppID AppId_t + internal uint ConsumerAppID; // m_nConsumerAppID AppId_t + + internal ulong SteamIDOwner; // m_ulSteamIDOwner uint64 + internal uint TimeCreated; // m_rtimeCreated uint32 + internal uint TimeUpdated; // m_rtimeUpdated uint32 + internal uint TimeAddedToUserList; // m_rtimeAddedToUserList uint32 + internal RemoteStoragePublishedFileVisibility Visibility; // m_eVisibility enum ERemoteStoragePublishedFileVisibility + internal bool Banned; // m_bBanned _Bool + internal bool AcceptedForUse; // m_bAcceptedForUse _Bool + internal bool TagsTruncated; // m_bTagsTruncated _Bool + internal ulong File; // m_hFile UGCHandle_t + internal ulong PreviewFile; // m_hPreviewFile UGCHandle_t + internal string PchFileName; // m_pchFileName char [260] + internal int FileSize; // m_nFileSize int32 + internal int PreviewFileSize; // m_nPreviewFileSize int32 + internal string URL; // m_rgchURL char [256] + internal uint VotesUp; // m_unVotesUp uint32 + internal uint VotesDown; // m_unVotesDown uint32 + internal float Score; // m_flScore float + internal uint NumChildren; // m_unNumChildren uint32 + + internal static Result From( SteamUGCDetails_t details, UGCQueryHandle_t handle ) + { + var d = new Result + { + Id = details.PublishedFileId, + FileType = details.FileType, + + Title = details.Title, + Description = details.Description, + Tags = details.Tags.Split( new[] { ',' }, StringSplitOptions.RemoveEmptyEntries ) + + }; + + return d; + } + + /// + /// A case insensitive check for tag + /// + public bool HasTag( string find ) + { + if ( Tags.Length == 0 ) return false; + + return Tags.Contains( find, StringComparer.OrdinalIgnoreCase ); + } + } +} \ No newline at end of file diff --git a/Facepunch.Steamworks/Structs/UgcQuery.cs b/Facepunch.Steamworks/Structs/UgcQuery.cs index 337bb37..58a0f59 100644 --- a/Facepunch.Steamworks/Structs/UgcQuery.cs +++ b/Facepunch.Steamworks/Structs/UgcQuery.cs @@ -2,112 +2,62 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Steamworks.Data; -namespace Steamworks.Data +using QueryType = Steamworks.Ugc.Query; + +namespace Steamworks.Ugc { - public struct UgcQuery + public struct Query { - UGCMatchingUGCType matching; - - UserUGCList userGc; + UgcType matchingType; UGCQuery queryType; - AppId consumerApp; AppId creatorApp; - List requiredTags; - bool? matchAnyTag; - - List excludedTags; - - Dictionary requiredKv; - - bool? WantsReturnOnlyIDs; - bool? WantsReturnKeyValueTags; - bool? WantsReturnLongDescription; - bool? WantsReturnMetadata; - bool? WantsReturnChildren; - bool? WantsReturnAdditionalPreviews; - bool? WantsReturnTotalOnly; - bool? WantsReturnPlaytimeStats; - - string searchText; - string language; - int? trendDays; - int? maxCacheAge; - - public static UgcQuery Items() => new UgcQuery { matching = UGCMatchingUGCType.Items }; - public static UgcQuery ItemsMtx() => new UgcQuery { matching = UGCMatchingUGCType.Items_Mtx }; - public static UgcQuery ItemsReadyToUse() => new UgcQuery { matching = UGCMatchingUGCType.Items_ReadyToUse }; - public static UgcQuery Collections() => new UgcQuery { matching = UGCMatchingUGCType.Collections }; - public static UgcQuery Artwork() => new UgcQuery { matching = UGCMatchingUGCType.Artwork }; - public static UgcQuery Videos() => new UgcQuery { matching = UGCMatchingUGCType.Videos }; - public static UgcQuery Screenshots() => new UgcQuery { matching = UGCMatchingUGCType.Screenshots }; - public static UgcQuery AllGuides() => new UgcQuery { matching = UGCMatchingUGCType.AllGuides }; - public static UgcQuery WebGuides() => new UgcQuery { matching = UGCMatchingUGCType.WebGuides }; - public static UgcQuery IntegratedGuides() => new UgcQuery { matching = UGCMatchingUGCType.IntegratedGuides }; - public static UgcQuery UsableInGame() => new UgcQuery { matching = UGCMatchingUGCType.UsableInGame }; - public static UgcQuery ControllerBindings() => new UgcQuery { matching = UGCMatchingUGCType.ControllerBindings }; - public static UgcQuery GameManagedItems() => new UgcQuery { matching = UGCMatchingUGCType.GameManagedItems }; - public static UgcQuery All() => new UgcQuery { matching = UGCMatchingUGCType.All }; - - public UgcQuery RankedByVote() { queryType = UGCQuery.RankedByVote; return this; } - public UgcQuery RankedByPublicationDate() { queryType = UGCQuery.RankedByPublicationDate; return this; } - public UgcQuery RankedByAcceptanceDate() { queryType = UGCQuery.AcceptedForGameRankedByAcceptanceDate; return this; } - public UgcQuery RankedByTrend() { queryType = UGCQuery.RankedByTrend; return this; } - public UgcQuery FavoritedByFriends() { queryType = UGCQuery.FavoritedByFriendsRankedByPublicationDate; return this; } - public UgcQuery CreatedByFriends() { queryType = UGCQuery.CreatedByFriendsRankedByPublicationDate; return this; } - public UgcQuery RankedByNumTimesReported() { queryType = UGCQuery.RankedByNumTimesReported; return this; } - public UgcQuery CreatedByFollowedUsers() { queryType = UGCQuery.CreatedByFollowedUsersRankedByPublicationDate; return this; } - public UgcQuery NotYetRated() { queryType = UGCQuery.NotYetRated; return this; } - public UgcQuery RankedByTotalVotesAsc() { queryType = UGCQuery.RankedByTotalVotesAsc; return this; } - public UgcQuery RankedByVotesUp() { queryType = UGCQuery.RankedByVotesUp; return this; } - public UgcQuery RankedByTextSearch() { queryType = UGCQuery.RankedByTextSearch; return this; } - public UgcQuery RankedByTotalUniqueSubscriptions() { queryType = UGCQuery.RankedByTotalUniqueSubscriptions; return this; } - public UgcQuery RankedByPlaytimeTrend() { queryType = UGCQuery.RankedByPlaytimeTrend; return this; } - public UgcQuery RankedByTotalPlaytime() { queryType = UGCQuery.RankedByTotalPlaytime; return this; } - public UgcQuery RankedByAveragePlaytimeTrend() { queryType = UGCQuery.RankedByAveragePlaytimeTrend; return this; } - public UgcQuery RankedByLifetimeAveragePlaytime() { queryType = UGCQuery.RankedByLifetimeAveragePlaytime; return this; } - public UgcQuery RankedByPlaytimeSessionsTrend() { queryType = UGCQuery.RankedByPlaytimeSessionsTrend; return this; } - public UgcQuery RankedByLifetimePlaytimeSessions() { queryType = UGCQuery.RankedByLifetimePlaytimeSessions; return this; } - - public UgcQuery WithOnlyIDs( bool b) { WantsReturnOnlyIDs = b; return this; } - public UgcQuery WithKeyValueTag( bool b ) { WantsReturnKeyValueTags = b; return this; } - public UgcQuery WithLongDescription( bool b ) { WantsReturnLongDescription = b; return this; } - public UgcQuery WithMetadata( bool b ) { WantsReturnMetadata = b; return this; } - public UgcQuery WithChildren( bool b ) { WantsReturnChildren = b; return this; } - public UgcQuery WithAdditionalPreviews( bool b ) { WantsReturnAdditionalPreviews = b; return this; } - public UgcQuery WithTotalOnly( bool b ) { WantsReturnTotalOnly = b; return this; } - public UgcQuery WithPlaytimeStats( bool b ) { WantsReturnPlaytimeStats = b; return this; } - public UgcQuery AllowCachedResponse( int maxSecondsAge ) { maxCacheAge = maxSecondsAge; return this; } - - public UgcQuery InLanguage( string lang ) { language = lang; return this; } - - /// - /// Found items must have at least one of the defined tags - /// - public UgcQuery MatchAnyTag() { matchAnyTag = true; return this; } - - /// - /// Found items must have all defined tags - /// - public UgcQuery MatchAllTags() { matchAnyTag = false; return this; } - - public UgcQuery WithTag( string tag ) + public Query( UgcType type ) : this() { - if ( requiredTags == null ) requiredTags = new List(); - requiredTags.Add( tag ); - return this; + matchingType = type; } - public UgcQuery WithoutTag( string tag ) - { - if ( excludedTags == null ) excludedTags = new List(); - excludedTags.Add( tag ); - return this; - } + public static Query All => new Query( UgcType.All ); + public static Query Items => new Query( UgcType.Items ); + public static Query ItemsMtx => new Query( UgcType.Items_Mtx ); + public static Query ItemsReadyToUse => new Query( UgcType.Items_ReadyToUse ); + public static Query Collections => new Query( UgcType.Collections ); + public static Query Artwork => new Query( UgcType.Artwork ); + public static Query Videos => new Query( UgcType.Videos ); + public static Query Screenshots => new Query( UgcType.Screenshots ); + public static Query AllGuides => new Query( UgcType.AllGuides ); + public static Query WebGuides => new Query( UgcType.WebGuides ); + public static Query IntegratedGuides => new Query( UgcType.IntegratedGuides ); + public static Query UsableInGame => new Query( UgcType.UsableInGame ); + public static Query ControllerBindings => new Query( UgcType.ControllerBindings ); + public static Query GameManagedItems => new Query( UgcType.GameManagedItems ); - public async Task GetPageAsync( int page ) + + public Query RankedByVote() { queryType = UGCQuery.RankedByVote; return this; } + public Query RankedByPublicationDate() { queryType = UGCQuery.RankedByPublicationDate; return this; } + public Query RankedByAcceptanceDate() { queryType = UGCQuery.AcceptedForGameRankedByAcceptanceDate; return this; } + public Query RankedByTrend() { queryType = UGCQuery.RankedByTrend; return this; } + public Query FavoritedByFriends() { queryType = UGCQuery.FavoritedByFriendsRankedByPublicationDate; return this; } + public Query CreatedByFriends() { queryType = UGCQuery.CreatedByFriendsRankedByPublicationDate; return this; } + public Query RankedByNumTimesReported() { queryType = UGCQuery.RankedByNumTimesReported; return this; } + public Query CreatedByFollowedUsers() { queryType = UGCQuery.CreatedByFollowedUsersRankedByPublicationDate; return this; } + public Query NotYetRated() { queryType = UGCQuery.NotYetRated; return this; } + public Query RankedByTotalVotesAsc() { queryType = UGCQuery.RankedByTotalVotesAsc; return this; } + public Query RankedByVotesUp() { queryType = UGCQuery.RankedByVotesUp; return this; } + public Query RankedByTextSearch() { queryType = UGCQuery.RankedByTextSearch; return this; } + public Query RankedByTotalUniqueSubscriptions() { queryType = UGCQuery.RankedByTotalUniqueSubscriptions; return this; } + public Query RankedByPlaytimeTrend() { queryType = UGCQuery.RankedByPlaytimeTrend; return this; } + public Query RankedByTotalPlaytime() { queryType = UGCQuery.RankedByTotalPlaytime; return this; } + public Query RankedByAveragePlaytimeTrend() { queryType = UGCQuery.RankedByAveragePlaytimeTrend; return this; } + public Query RankedByLifetimeAveragePlaytime() { queryType = UGCQuery.RankedByLifetimeAveragePlaytime; return this; } + public Query RankedByPlaytimeSessionsTrend() { queryType = UGCQuery.RankedByPlaytimeSessionsTrend; return this; } + public Query RankedByLifetimePlaytimeSessions() { queryType = UGCQuery.RankedByLifetimePlaytimeSessions; return this; } + + + public async Task GetPageAsync( int page ) { if ( page <= 0 ) throw new System.Exception( "page should be > 0" ); @@ -115,47 +65,18 @@ public UgcQuery WithoutTag( string tag ) if ( creatorApp == 0 ) creatorApp = consumerApp; UGCQueryHandle_t handle; + handle = SteamUGC.Internal.CreateQueryAllUGCRequest1( queryType, matchingType, creatorApp.Value, consumerApp.Value, (uint)page ); - handle = SteamUGC.Internal.CreateQueryAllUGCRequest1( queryType, matching, creatorApp.Value, consumerApp.Value, (uint) page ); - - // Apply stored constraints - { - if ( requiredTags != null ) - { - foreach ( var tag in requiredTags ) - SteamUGC.Internal.AddRequiredTag( handle, tag ); - } - - if ( excludedTags != null ) - { - foreach ( var tag in excludedTags ) - SteamUGC.Internal.AddExcludedTag( handle, tag ); - } - - if ( requiredKv != null ) - { - foreach ( var tag in requiredKv ) - SteamUGC.Internal.AddRequiredKeyValueTag( handle, tag.Key, tag.Value ); - } - - if ( matchAnyTag .HasValue ) - { - SteamUGC.Internal.SetMatchAnyTag( handle, matchAnyTag.Value ); - } - - // - // TODO - add more - // - } + ApplyConstraints( handle ); var result = await SteamUGC.Internal.SendQueryUGCRequest( handle ); if ( !result.HasValue ) return null; - if ( result.Value.Result != Result.OK ) + if ( result.Value.Result != Steamworks.Result.OK ) return null; - return new UgcQueryPage + return new ResultPage { Handle = result.Value.Handle, ResultCount = (int) result.Value.NumResultsReturned, @@ -164,103 +85,86 @@ public UgcQuery WithoutTag( string tag ) }; } - } - public struct UgcQueryPage : System.IDisposable - { - internal UGCQueryHandle_t Handle; + #region SharedConstraints + public QueryType WithType( UgcType type ){ matchingType = type; return this; } + bool? WantsReturnOnlyIDs; + public QueryType WithOnlyIDs( bool b ) { WantsReturnOnlyIDs = b; return this; } + bool? WantsReturnKeyValueTags; + public QueryType WithKeyValueTag( bool b ) { WantsReturnKeyValueTags = b; return this; } + bool? WantsReturnLongDescription; + public QueryType WithLongDescription( bool b ) { WantsReturnLongDescription = b; return this; } + bool? WantsReturnMetadata; + public QueryType WithMetadata( bool b ) { WantsReturnMetadata = b; return this; } + bool? WantsReturnChildren; + public QueryType WithChildren( bool b ) { WantsReturnChildren = b; return this; } + bool? WantsReturnAdditionalPreviews; + public QueryType WithAdditionalPreviews( bool b ) { WantsReturnAdditionalPreviews = b; return this; } + bool? WantsReturnTotalOnly; + public QueryType WithTotalOnly( bool b ) { WantsReturnTotalOnly = b; return this; } + bool? WantsReturnPlaytimeStats; + public QueryType WithPlaytimeStats( bool b ) { WantsReturnPlaytimeStats = b; return this; } + int? maxCacheAge; + public QueryType AllowCachedResponse( int maxSecondsAge ) { maxCacheAge = maxSecondsAge; return this; } + string language; + public QueryType InLanguage( string lang ) { language = lang; return this; } - public int ResultCount; - public int TotalCount; - - public bool CachedData; - - public IEnumerable Entries - { - get - { - var details = default( SteamUGCDetails_t ); - for ( uint i=0; i< ResultCount; i++ ) - { - if ( SteamUGC.Internal.GetQueryUGCResult( Handle, i, ref details ) ) - { - yield return UgcDetails.From( details, Handle ); - } - } - } - } - - - public void Dispose() - { - if ( Handle > 0 ) - { - SteamUGC.Internal.ReleaseQueryUGCRequest( Handle ); - Handle = 0; - } - } - } - - public struct UgcDetails - { - public PublishedFileId Id; - - public string Title; - public string Description; - public string[] Tags; - - - // - // TODO; - // - internal Result Result; // m_eResult enum EResult - internal WorkshopFileType FileType; // m_eFileType enum EWorkshopFileType - internal uint CreatorAppID; // m_nCreatorAppID AppId_t - internal uint ConsumerAppID; // m_nConsumerAppID AppId_t - - internal ulong SteamIDOwner; // m_ulSteamIDOwner uint64 - internal uint TimeCreated; // m_rtimeCreated uint32 - internal uint TimeUpdated; // m_rtimeUpdated uint32 - internal uint TimeAddedToUserList; // m_rtimeAddedToUserList uint32 - internal RemoteStoragePublishedFileVisibility Visibility; // m_eVisibility enum ERemoteStoragePublishedFileVisibility - internal bool Banned; // m_bBanned _Bool - internal bool AcceptedForUse; // m_bAcceptedForUse _Bool - internal bool TagsTruncated; // m_bTagsTruncated _Bool - internal ulong File; // m_hFile UGCHandle_t - internal ulong PreviewFile; // m_hPreviewFile UGCHandle_t - internal string PchFileName; // m_pchFileName char [260] - internal int FileSize; // m_nFileSize int32 - internal int PreviewFileSize; // m_nPreviewFileSize int32 - internal string URL; // m_rgchURL char [256] - internal uint VotesUp; // m_unVotesUp uint32 - internal uint VotesDown; // m_unVotesDown uint32 - internal float Score; // m_flScore float - internal uint NumChildren; // m_unNumChildren uint32 - - internal static UgcDetails From( SteamUGCDetails_t details, UGCQueryHandle_t handle ) - { - var d = new UgcDetails - { - Id = details.PublishedFileId, - FileType = details.FileType, - - Title = details.Title, - Description = details.Description, - Tags = details.Tags.Split( new[] { ',' }, StringSplitOptions.RemoveEmptyEntries ) - - }; - - return d; - } + List requiredTags; + bool? matchAnyTag; + List excludedTags; + Dictionary requiredKv; /// - /// A case insensitive check for tag + /// Found items must have at least one of the defined tags /// - public bool HasTag( string find ) - { - if ( Tags.Length == 0 ) return false; + public QueryType MatchAnyTag() { matchAnyTag = true; return this; } - return Tags.Contains( find, StringComparer.OrdinalIgnoreCase ); + /// + /// Found items must have all defined tags + /// + public QueryType MatchAllTags() { matchAnyTag = false; return this; } + + public QueryType WithTag( string tag ) + { + if ( requiredTags == null ) requiredTags = new List(); + requiredTags.Add( tag ); + return this; } + + public QueryType WithoutTag( string tag ) + { + if ( excludedTags == null ) excludedTags = new List(); + excludedTags.Add( tag ); + return this; + } + + void ApplyConstraints( UGCQueryHandle_t handle ) + { + if ( requiredTags != null ) + { + foreach ( var tag in requiredTags ) + SteamUGC.Internal.AddRequiredTag( handle, tag ); + } + + if ( excludedTags != null ) + { + foreach ( var tag in excludedTags ) + SteamUGC.Internal.AddExcludedTag( handle, tag ); + } + + if ( requiredKv != null ) + { + foreach ( var tag in requiredKv ) + SteamUGC.Internal.AddRequiredKeyValueTag( handle, tag.Key, tag.Value ); + } + + if ( matchAnyTag.HasValue ) + { + SteamUGC.Internal.SetMatchAnyTag( handle, matchAnyTag.Value ); + } + } + + #endregion + } } \ No newline at end of file diff --git a/Facepunch.Steamworks/Structs/UgcQueryUser.cs b/Facepunch.Steamworks/Structs/UgcQueryUser.cs new file mode 100644 index 0000000..84ee88c --- /dev/null +++ b/Facepunch.Steamworks/Structs/UgcQueryUser.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Steamworks.Data; +using QueryType = Steamworks.Ugc.UserQuery; + +namespace Steamworks.Ugc +{ + public struct UserQuery + { + public UserQuery( UgcType type, SteamId steamid = default ) : this() + { + if ( steamid == 0 ) + steamid = SteamClient.SteamId; + + this.steamid = steamid; + this.matchingType = type; + } + + SteamId steamid; + + UserUGCList userType; + UserUGCListSortOrder userSort; + UgcType matchingType; + + AppId consumerApp; + AppId creatorApp; + + public static UserQuery All => new UserQuery( UgcType.All, 0 ); + public static UserQuery Items => new UserQuery( UgcType.Items, 0 ); + /* + public static UserQuery ItemsMtx( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.Items_Mtx }; + public static UserQuery ItemsReadyToUse( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.Items_ReadyToUse }; + public static UserQuery Collections( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.Collections }; + public static UserQuery Artwork( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.Artwork }; + public static UserQuery Videos( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.Videos }; + public static UserQuery Screenshots( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.Screenshots }; + public static UserQuery AllGuides( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.AllGuides }; + public static UserQuery WebGuides( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.WebGuides }; + public static UserQuery IntegratedGuides( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.IntegratedGuides }; + public static UserQuery UsableInGame( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.UsableInGame }; + public static UserQuery ControllerBindings( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.ControllerBindings }; + public static UserQuery GameManagedItems( SteamId steamid = default ) => new UserQuery( steamid ) { matchingType = UGCMatchingUGCType.GameManagedItems }; + */ + + public UserQuery SortByCreationDate() { userSort = UserUGCListSortOrder.CreationOrderDesc; return this; } + public UserQuery SortByCreationDateAsc() { userSort = UserUGCListSortOrder.CreationOrderAsc; return this; } + public UserQuery SortByTitleAsc() { userSort = UserUGCListSortOrder.TitleAsc; return this; } + public UserQuery SortByUpdateDate() { userSort = UserUGCListSortOrder.LastUpdatedDesc; return this; } + public UserQuery SortBySubscriptionDate() { userSort = UserUGCListSortOrder.SubscriptionDateDesc; return this; } + public UserQuery SortByVoteScore() { userSort = UserUGCListSortOrder.VoteScoreDesc; return this; } + public UserQuery SortByModeration() { userSort = UserUGCListSortOrder.ForModeration; return this; } + + + public UserQuery GetPublished() { userType = UserUGCList.Published; return this; } + + public UserQuery FromUser( SteamId steamid ) + { + this.steamid = steamid; + return this; + } + + public UserQuery FromSelf() + { + this.steamid = SteamClient.SteamId; + return this; + } + + + public async Task GetPageAsync( int page ) + { + if ( page <= 0 ) throw new System.Exception( "page should be > 0" ); + + if ( consumerApp == 0 ) consumerApp = SteamClient.AppId; + if ( creatorApp == 0 ) creatorApp = consumerApp; + + UGCQueryHandle_t handle; + + handle = SteamUGC.Internal.CreateQueryUserUGCRequest( steamid.AccountId, userType, matchingType, userSort, creatorApp.Value, consumerApp.Value, (uint)page ); + + ApplyConstraints( handle ); + + var result = await SteamUGC.Internal.SendQueryUGCRequest( handle ); + if ( !result.HasValue ) + return null; + + if ( result.Value.Result != Steamworks.Result.OK ) + return null; + + return new ResultPage + { + Handle = result.Value.Handle, + ResultCount = (int) result.Value.NumResultsReturned, + TotalCount = (int)result.Value.TotalMatchingResults, + CachedData = result.Value.CachedData + }; + } + + #region SharedConstraints + public QueryType WithType( UgcType type ) { matchingType = type; return this; } + bool? WantsReturnOnlyIDs; + public QueryType WithOnlyIDs( bool b ) { WantsReturnOnlyIDs = b; return this; } + bool? WantsReturnKeyValueTags; + public QueryType WithKeyValueTag( bool b ) { WantsReturnKeyValueTags = b; return this; } + bool? WantsReturnLongDescription; + public QueryType WithLongDescription( bool b ) { WantsReturnLongDescription = b; return this; } + bool? WantsReturnMetadata; + public QueryType WithMetadata( bool b ) { WantsReturnMetadata = b; return this; } + bool? WantsReturnChildren; + public QueryType WithChildren( bool b ) { WantsReturnChildren = b; return this; } + bool? WantsReturnAdditionalPreviews; + public QueryType WithAdditionalPreviews( bool b ) { WantsReturnAdditionalPreviews = b; return this; } + bool? WantsReturnTotalOnly; + public QueryType WithTotalOnly( bool b ) { WantsReturnTotalOnly = b; return this; } + bool? WantsReturnPlaytimeStats; + public QueryType WithPlaytimeStats( bool b ) { WantsReturnPlaytimeStats = b; return this; } + int? maxCacheAge; + public QueryType AllowCachedResponse( int maxSecondsAge ) { maxCacheAge = maxSecondsAge; return this; } + string language; + public QueryType InLanguage( string lang ) { language = lang; return this; } + + List requiredTags; + bool? matchAnyTag; + List excludedTags; + Dictionary requiredKv; + + /// + /// Found items must have at least one of the defined tags + /// + public QueryType MatchAnyTag() { matchAnyTag = true; return this; } + + /// + /// Found items must have all defined tags + /// + public QueryType MatchAllTags() { matchAnyTag = false; return this; } + + public QueryType WithTag( string tag ) + { + if ( requiredTags == null ) requiredTags = new List(); + requiredTags.Add( tag ); + return this; + } + + public QueryType WithoutTag( string tag ) + { + if ( excludedTags == null ) excludedTags = new List(); + excludedTags.Add( tag ); + return this; + } + + void ApplyConstraints( UGCQueryHandle_t handle ) + { + if ( requiredTags != null ) + { + foreach ( var tag in requiredTags ) + SteamUGC.Internal.AddRequiredTag( handle, tag ); + } + + if ( excludedTags != null ) + { + foreach ( var tag in excludedTags ) + SteamUGC.Internal.AddExcludedTag( handle, tag ); + } + + if ( requiredKv != null ) + { + foreach ( var tag in requiredKv ) + SteamUGC.Internal.AddRequiredKeyValueTag( handle, tag.Key, tag.Value ); + } + + if ( matchAnyTag.HasValue ) + { + SteamUGC.Internal.SetMatchAnyTag( handle, matchAnyTag.Value ); + } + } + + #endregion + + } +} \ No newline at end of file diff --git a/Facepunch.Steamworks/Structs/UgcResultPage.cs b/Facepunch.Steamworks/Structs/UgcResultPage.cs new file mode 100644 index 0000000..eec1b31 --- /dev/null +++ b/Facepunch.Steamworks/Structs/UgcResultPage.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Steamworks.Data; + +namespace Steamworks.Ugc +{ + public struct ResultPage : System.IDisposable + { + internal UGCQueryHandle_t Handle; + + public int ResultCount; + public int TotalCount; + + public bool CachedData; + + public IEnumerable Entries + { + get + { + var details = default( SteamUGCDetails_t ); + for ( uint i=0; i< ResultCount; i++ ) + { + if ( SteamUGC.Internal.GetQueryUGCResult( Handle, i, ref details ) ) + { + yield return Result.From( details, Handle ); + } + } + } + } + + public void Dispose() + { + if ( Handle > 0 ) + { + SteamUGC.Internal.ReleaseQueryUGCRequest( Handle ); + Handle = 0; + } + } + } +} \ No newline at end of file diff --git a/Generator/Cleanup.cs b/Generator/Cleanup.cs index 6d43299..3dcbb52 100644 --- a/Generator/Cleanup.cs +++ b/Generator/Cleanup.cs @@ -21,6 +21,7 @@ public static string ConvertType( string type ) type = type.Replace( "PublishedFileId_t", "PublishedFileId" ); type = type.Replace( "LeaderboardSortMethod", "LeaderboardSort" ); type = type.Replace( "LeaderboardDisplayType", "LeaderboardDisplay" ); + type = type.Replace( "UGCMatchingUGCType", "UgcType" ); return type; } @@ -51,6 +52,7 @@ internal static string Expose( string name ) if ( name == "BroadcastUploadResult" ) return "public"; if ( name == "PublishedFileId" ) return "public"; if ( name == "Result" ) return "public"; + if ( name == "UgcType" ) return "public"; return "internal"; }