Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Facepunch.Steamworks/SteamApps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,68 @@ public static string CurrentBetaName
}
}

/// <summary>
/// Gets the total number of known app branches including the default "public" branch.
/// </summary>
/// <param name="availableCount">Returns the number of available beta branches.</param>
/// <param name="privateCount">Returns the number of private beta branches.</param>
/// <returns>The total number of beta branches.</returns>
public static int GetBetaCount( out int availableCount, out int privateCount )
{
availableCount = 0;
privateCount = 0;
return Internal.GetNumBetas( ref availableCount, ref privateCount );
}

/// <summary>
/// Gets detailed information about a specific beta branch by index.
/// </summary>
/// <param name="betaIndex">The index of the beta branch (0-based).</param>
/// <returns>Beta branch information, or null if the index is invalid.</returns>
public static BetaInformation? GetBetaInfo( int betaIndex )
{
uint flags = 0;
uint buildId = 0;

if ( !Internal.GetBetaInfo( betaIndex, ref flags, ref buildId, out var name, out var description ) )
return null;

return new BetaInformation
{
Name = name,
Description = description,
BuildId = buildId,
Flags = (BetaBranchFlags)flags
};
}

/// <summary>
/// Gets information about all available beta branches.
/// </summary>
/// <returns>An enumerable of beta branch information.</returns>
public static IEnumerable<BetaInformation> GetBetaInformation()
{
int totalCount = GetBetaCount( out var availableCount, out var privateCount );

for ( int i = 0; i < totalCount; i++ )
{
var betaInfo = GetBetaInfo( i );
if ( betaInfo.HasValue )
yield return betaInfo.Value;
}
}

/// <summary>
/// Selects the specified beta branch as active for this app.
/// The game may need to restart so Steam can update to that branch.
/// </summary>
/// <param name="betaName">The name of the beta branch to activate. Use null or empty string to select the default "public" branch.</param>
/// <returns>True if the beta branch was successfully set as active.</returns>
public static bool SetActiveBeta( string betaName )
{
return Internal.SetActiveBeta( betaName ?? "" );
}

/// <summary>
/// Force verify game content on next launch.
/// <para>
Expand Down
120 changes: 120 additions & 0 deletions Facepunch.Steamworks/SteamRemoteStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,5 +184,125 @@ public static IEnumerable<string> Files
}
}

/// <summary>
/// Share a file with the Steam community.
/// The file must be in the user's Steam Cloud storage.
/// </summary>
/// <param name="filename">The name of the file to share</param>
/// <returns>A task that returns the shared file UGC handle on success</returns>
public static async Task<Data.Ugc?> FileShareAsync( string filename )
{
var result = await Internal.FileShare( filename );
if ( !result.HasValue || result.Value.Result != Result.OK )
return null;

return new Data.Ugc { Handle = result.Value.File };
}

/// <summary>
/// Downloads UGC content to Steam's local cache.
/// </summary>
/// <param name="ugc">The UGC handle to download</param>
/// <param name="priority">Download priority (higher values get downloaded first)</param>
/// <returns>A task that returns the download result on completion</returns>
public static async Task<UgcDownloadResult?> UgcDownloadAsync( Data.Ugc ugc, uint priority = 0 )
{
var result = await Internal.UGCDownload( ugc.Handle, priority );
if ( !result.HasValue || result.Value.Result != Result.OK )
return null;

return new UgcDownloadResult
{
Result = result.Value.Result,
Handle = new Data.Ugc { Handle = result.Value.File },
AppId = result.Value.AppID,
SizeInBytes = result.Value.SizeInBytes,
Filename = result.Value.PchFileNameUTF8(),
Owner = result.Value.SteamIDOwner
};
}

/// <summary>
/// Downloads UGC content to a specific location on disk.
/// </summary>
/// <param name="ugc">The UGC handle to download</param>
/// <param name="location">The local file path to download to</param>
/// <param name="priority">Download priority (higher values get downloaded first)</param>
/// <returns>A task that returns the download result on completion</returns>
public static async Task<UgcDownloadResult?> UgcDownloadToLocationAsync( Data.Ugc ugc, string location, uint priority = 0 )
{
var result = await Internal.UGCDownloadToLocation( ugc.Handle, location, priority );
if ( !result.HasValue || result.Value.Result != Result.OK )
return null;

return new UgcDownloadResult
{
Result = result.Value.Result,
Handle = new Data.Ugc { Handle = result.Value.File },
AppId = result.Value.AppID,
SizeInBytes = result.Value.SizeInBytes,
Filename = result.Value.PchFileNameUTF8(),
Owner = result.Value.SteamIDOwner
};
}

/// <summary>
/// Reads the contents of a UGC file into a byte array.
/// </summary>
/// <param name="ugc">The UGC handle to read from</param>
/// <param name="maxSize">Maximum size to read (default 10MB for safety)</param>
/// <returns>The data read from the UGC file, or null if failed</returns>
public static unsafe byte[] UgcRead( Data.Ugc ugc, int maxSize = 10 * 1024 * 1024 )
{
// Try to get size from download progress first
var progress = GetUgcDownloadProgress( ugc );
var size = progress?.BytesExpected ?? maxSize;

if ( size <= 0 || size > maxSize )
size = maxSize;

var buffer = new byte[size];

fixed ( byte* ptr = buffer )
{
// Use Close action (2) to close the file handle after reading
var bytesRead = Internal.UGCRead( ugc.Handle, (IntPtr)ptr, size, 0, (UGCReadAction)2 );
if ( bytesRead <= 0 )
return null;

// Trim buffer to actual bytes read
if ( bytesRead != size )
{
var trimmedBuffer = new byte[bytesRead];
Array.Copy( buffer, trimmedBuffer, bytesRead );
return trimmedBuffer;
}

return buffer;
}
}

/// <summary>
/// Gets the download progress for a UGC file.
/// </summary>
/// <param name="ugc">The UGC handle to check progress for</param>
/// <returns>Download progress information, or null if not downloading</returns>
public static UgcDownloadProgress? GetUgcDownloadProgress( Data.Ugc ugc )
{
var bytesDownloaded = 0;
var bytesExpected = 0;

if ( !Internal.GetUGCDownloadProgress( ugc.Handle, ref bytesDownloaded, ref bytesExpected ) )
return null;

return new UgcDownloadProgress
{
Handle = ugc,
BytesDownloaded = bytesDownloaded,
BytesExpected = bytesExpected,
Progress = bytesExpected > 0 ? (float)bytesDownloaded / bytesExpected : 0f
};
}

}
}
59 changes: 59 additions & 0 deletions Facepunch.Steamworks/Structs/BetaInformation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;


namespace Steamworks.Data
{
/// <summary>
/// Provides information about a beta branch.
/// </summary>
public struct BetaInformation
{
/// <summary>
/// The name of the beta branch.
/// </summary>
public string Name { get; internal set; }

/// <summary>
/// The description of the beta branch.
/// </summary>
public string Description { get; internal set; }

/// <summary>
/// The build ID of the beta branch.
/// </summary>
public uint BuildId { get; internal set; }

/// <summary>
/// The flags indicating the status of the beta branch.
/// </summary>
internal BetaBranchFlags Flags { get; set; }

/// <summary>
/// Whether this is the default branch.
/// </summary>
public bool IsDefault => (Flags & BetaBranchFlags.Default) != 0;

/// <summary>
/// Whether this beta branch is available for selection.
/// </summary>
public bool IsAvailable => (Flags & BetaBranchFlags.Available) != 0;

/// <summary>
/// Whether this beta branch is private (requires password or invitation).
/// </summary>
public bool IsPrivate => (Flags & BetaBranchFlags.Private) != 0;

/// <summary>
/// Whether this beta branch is currently selected by the user.
/// </summary>
public bool IsSelected => (Flags & BetaBranchFlags.Selected) != 0;

/// <summary>
/// Whether this beta branch is currently installed.
/// </summary>
public bool IsInstalled => (Flags & BetaBranchFlags.Installed) != 0;
}
}
18 changes: 18 additions & 0 deletions Facepunch.Steamworks/Structs/Ugc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ namespace Steamworks.Data
public struct Ugc
{
internal UGCHandle_t Handle;

/// <summary>
/// Create a Ugc from a ulong handle value
/// </summary>
public Ugc( ulong handle )
{
Handle = handle;
}

/// <summary>
/// Implicit conversion from ulong
/// </summary>
public static implicit operator Ugc( ulong handle ) => new Ugc( handle );

/// <summary>
/// Get the underlying handle value
/// </summary>
public ulong Value => Handle.Value;
}
}

Expand Down
35 changes: 35 additions & 0 deletions Facepunch.Steamworks/Structs/UgcDownloadProgress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;

namespace Steamworks.Data
{
/// <summary>
/// Download progress information for a UGC file
/// </summary>
public struct UgcDownloadProgress
{
/// <summary>
/// The UGC handle being downloaded
/// </summary>
public Ugc Handle { get; internal set; }

/// <summary>
/// Number of bytes downloaded so far
/// </summary>
public int BytesDownloaded { get; internal set; }

/// <summary>
/// Total number of bytes expected to download
/// </summary>
public int BytesExpected { get; internal set; }

/// <summary>
/// Download progress as a value between 0.0 and 1.0
/// </summary>
public float Progress { get; internal set; }

/// <summary>
/// Whether the download is complete
/// </summary>
public bool IsComplete => BytesDownloaded >= BytesExpected && BytesExpected > 0;
}
}
40 changes: 40 additions & 0 deletions Facepunch.Steamworks/Structs/UgcDownloadResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;

namespace Steamworks.Data
{
/// <summary>
/// Result from downloading UGC content
/// </summary>
public struct UgcDownloadResult
{
/// <summary>
/// The result of the download operation
/// </summary>
public Result Result { get; internal set; }

/// <summary>
/// The UGC handle that was downloaded
/// </summary>
public Ugc Handle { get; internal set; }

/// <summary>
/// The App ID that created this content
/// </summary>
public AppId AppId { get; internal set; }

/// <summary>
/// Size of the downloaded file in bytes
/// </summary>
public int SizeInBytes { get; internal set; }

/// <summary>
/// Name of the downloaded file
/// </summary>
public string Filename { get; internal set; }

/// <summary>
/// Steam ID of the user who created this content
/// </summary>
public SteamId Owner { get; internal set; }
}
}