Fixed downloading leaderboard file attachments

This commit is contained in:
James King 2017-05-16 16:35:37 +01:00
parent 836f6e285c
commit 7e3527799a
5 changed files with 149 additions and 13 deletions

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
@ -169,5 +170,72 @@ public void AddScoresCallback()
} }
} }
} }
[TestMethod]
public void AddFileAttachment()
{
using ( var client = new Steamworks.Client( 252490 ) )
{
var board = client.GetLeaderboard( "TestLeaderboard", Steamworks.Client.LeaderboardSortMethod.Ascending, Steamworks.Client.LeaderboardDisplayType.Numeric );
while ( !board.IsValid )
{
Thread.Sleep( 10 );
client.Update();
}
Assert.IsTrue( board.IsValid );
Assert.IsFalse( board.IsError );
var done = false;
const int score = 5678;
const string attachment = "Hello world!";
var file = client.RemoteStorage.CreateFile( "score/example.txt" );
file.WriteAllText( attachment );
Assert.IsTrue( board.AddScore( false, score, null, ( success, result ) =>
{
Assert.IsTrue( success );
Assert.IsTrue( result.ScoreChanged );
Assert.IsTrue( board.AttachRemoteFile( file, attached =>
{
Assert.IsTrue( attached );
done = true;
} ) );
} ) );
while ( !done )
{
Thread.Sleep( 10 );
client.Update();
}
done = false;
Assert.IsTrue( board.FetchScores( Steamworks.Leaderboard.RequestType.GlobalAroundUser, 0, 0, ( success, entries ) =>
{
Assert.AreEqual( 1, entries.Length );
Assert.IsNotNull( entries[0].AttachedFile );
Assert.IsTrue( entries[0].AttachedFile.Download( downloaded =>
{
Assert.IsTrue( downloaded );
Assert.AreEqual( attachment, entries[0].AttachedFile.ReadAllText() );
done = true;
} ) );
} ) );
while ( !done )
{
Thread.Sleep( 10 );
client.Update();
}
}
}
} }
} }

View File

@ -177,22 +177,20 @@ public bool AttachRemoteFile( RemoteFile file, AttachRemoteFileCallback callback
if ( file.IsShared ) if ( file.IsShared )
{ {
client.native.userstats.AttachLeaderboardUGC( BoardId, file.UGCHandle, ( result, error ) => var handle = client.native.userstats.AttachLeaderboardUGC( BoardId, file.UGCHandle, ( result, error ) =>
{ {
callback?.Invoke( !error && result.Result == Result.OK ); callback?.Invoke( !error && result.Result == Result.OK );
} ); } );
return true;
return handle.CallResultHandle != 0;
} }
file.Share( success => file.Share( success =>
{ {
if ( !success || !file.IsShared ) if ( !success || !file.IsShared || !AttachRemoteFile( file, callback ) )
{ {
callback?.Invoke( false ); callback?.Invoke( false );
return;
} }
AttachRemoteFile( file, callback );
} ); } );
return true; return true;
} }
@ -226,7 +224,8 @@ private unsafe void ReadScores( LeaderboardScoresDownloaded_t result, List<Entry
Score = entry.Score, Score = entry.Score,
SteamId = entry.SteamIDUser, SteamId = entry.SteamIDUser,
SubScores = entry.CDetails == 0 ? null : subEntriesBuffer.Take( entry.CDetails ).ToArray(), SubScores = entry.CDetails == 0 ? null : subEntriesBuffer.Take( entry.CDetails ).ToArray(),
Name = client.Friends.GetName( entry.SteamIDUser ) Name = client.Friends.GetName( entry.SteamIDUser ),
AttachedFile = (entry.UGC >> 32) == 0xffffffff ? null : new RemoteFile( client.RemoteStorage, entry.UGC )
} ); } );
} }
} }
@ -288,7 +287,7 @@ public struct Entry
public int Score; public int Score;
public int[] SubScores; public int[] SubScores;
public int GlobalRank; public int GlobalRank;
public RemoteFile AttachedFile;
/// <summary> /// <summary>
/// Note that the player's name might not be immediately available. /// Note that the player's name might not be immediately available.

View File

@ -125,11 +125,18 @@ public class RemoteFile
private UGCHandle_t _handle; private UGCHandle_t _handle;
private ulong _ownerId; private ulong _ownerId;
private bool _isDownloading;
private byte[] _downloadedData;
/// <summary> /// <summary>
/// Check if the file exists. /// Check if the file exists.
/// </summary> /// </summary>
public bool Exists { get; internal set; } public bool Exists { get; internal set; }
public bool IsDownloading { get { return _isUgc && _isDownloading && _downloadedData == null; } }
public bool IsDownloaded { get { return !_isUgc || _downloadedData != null; } }
/// <summary> /// <summary>
/// If true, the file is available for other users to download. /// If true, the file is available for other users to download.
/// </summary> /// </summary>
@ -137,6 +144,8 @@ public class RemoteFile
internal UGCHandle_t UGCHandle { get { return _handle; } } internal UGCHandle_t UGCHandle { get { return _handle; } }
public ulong SharingId { get { return UGCHandle.Value; } }
/// <summary> /// <summary>
/// Name and path of the file. /// Name and path of the file.
/// </summary> /// </summary>
@ -201,7 +210,6 @@ internal RemoteFile( RemoteStorage r, string name, ulong ownerId, int sizeInByte
/// <summary> /// <summary>
/// Creates a <see cref="RemoteFileWriteStream"/> used to write to this file. /// Creates a <see cref="RemoteFileWriteStream"/> used to write to this file.
/// </summary> /// </summary>
/// <returns></returns>
public RemoteFileWriteStream OpenWrite() public RemoteFileWriteStream OpenWrite()
{ {
return new RemoteFileWriteStream( remoteStorage, this ); return new RemoteFileWriteStream( remoteStorage, this );
@ -227,6 +235,62 @@ public void WriteAllText( string text, Encoding encoding = null )
WriteAllBytes( encoding.GetBytes( text ) ); WriteAllBytes( encoding.GetBytes( text ) );
} }
/// <summary>
/// Callback invoked by <see cref="RemoteFile.Download"/> when a file download is complete.
/// </summary>
public delegate void DownloadCallback( bool success );
/// <summary>
/// Gets the number of bytes downloaded and the total number of bytes expected while
/// this file is downloading.
/// </summary>
/// <returns>True if the file is downloading</returns>
public bool GetDownloadProgress( out int bytesDownloaded, out int bytesExpected )
{
return remoteStorage.native.GetUGCDownloadProgress( _handle, out bytesDownloaded, out bytesExpected );
}
/// <summary>
/// Attempts to start downloading a shared file.
/// </summary>
/// <returns>True if the download has successfully started</returns>
public bool Download( DownloadCallback callback = null )
{
if ( !_isUgc ) return false;
if ( _isDownloading ) return false;
if ( IsDownloaded ) return false;
_isDownloading = true;
remoteStorage.native.UGCDownload( _handle, 1000, ( result, error ) =>
{
_isDownloading = false;
if ( error || result.Result != Result.OK )
{
callback?.Invoke( false );
return;
}
_ownerId = result.SteamIDOwner;
_sizeInBytes = result.SizeInBytes;
_fileName = result.PchFileName;
unsafe
{
_downloadedData = new byte[_sizeInBytes];
fixed ( byte* bufferPtr = _downloadedData )
{
remoteStorage.native.UGCRead( _handle, (IntPtr) bufferPtr, _sizeInBytes, 0, UGCReadAction.ontinueReading );
}
}
callback?.Invoke( true );
} );
return true;
}
/// <summary> /// <summary>
/// Opens a stream used to read from this file. /// Opens a stream used to read from this file.
/// </summary> /// </summary>
@ -243,8 +307,8 @@ public unsafe byte[] ReadAllBytes()
{ {
if ( _isUgc ) if ( _isUgc )
{ {
// Need to download if ( !IsDownloaded ) throw new Exception( "Cannot read a file that hasn't been downloaded." );
throw new NotImplementedException(); return _downloadedData;
} }
var size = SizeInBytes; var size = SizeInBytes;

View File

@ -14929,7 +14929,7 @@ public static void RegisterCallback( Facepunch.Steamworks.BaseSteamworks steamwo
} }
} }
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 8 )]
internal struct LeaderboardEntry_t internal struct LeaderboardEntry_t
{ {
public ulong SteamIDUser; // m_steamIDUser class CSteamID public ulong SteamIDUser; // m_steamIDUser class CSteamID

View File

@ -30,6 +30,11 @@ public class TypeDef
"CCallbackBase" "CCallbackBase"
}; };
public readonly static string[] ForceLargePackStructs = new string[]
{
"LeaderboardEntry_t"
};
void Structs() void Structs()
{ {
foreach ( var c in def.structs ) foreach ( var c in def.structs )
@ -42,7 +47,7 @@ void Structs()
int defaultPack = 8; int defaultPack = 8;
if ( c.Fields.Any( x => x.Type.Contains( "class CSteamID" ) ) ) if ( c.Fields.Any( x => x.Type.Contains( "class CSteamID" ) ) && !ForceLargePackStructs.Contains( c.Name ) )
defaultPack = 4; defaultPack = 4;
// //