diff --git a/Facepunch.Steamworks/SteamApps.cs b/Facepunch.Steamworks/SteamApps.cs index e7d9a4c..dd232ac 100644 --- a/Facepunch.Steamworks/SteamApps.cs +++ b/Facepunch.Steamworks/SteamApps.cs @@ -25,6 +25,11 @@ internal static ISteamApps Internal } } + internal static void Shutdown() + { + _internal = null; + } + internal static void InstallEvents() { new Event( x => OnDlcInstalled?.Invoke( x.AppID ) ); diff --git a/Facepunch.Steamworks/SteamClient.cs b/Facepunch.Steamworks/SteamClient.cs index 9eee454..f49849d 100644 --- a/Facepunch.Steamworks/SteamClient.cs +++ b/Facepunch.Steamworks/SteamClient.cs @@ -63,7 +63,18 @@ internal static async void RunCallbacksAsync() public static void Shutdown() { - // TODO. + initialized = false; + + SteamApps.Shutdown(); + SteamUtils.Shutdown(); + SteamParental.Shutdown(); + SteamMusic.Shutdown(); + SteamVideo.Shutdown(); + SteamUser.Shutdown(); + SteamFriends.Shutdown(); + SteamScreenshots.Shutdown(); + SteamUserStats.Shutdown(); + SteamInventory.Shutdown(); } internal static void RegisterCallback( IntPtr intPtr, int callbackId ) diff --git a/Facepunch.Steamworks/SteamFriends.cs b/Facepunch.Steamworks/SteamFriends.cs index b2b7556..42fc926 100644 --- a/Facepunch.Steamworks/SteamFriends.cs +++ b/Facepunch.Steamworks/SteamFriends.cs @@ -27,6 +27,10 @@ internal static ISteamFriends Internal return _internal; } } + internal static void Shutdown() + { + _internal = null; + } static Dictionary richPresence; diff --git a/Facepunch.Steamworks/SteamInventory.cs b/Facepunch.Steamworks/SteamInventory.cs index 52931cb..a4582fc 100644 --- a/Facepunch.Steamworks/SteamInventory.cs +++ b/Facepunch.Steamworks/SteamInventory.cs @@ -25,6 +25,10 @@ internal static ISteamInventory Internal return _internal; } } + internal static void Shutdown() + { + _internal = null; + } internal static void InstallEvents() { diff --git a/Facepunch.Steamworks/SteamMusic.cs b/Facepunch.Steamworks/SteamMusic.cs index 28d69b3..11b835c 100644 --- a/Facepunch.Steamworks/SteamMusic.cs +++ b/Facepunch.Steamworks/SteamMusic.cs @@ -23,6 +23,10 @@ internal static ISteamMusic Internal return _internal; } } + internal static void Shutdown() + { + _internal = null; + } internal static void InstallEvents() { diff --git a/Facepunch.Steamworks/SteamParental.cs b/Facepunch.Steamworks/SteamParental.cs index 3dd40b6..10145bd 100644 --- a/Facepunch.Steamworks/SteamParental.cs +++ b/Facepunch.Steamworks/SteamParental.cs @@ -23,6 +23,10 @@ internal static ISteamParentalSettings Internal return _internal; } } + internal static void Shutdown() + { + _internal = null; + } internal static void InstallEvents() { diff --git a/Facepunch.Steamworks/SteamRemoteStorage.cs b/Facepunch.Steamworks/SteamRemoteStorage.cs index 23324c6..a191856 100644 --- a/Facepunch.Steamworks/SteamRemoteStorage.cs +++ b/Facepunch.Steamworks/SteamRemoteStorage.cs @@ -24,6 +24,11 @@ internal static ISteamRemoteStorage Internal } } + internal static void Shutdown() + { + _internal = null; + } + /// /// Creates a new file, writes the bytes to the file, and then closes the file. /// If the target file already exists, it is overwritten diff --git a/Facepunch.Steamworks/SteamScreenshots.cs b/Facepunch.Steamworks/SteamScreenshots.cs index cafdbb4..972baa6 100644 --- a/Facepunch.Steamworks/SteamScreenshots.cs +++ b/Facepunch.Steamworks/SteamScreenshots.cs @@ -23,6 +23,10 @@ internal static ISteamScreenshots Internal return _internal; } } + internal static void Shutdown() + { + _internal = null; + } internal static void InstallEvents() { diff --git a/Facepunch.Steamworks/SteamServer.cs b/Facepunch.Steamworks/SteamServer.cs index 22c25eb..53c00b9 100644 --- a/Facepunch.Steamworks/SteamServer.cs +++ b/Facepunch.Steamworks/SteamServer.cs @@ -31,6 +31,8 @@ internal static ISteamGameServer Internal internal static void InstallEvents() { new Event( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ), true ); + + SteamServerInventory.InstallEvents(); } /// @@ -77,6 +79,10 @@ public static void Init( AppId appid, SteamServerInit init ) public static void Shutdown() { initialized = false; + + _internal = null; + + SteamServerInventory.Shutdown(); } diff --git a/Facepunch.Steamworks/SteamServerInventory.cs b/Facepunch.Steamworks/SteamServerInventory.cs new file mode 100644 index 0000000..d4f3420 --- /dev/null +++ b/Facepunch.Steamworks/SteamServerInventory.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Steamworks.Data; + +namespace Steamworks +{ + public static class SteamServerInventory + { + static ISteamInventory _internal; + internal static ISteamInventory Internal + { + get + { + if ( _internal == null ) + _internal = new ISteamInventory(); + + return _internal; + } + } + internal static void Shutdown() + { + _internal = null; + } + + internal static void InstallEvents() + { + new Event( x => DefinitionsUpdated(), true ); + } + + public static event Action OnDefinitionsUpdated; + + internal static int defUpdateCount = 0; + + internal static void DefinitionsUpdated() + { + Definitions = GetDefinitions(); + + if ( Definitions != null ) + { + _defMap = new Dictionary(); + + foreach ( var d in Definitions ) + { + _defMap[d.Id] = d; + } + } + + defUpdateCount++; + + OnDefinitionsUpdated?.Invoke(); + } + + + /// + /// Call this if you're going to want to access definition information. You should be able to get + /// away with calling this once at the start if your game, assuming your items don't change all the time. + /// This will trigger OnDefinitionsUpdated at which point Definitions should be set. + /// + public static void LoadItemDefinitions() + { + Internal.LoadItemDefinitions(); + } + + /// + /// Will call LoadItemDefinitions and wait until Definitions is not null + /// + public static async Task WaitForDefinitions( float timeoutSeconds = 10 ) + { + LoadItemDefinitions(); + + var sw = Stopwatch.StartNew(); + + while ( Definitions == null ) + { + if ( sw.Elapsed.TotalSeconds > timeoutSeconds ) + return false; + + await Task.Delay( 10 ); + } + + return true; + } + + internal static InventoryDef FindDefinition( InventoryDefId defId ) + { + if ( _defMap.TryGetValue( defId, out var val ) ) + return val; + + return null; + } + + public static string Currency { get; internal set; } + + public static async Task GetDefinitionsWithPricesAsync() + { + var priceRequest = await Internal.RequestPrices(); + if ( !priceRequest.HasValue || priceRequest.Value.Result != Result.OK ) + return null; + + Currency = priceRequest?.Currency; + + var num = Internal.GetNumItemsWithPrices(); + + if ( num <= 0 ) + return null; + + var defs = new InventoryDefId[num]; + var currentPrices = new ulong[num]; + var baseprices = new ulong[num]; + + var gotPrices = Internal.GetItemsWithPrices( defs, currentPrices, baseprices, num ); + if ( !gotPrices ) + return null; + + return defs.Select( x => new InventoryDef( x ) ).ToArray(); + } + + public static InventoryDef[] Definitions { get; internal set; } + public static Dictionary _defMap; + + internal static InventoryDef[] GetDefinitions() + { + uint num = 0; + if ( !Internal.GetItemDefinitionIDs( null, ref num ) ) + return null; + + var defs = new InventoryDefId[num]; + + if ( !Internal.GetItemDefinitionIDs( defs, ref num ) ) + return null; + + return defs.Select( x => new InventoryDef( x ) ).ToArray(); + } + + /// + /// Deserializes a result set and verifies the signature bytes. + /// This call has a potential soft-failure mode where the Result is expired, it will + /// still succeed in this mode.The "expired" + /// result could indicate that the data may be out of date - not just due to timed + /// expiration( one hour ), but also because one of the items in the result set may + /// have been traded or consumed since the result set was generated.You could compare + /// the timestamp from GetResultTimestamp to ISteamUtils::GetServerRealTime to determine + /// how old the data is. You could simply ignore the "expired" result code and + /// continue as normal, or you could request the player with expired data to send + /// an updated result set. + /// You should call CheckResultSteamID on the result handle when it completes to verify + /// that a remote player is not pretending to have a different user's inventory. + /// + static async Task DeserializeAsync( byte[] data, int dataLength = -1 ) + { + if ( data == null ) + throw new ArgumentException( "data should nto be null" ); + + if ( dataLength == -1 ) + dataLength = data.Length; + + var sresult = DeserializeResult( data, dataLength ); + if ( !sresult.HasValue ) return null; + + return await InventoryResult.GetAsync( sresult.Value ); + } + + internal static unsafe SteamInventoryResult_t? DeserializeResult( byte[] data, int dataLength = -1 ) + { + var sresult = default( SteamInventoryResult_t ); + + fixed ( byte* ptr = data ) + { + if ( !Internal.DeserializeResult( ref sresult, (IntPtr)ptr, (uint)dataLength, false ) ) + return null; + } + + return sresult; + } + + } +} \ No newline at end of file diff --git a/Facepunch.Steamworks/SteamUgc.cs b/Facepunch.Steamworks/SteamUgc.cs index 5484eeb..7a99716 100644 --- a/Facepunch.Steamworks/SteamUgc.cs +++ b/Facepunch.Steamworks/SteamUgc.cs @@ -28,6 +28,11 @@ internal static ISteamUGC Internal } } + internal static void Shutdown() + { + _internal = null; + } + public static async Task DeleteFileAsync( PublishedFileId fileId ) { var r = await Internal.DeleteItem( fileId ); diff --git a/Facepunch.Steamworks/SteamUser.cs b/Facepunch.Steamworks/SteamUser.cs index f1c09c6..02ddd75 100644 --- a/Facepunch.Steamworks/SteamUser.cs +++ b/Facepunch.Steamworks/SteamUser.cs @@ -31,6 +31,10 @@ internal static ISteamUser Internal return _internal; } } + internal static void Shutdown() + { + _internal = null; + } static Dictionary richPresence; diff --git a/Facepunch.Steamworks/SteamUserStats.cs b/Facepunch.Steamworks/SteamUserStats.cs index 78dbae8..dfd0afa 100644 --- a/Facepunch.Steamworks/SteamUserStats.cs +++ b/Facepunch.Steamworks/SteamUserStats.cs @@ -20,6 +20,10 @@ internal static ISteamUserStats Internal return _internal; } } + internal static void Shutdown() + { + _internal = null; + } public static bool StatsRecieved { get; internal set; } diff --git a/Facepunch.Steamworks/SteamUtils.cs b/Facepunch.Steamworks/SteamUtils.cs index 30a079d..c14940d 100644 --- a/Facepunch.Steamworks/SteamUtils.cs +++ b/Facepunch.Steamworks/SteamUtils.cs @@ -24,6 +24,11 @@ internal static ISteamUtils Internal } } + internal static void Shutdown() + { + _internal = null; + } + internal static void InstallEvents() { new Event( x => OnIpCountryChanged?.Invoke() ); diff --git a/Facepunch.Steamworks/SteamVideo.cs b/Facepunch.Steamworks/SteamVideo.cs index b002921..b183a26 100644 --- a/Facepunch.Steamworks/SteamVideo.cs +++ b/Facepunch.Steamworks/SteamVideo.cs @@ -24,6 +24,11 @@ internal static ISteamVideo Internal } } + internal static void Shutdown() + { + _internal = null; + } + internal static void InstallEvents() { new Event( x => OnBroadcastStarted?.Invoke() );