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 { /// /// Exposes a wide range of information and actions for applications and Downloadable Content (DLC). /// public class SteamApps : SteamSharedClass { internal static ISteamApps? Internal => Interface as ISteamApps; internal override bool InitializeInterface( bool server ) { SetInterface( server, new ISteamApps( server ) ); if ( Interface is null || Interface.Self == IntPtr.Zero ) return false; return true; } internal static void InstallEvents() { Dispatch.Install( x => OnDlcInstalled?.Invoke( x.AppID ) ); Dispatch.Install( x => OnNewLaunchParameters?.Invoke() ); } /// /// Posted after the user gains ownership of DLC and that DLC is installed. /// public static event Action? OnDlcInstalled; /// /// Posted after the user gains executes a Steam URL with command line or query parameters /// such as steam://run/appid//-commandline/?param1=value1(and)param2=value2(and)param3=value3 etc /// while the game is already running. The new params can be queried /// with GetLaunchQueryParam and GetLaunchCommandLine. /// public static event Action? OnNewLaunchParameters; /// /// Gets whether or not the active user is subscribed to the current App ID. /// public static bool IsSubscribed => Internal != null && Internal.BIsSubscribed(); /// /// Gets whether or not the user borrowed this game via Family Sharing. If true, call GetAppOwner() to get the lender SteamID. /// public static bool IsSubscribedFromFamilySharing => Internal != null && Internal.BIsSubscribedFromFamilySharing(); /// /// Gets whether or not the license owned by the user provides low violence depots. /// Low violence depots are useful for copies sold in countries that have content restrictions /// public static bool IsLowViolence => Internal != null && Internal.BIsLowViolence(); /// /// Gets whether or not the current App ID license is for Cyber Cafes. /// public static bool IsCybercafe => Internal != null && Internal.BIsCybercafe(); /// /// Gets whether or not the user has a VAC ban on their account. /// public static bool IsVACBanned => Internal != null && Internal.BIsVACBanned(); /// /// Gets the current language that the user has set. /// This falls back to the Steam UI language if the user hasn't explicitly picked a language for the title. /// public static string? GameLanguage => Internal?.GetCurrentGameLanguage(); /// /// Gets a list of the languages the current app supports. /// public static string[]? AvailableLanguages => Internal?.GetAvailableGameLanguages().Split( new[] { ',' }, StringSplitOptions.RemoveEmptyEntries ); /// /// Gets whether or not the active user is subscribed to a specified App ID. /// Only use this if you need to check ownership of another game related to yours, a demo for example. /// /// The App ID of the DLC to check. public static bool IsSubscribedToApp( AppId appid ) => Internal != null && Internal.BIsSubscribedApp( appid.Value ); /// /// Gets whether or not the user owns a specific DLC and if the DLC is installed. /// /// The App ID of the DLC to check. public static bool IsDlcInstalled( AppId appid ) => Internal != null && Internal.BIsDlcInstalled( appid.Value ); /// /// Returns the time of the purchase of the app. /// /// The App ID to check the purchase time for. public static DateTime PurchaseTime( AppId appid = default ) { if (Internal is null) { return default; } if ( appid == 0 ) appid = SteamClient.AppId; return Epoch.ToDateTime(Internal.GetEarliestPurchaseUnixTime(appid.Value ) ); } /// /// Checks if the user is subscribed to the current app through a free weekend. /// This function will return false for users who have a retail or other type of license. /// Before using, please ask your Valve technical contact how to package and secure your free weekened. /// public static bool IsSubscribedFromFreeWeekend => Internal != null && Internal.BIsSubscribedFromFreeWeekend(); /// /// Returns metadata for all available DLC. /// public static IEnumerable DlcInformation() { var appid = default( AppId ); var available = false; if (Internal is null) { yield break; } int dlcCount = Internal.GetDLCCount(); for ( int i = 0; i < dlcCount; i++ ) { if (Internal is null) { yield break; } if ( !Internal.BGetDLCDataByIndex( i, ref appid, ref available, out var strVal ) ) continue; yield return new DlcInformation { AppId = appid.Value, Name = strVal, Available = available }; } } /// /// Install control for optional DLC. /// /// The App ID of the DLC to install. public static void InstallDlc( AppId appid ) => Internal?.InstallDLC( appid.Value ); /// /// Uninstall control for optional DLC. /// /// The App ID of the DLC to uninstall. public static void UninstallDlc( AppId appid ) => Internal?.UninstallDLC( appid.Value ); /// /// Gets the name of the beta branch that is launched, or if the application is not running on a beta branch. /// public static string? CurrentBetaName { get { if ( Internal is null || !Internal.GetCurrentBetaName( out var strVal ) ) return null; return strVal; } } /// /// Force verify game content on next launch. /// /// If you detect the game is out-of-date (for example, by having the client detect a version mismatch with a server), /// you can call MarkContentCorrupt to force a verify, show a message to the user, and then quit. /// /// /// Whether or not to only verify missing files. public static void MarkContentCorrupt( bool missingFilesOnly ) => Internal?.MarkContentCorrupt( missingFilesOnly ); /// /// Gets a list of all installed depots for a given App ID in mount order. /// /// The App ID. public static IEnumerable InstalledDepots( AppId appid = default ) { if (Internal is null) { yield break; } if ( appid == 0 ) appid = SteamClient.AppId; var depots = new DepotId_t[32]; uint count = Internal.GetInstalledDepots( appid.Value, depots, (uint) depots.Length ); for ( int i = 0; i < count; i++ ) { yield return new DepotId { Value = depots[i].Value }; } } /// /// Gets the install folder for a specific App ID. /// This works even if the application is not installed, based on where the game would be installed with the default Steam library location. /// /// The App ID. public static string? AppInstallDir( AppId appid = default ) { if ( appid == 0 ) appid = SteamClient.AppId; if ( Internal is null || Internal.GetAppInstallDir( appid.Value, out var strVal ) == 0 ) return null; return strVal; } /// /// Gets whether or not the app is owned by the current user. The app may not actually be owned by the current user; they may have it left over from a free weekend, etc. /// /// The App ID. public static bool IsAppInstalled( AppId appid ) => Internal != null && Internal.BIsAppInstalled( appid.Value ); /// /// Gets the Steam ID of the original owner of the current app. If it's different from the current user then it is borrowed. /// public static SteamId AppOwner => Internal?.GetAppOwner().Value ?? default; /// /// Gets the associated launch parameter if the game is run via steam://run/appid/?param1=value1;param2=value2;param3=value3 etc. /// /// Parameter names starting with the character '@' are reserved for internal use and will always return an empty string. /// Parameter names starting with an underscore '_' are reserved for steam features -- they can be queried by the game, /// but it is advised that you not param names beginning with an underscore for your own features. /// /// /// The name of the parameter. /// The launch parameter value. public static string? GetLaunchParam( string param ) => Internal?.GetLaunchQueryParam( param ); /// /// Gets the download progress for optional DLC. /// /// The App ID to check the progress for. public static DownloadProgress DlcDownloadProgress( AppId appid ) { ulong punBytesDownloaded = 0; ulong punBytesTotal = 0; if ( Internal is null || !Internal.GetDlcDownloadProgress( appid.Value, ref punBytesDownloaded, ref punBytesTotal ) ) return default; return new DownloadProgress { BytesDownloaded = punBytesDownloaded, BytesTotal = punBytesTotal, Active = true }; } /// /// Gets the Build ID of this app, which can change at any time based on backend updates to the game. /// Defaults to 0 if you're not running a build downloaded from steam. /// public static int BuildId => Internal?.GetAppBuildId() ?? 0; /// /// Asynchronously retrieves metadata details about a specific file in the depot manifest. /// /// The name of the file. public static async Task GetFileDetailsAsync( string filename ) { if (Internal is null) { return null; } var r = await Internal.GetFileDetails( filename ); if ( !r.HasValue || r.Value.Result != Result.OK ) return null; return new FileDetails { SizeInBytes = r.Value.FileSize, Flags = r.Value.Flags, Sha1 = string.Join( "", r.Value.FileSHA.Select( x => x.ToString( "x" ) ) ) }; } /// /// Get command line if game was launched via Steam URL, e.g. steam://run/appid//command line/. /// This method of passing a connect string (used when joining via rich presence, accepting an /// invite, etc) is preferable to passing the connect string on the operating system command /// line, which is a security risk. In order for rich presence joins to go through this /// path and not be placed on the OS command line, you must set a value in your app's /// configuration on Steam. Ask Valve for help with this. /// public static string? CommandLine { get { string? strVal = null; Internal?.GetLaunchCommandLine( out strVal ); return strVal; } } /// /// Check if game is a timed trial with limited playtime. /// /// The amount of seconds left on the timed trial. /// The amount of seconds played on the timed trial. public static bool IsTimedTrial( out int secondsAllowed, out int secondsPlayed ) { uint a = 0; uint b = 0; secondsAllowed = 0; secondsPlayed = 0; if ( Internal == null || !Internal.BIsTimedTrial( ref a, ref b ) ) return false; secondsAllowed = (int) a; secondsPlayed = (int) b; return true; } } }