Basic Ugc Querying

This commit is contained in:
Garry Newman 2019-04-17 16:41:06 +01:00
parent 6dd5164f34
commit e9f5898b6c
12 changed files with 342 additions and 14 deletions

View File

@ -91,6 +91,7 @@
<ItemGroup>
<Compile Include="UserTest.cs" />
<Compile Include="UserStatsTest.cs" />
<Compile Include="UgcTest.cs" />
<Compile Include="UtilsTest.cs" />
<Compile Include="AppTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -0,0 +1,46 @@
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Steamworks.Data;
namespace Steamworks
{
[TestClass]
[DeploymentItem( "steam_api64.dll" )]
public class UgcTests
{
[TestMethod]
public async Task QueryAll()
{
var q = UgcQuery.All();
var result = await q.GetPageAsync( 1 );
Assert.IsNotNull( result );
Console.WriteLine( $"ResultCount: {result?.ResultCount}" );
Console.WriteLine( $"TotalCount: {result?.TotalCount}" );
}
[TestMethod]
public async Task QueryAllFromFriends()
{
var q = UgcQuery.All()
.CreatedByFriends();
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}" );
}
}
}
}

View File

@ -63,7 +63,7 @@ public void CurrentBatteryPower()
[TestMethod]
public void AppId()
{
var cnt = SteamUtils.AppId;
var cnt = SteamClient.AppId;
Assert.IsTrue( cnt.Value > 0 );

View File

@ -18,8 +18,8 @@ public ISteamUGC( bool server = false ) : base( server )
public override void InitInternals()
{
_CreateQueryUserUGCRequest = Marshal.GetDelegateForFunctionPointer<FCreateQueryUserUGCRequest>( Marshal.ReadIntPtr( VTable, 0) );
_CreateQueryAllUGCRequest1 = Marshal.GetDelegateForFunctionPointer<FCreateQueryAllUGCRequest1>( Marshal.ReadIntPtr( VTable, 8) );
_CreateQueryAllUGCRequest2 = Marshal.GetDelegateForFunctionPointer<FCreateQueryAllUGCRequest2>( Marshal.ReadIntPtr( VTable, 16) );
_CreateQueryAllUGCRequest1 = Marshal.GetDelegateForFunctionPointer<FCreateQueryAllUGCRequest1>( Marshal.ReadIntPtr( VTable, 16) );
_CreateQueryAllUGCRequest2 = Marshal.GetDelegateForFunctionPointer<FCreateQueryAllUGCRequest2>( Marshal.ReadIntPtr( VTable, 8) );
_CreateQueryUGCDetailsRequest = Marshal.GetDelegateForFunctionPointer<FCreateQueryUGCDetailsRequest>( Marshal.ReadIntPtr( VTable, 24) );
_SendQueryUGCRequest = Marshal.GetDelegateForFunctionPointer<FSendQueryUGCRequest>( Marshal.ReadIntPtr( VTable, 32) );
_GetQueryUGCResult = Marshal.GetDelegateForFunctionPointer<FGetQueryUGCResult>( Marshal.ReadIntPtr( VTable, 40) );
@ -153,13 +153,13 @@ internal UGCQueryHandle_t CreateQueryUGCDetailsRequest( [In,Out] PublishedFileId
#region FunctionMeta
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
[return: MarshalAs( UnmanagedType.I1 )]
private delegate bool FGetQueryUGCResult( IntPtr self, UGCQueryHandle_t handle, uint index, [In,Out] SteamUGCDetails_t[] pDetails );
private delegate bool FGetQueryUGCResult( IntPtr self, UGCQueryHandle_t handle, uint index, ref SteamUGCDetails_t pDetails );
private FGetQueryUGCResult _GetQueryUGCResult;
#endregion
internal bool GetQueryUGCResult( UGCQueryHandle_t handle, uint index, [In,Out] SteamUGCDetails_t[] pDetails )
internal bool GetQueryUGCResult( UGCQueryHandle_t handle, uint index, ref SteamUGCDetails_t pDetails )
{
return _GetQueryUGCResult( Self, handle, index, pDetails );
return _GetQueryUGCResult( Self, handle, index, ref pDetails );
}
#region FunctionMeta

View File

@ -56,7 +56,7 @@ internal static ISteamMatchmakingServers Internal
public Base()
{
AppId = SteamUtils.AppId; // Default AppId is this
AppId = SteamClient.AppId; // Default AppId is this
}
/// <summary>

View File

@ -25,6 +25,8 @@ public static void Init( uint appid )
throw new System.Exception( "SteamApi_Init returned false. Steam isn't running, couldn't find Steam, AppId is ureleased, Don't own AppId." );
}
AppId = appid;
initialized = true;
SteamApps.InstallEvents();
@ -108,5 +110,10 @@ internal static void UnregisterCallback( IntPtr intPtr )
/// gets the status of the current user
/// </summary>
public static FriendState State => SteamFriends.Internal.GetPersonaState();
/// <summary>
/// returns the appID of the current process
/// </summary>
public static AppId AppId { get; internal set; }
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Steamworks.Data;
namespace Steamworks
{
/// <summary>
/// Functions for accessing and manipulating Steam user information.
/// This is also where the APIs for Steam Voice are exposed.
/// </summary>
public static class SteamUGC
{
static ISteamUGC _internal;
internal static ISteamUGC Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamUGC();
}
return _internal;
}
}
}
}

View File

@ -121,11 +121,6 @@ public static bool GetImageSize( int image, out uint width, out uint height )
/// </summary>
public static float CurrentBatteryPower => Math.Min( Internal.GetCurrentBatteryPower() / 100, 1.0f );
/// <summary>
/// returns the appID of the current process
/// </summary>
public static AppId AppId => Internal.GetAppID();
static NotificationPosition overlayNotificationPosition = NotificationPosition.BottomRight;
/// <summary>

View File

@ -40,7 +40,7 @@ public override string ToString()
/// <summary>
/// Return true if this user is playing the game we're running
/// </summary>
public bool IsPlayingThisGame => GameInfo?.GameID == SteamUtils.AppId;
public bool IsPlayingThisGame => GameInfo?.GameID == SteamClient.AppId;
/// <summary>
/// Returns true if this friend is online

View File

@ -0,0 +1,241 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Steamworks.Data
{
public struct UgcQuery
{
UGCMatchingUGCType matching;
UserUGCList userGc;
UGCQuery queryType;
AppId consumerApp;
AppId creatorApp;
List<string> requiredTags;
bool? matchAnyTag;
List<string> excludedTags;
Dictionary<string, string> 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 ReturnOnlyIDs( bool b) { WantsReturnOnlyIDs = b; return this; }
public UgcQuery ReturnKeyValueTag( bool b ) { WantsReturnKeyValueTags = b; return this; }
public UgcQuery ReturnLongDescription( bool b ) { WantsReturnLongDescription = b; return this; }
public UgcQuery ReturnMetadata( bool b ) { WantsReturnMetadata = b; return this; }
public UgcQuery ReturnChildren( bool b ) { WantsReturnChildren = b; return this; }
public UgcQuery ReturnAdditionalPreviews( bool b ) { WantsReturnAdditionalPreviews = b; return this; }
public UgcQuery ReturnTotalOnly( bool b ) { WantsReturnTotalOnly = b; return this; }
public UgcQuery ReturnPlaytimeStats( bool b ) { WantsReturnPlaytimeStats = b; return this; }
public UgcQuery AllowCachedResponse( int maxSecondsAge ) { maxCacheAge = maxSecondsAge; return this; }
public UgcQuery InLanguage( string lang ) { language = lang; return this; }
public UgcQuery MatchAnyTag( bool b ) { matchAnyTag = b; return this; }
public UgcQuery WithTag( string tag )
{
if ( requiredTags == null ) requiredTags = new List<string>();
requiredTags.Add( tag );
return this;
}
public UgcQuery WithoutTag( string tag )
{
if ( excludedTags == null ) excludedTags = new List<string>();
excludedTags.Add( tag );
return this;
}
public async Task<UgcQueryPage?> 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.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 );
}
//
// TODO - add more
//
}
var result = await SteamUGC.Internal.SendQueryUGCRequest( handle );
if ( !result.HasValue )
return null;
if ( result.Value.Result != Result.OK )
return null;
return new UgcQueryPage
{
Handle = result.Value.Handle,
ResultCount = (int) result.Value.NumResultsReturned,
TotalCount = (int)result.Value.TotalMatchingResults,
CachedData = result.Value.CachedData
};
}
}
public struct UgcQueryPage : System.IDisposable
{
internal UGCQueryHandle_t Handle;
public int ResultCount;
public int TotalCount;
public bool CachedData;
public IEnumerable<UgcDetails> 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;
//
// 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 string Tags; // m_rgchTags char [1025]
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,
};
return d;
}
}
}

View File

@ -80,6 +80,12 @@ void WriteFunctionPointerReader( CodeParser.Class clss )
Swap( clss, "GetGlobalStatHistory1", "GetGlobalStatHistory2", locations );
}
if ( clss.Name == "ISteamUGC" )
{
Swap( clss, "CreateQueryAllUGCRequest1", "CreateQueryAllUGCRequest2", locations );
}
StartBlock( $"public override void InitInternals()" );
{
for (int i=0; i< clss.Functions.Count; i++ )

View File

@ -64,7 +64,7 @@ public virtual bool IsVector
if ( VarName == "psteamIDClans" ) return true;
if ( VarName == "pScoreDetails" ) return true;
if ( VarName == "prgUsers" ) return true;
if ( VarName == "pDetails" ) return true;
if ( VarName == "pDetails" && Func == "GetDownloadedLeaderboardEntry" ) return true;
if ( VarName == "pData" && NativeType.EndsWith( "*" ) && Func.StartsWith( "GetGlobalStatHistory" ) ) return true;
if ( NativeType.EndsWith( "**" ) ) return true;