using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using Steamworks.Data; namespace Steamworks { /// /// Functions for accessing and manipulating Steam user information. /// This is also where the APIs for Steam Voice are exposed. /// public class SteamUGC : SteamSharedClass { internal static ISteamUGC Internal => Interface as ISteamUGC; internal override void InitializeInterface( bool server ) { SetInterface( server, new ISteamUGC( server ) ); InstallEvents( server ); } internal static void InstallEvents( bool server ) { Dispatch.Install( x => { if (x.AppID == SteamClient.AppId) { OnDownloadItemResult?.Invoke(x.Result, x.PublishedFileId); } }, server ); Dispatch.Install(x => { if (x.AppID == SteamClient.AppId) { GlobalOnItemInstalled?.Invoke(x.PublishedFileId); } }, server); } /// /// Posted after Download call /// public static event Action OnDownloadItemResult; public static async Task DeleteFileAsync( PublishedFileId fileId ) { var r = await Internal.DeleteItem( fileId ); return r?.Result == Result.OK; } /// /// Start downloading this item. You'll get notified of completion via OnDownloadItemResult. /// /// The ID of the file you want to download /// If true this should go straight to the top of the download list /// true if nothing went wrong and the download is started public static bool Download( PublishedFileId fileId, bool highPriority = false ) { return Internal.DownloadItem( fileId, highPriority ); } /// /// Will attempt to download this item asyncronously - allowing you to instantly react to its installation /// /// The ID of the file you want to download /// An optional callback /// Allows you to send a message to cancel the download anywhere during the process /// How often to call the progress function /// true if downloaded and installed correctly public static async Task DownloadAsync( PublishedFileId fileId, Action progress = null, int millisecondsUpdateDelay = 60, CancellationToken? ct = null) { var item = new Steamworks.Ugc.Item( fileId ); var cancellationToken = ct ?? new CancellationTokenSource(TimeSpan.FromSeconds(60)).Token; async Task waitOrCancel() { cancellationToken.ThrowIfCancellationRequested(); await Task.Delay(millisecondsUpdateDelay); } progress?.Invoke( 0.0f ); Result downloadStartResult = Result.None; void onDownloadFinished(Result r, ulong id) { if (id != item.Id) { return; } downloadStartResult = r; } OnDownloadItemResult += onDownloadFinished; if (!Download(fileId, highPriority: true)) { return item.IsInstalled; } await Task.Delay(500); try { while (true) { cancellationToken.ThrowIfCancellationRequested(); progress?.Invoke(item.DownloadAmount); if (downloadStartResult != Result.None) { if (downloadStartResult != Result.OK) { return false; } break; } if (!item.IsDownloadPending && !item.IsDownloading) { if (item.IsInstalled) { break; } if (!Download(fileId, highPriority: true)) { return item.IsInstalled; } } await Task.Delay( millisecondsUpdateDelay ); } } finally { OnDownloadItemResult -= onDownloadFinished; } progress?.Invoke( 1.0f ); return item.IsInstalled; } /// /// Utility function to fetch a single item. Internally this uses Ugc.FileQuery - /// which you can use to query multiple items if you need to. /// public static async Task QueryFileAsync( PublishedFileId fileId ) { var result = await Ugc.Query.All .WithFileId( fileId ) .GetPageAsync( 1 ); if ( !result.HasValue || result.Value.ResultCount != 1 ) return null; var item = result.Value.Entries.First(); result.Value.Dispose(); return item; } public static async Task StartPlaytimeTracking(PublishedFileId fileId) { var result = await Internal.StartPlaytimeTracking(new[] {fileId}, 1); return result.Value.Result == Result.OK; } public static async Task StopPlaytimeTracking(PublishedFileId fileId) { var result = await Internal.StopPlaytimeTracking(new[] {fileId}, 1); return result.Value.Result == Result.OK; } public static async Task StopPlaytimeTrackingForAllItems() { var result = await Internal.StopPlaytimeTrackingForAllItems(); return result.Value.Result == Result.OK; } public static Action GlobalOnItemInstalled; public static uint NumSubscribedItems { get { return Internal.GetNumSubscribedItems(); } } public static PublishedFileId[] GetSubscribedItems() { uint numSubscribed = NumSubscribedItems; PublishedFileId[] ids = new PublishedFileId[numSubscribed]; Internal.GetSubscribedItems(ids, numSubscribed); return ids; } } }