diff --git a/Facepunch.Steamworks/Client/Auth.cs b/Facepunch.Steamworks/Client/Auth.cs
index d33ff40..d807ae9 100644
--- a/Facepunch.Steamworks/Client/Auth.cs
+++ b/Facepunch.Steamworks/Client/Auth.cs
@@ -53,7 +53,6 @@ public void Dispose()
}
///
- /// Creates an auth ticket.
/// Which you can send to a server to authenticate that you are who you say you are.
///
public unsafe Ticket GetAuthSessionTicket()
diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs
new file mode 100644
index 0000000..b91bf2d
--- /dev/null
+++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs
@@ -0,0 +1,389 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using SteamNative;
+
+
+namespace Steamworks.Internal
+{
+ public class ISteamUser : BaseSteamInterface
+ {
+ public override string InterfaceName => "SteamUser020";
+
+ public override void InitInternals()
+ {
+ GetHSteamUserDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 0) );
+ BLoggedOnDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 8) );
+ GetSteamIDDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 16) );
+ InitiateGameConnectionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 24) );
+ TerminateGameConnectionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 32) );
+ TrackAppUsageEventDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 40) );
+ GetUserDataFolderDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 48) );
+ StartVoiceRecordingDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 56) );
+ StopVoiceRecordingDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 64) );
+ GetAvailableVoiceDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 72) );
+ GetVoiceDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 80) );
+ DecompressVoiceDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 88) );
+ GetVoiceOptimalSampleRateDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 96) );
+ GetAuthSessionTicketDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 104) );
+ BeginAuthSessionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 112) );
+ EndAuthSessionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 120) );
+ CancelAuthTicketDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 128) );
+ UserHasLicenseForAppDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 136) );
+ BIsBehindNATDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 144) );
+ AdvertiseGameDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 152) );
+ RequestEncryptedAppTicketDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 160) );
+ GetEncryptedAppTicketDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 168) );
+ GetGameBadgeLevelDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 176) );
+ GetPlayerSteamLevelDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 184) );
+ RequestStoreAuthURLDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 192) );
+ BIsPhoneVerifiedDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 200) );
+ BIsTwoFactorEnabledDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 208) );
+ BIsPhoneIdentifyingDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 216) );
+ BIsPhoneRequiringVerificationDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 224) );
+ GetMarketEligibilityDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 232) );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate HSteamUser GetHSteamUserDelegate( IntPtr self );
+ private GetHSteamUserDelegate GetHSteamUserDelegatePointer;
+
+ #endregion
+ public HSteamUser GetHSteamUser()
+ {
+ return GetHSteamUserDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ [return: MarshalAs( UnmanagedType.I1 )]
+ public delegate bool BLoggedOnDelegate( IntPtr self );
+ private BLoggedOnDelegate BLoggedOnDelegatePointer;
+
+ #endregion
+ public bool BLoggedOn()
+ {
+ return BLoggedOnDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void GetSteamIDDelegate( IntPtr self, ref CSteamID retVal );
+ private GetSteamIDDelegate GetSteamIDDelegatePointer;
+
+ #endregion
+ public CSteamID GetSteamID()
+ {
+ var retVal = default( CSteamID );
+ GetSteamIDDelegatePointer( Self, ref retVal );
+ return retVal;
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate int InitiateGameConnectionDelegate( IntPtr self, IntPtr pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint unIPServer, ushort usPortServer, [MarshalAs( UnmanagedType.U1 )] bool bSecure );
+ private InitiateGameConnectionDelegate InitiateGameConnectionDelegatePointer;
+
+ #endregion
+ public int InitiateGameConnection( IntPtr pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint unIPServer, ushort usPortServer, [MarshalAs( UnmanagedType.U1 )] bool bSecure )
+ {
+ return InitiateGameConnectionDelegatePointer( Self, pAuthBlob, cbMaxAuthBlob, steamIDGameServer, unIPServer, usPortServer, bSecure );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void TerminateGameConnectionDelegate( IntPtr self, uint unIPServer, ushort usPortServer );
+ private TerminateGameConnectionDelegate TerminateGameConnectionDelegatePointer;
+
+ #endregion
+ public void TerminateGameConnection( uint unIPServer, ushort usPortServer )
+ {
+ TerminateGameConnectionDelegatePointer( Self, unIPServer, usPortServer );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void TrackAppUsageEventDelegate( IntPtr self, CGameID gameID, int eAppUsageEvent, string pchExtraInfo );
+ private TrackAppUsageEventDelegate TrackAppUsageEventDelegatePointer;
+
+ #endregion
+ public void TrackAppUsageEvent( CGameID gameID, int eAppUsageEvent, string pchExtraInfo )
+ {
+ TrackAppUsageEventDelegatePointer( Self, gameID, eAppUsageEvent, pchExtraInfo );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ [return: MarshalAs( UnmanagedType.I1 )]
+ public delegate bool GetUserDataFolderDelegate( IntPtr self, StringBuilder pchBuffer, int cubBuffer );
+ private GetUserDataFolderDelegate GetUserDataFolderDelegatePointer;
+
+ #endregion
+ public bool GetUserDataFolder( StringBuilder pchBuffer, int cubBuffer )
+ {
+ return GetUserDataFolderDelegatePointer( Self, pchBuffer, cubBuffer );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void StartVoiceRecordingDelegate( IntPtr self );
+ private StartVoiceRecordingDelegate StartVoiceRecordingDelegatePointer;
+
+ #endregion
+ public void StartVoiceRecording()
+ {
+ StartVoiceRecordingDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void StopVoiceRecordingDelegate( IntPtr self );
+ private StopVoiceRecordingDelegate StopVoiceRecordingDelegatePointer;
+
+ #endregion
+ public void StopVoiceRecording()
+ {
+ StopVoiceRecordingDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate VoiceResult GetAvailableVoiceDelegate( IntPtr self, ref uint pcbCompressed, ref uint pcbUncompressed_Deprecated, uint nUncompressedVoiceDesiredSampleRate_Deprecated );
+ private GetAvailableVoiceDelegate GetAvailableVoiceDelegatePointer;
+
+ #endregion
+ public VoiceResult GetAvailableVoice( ref uint pcbCompressed, ref uint pcbUncompressed_Deprecated, uint nUncompressedVoiceDesiredSampleRate_Deprecated )
+ {
+ return GetAvailableVoiceDelegatePointer( Self, ref pcbCompressed, ref pcbUncompressed_Deprecated, nUncompressedVoiceDesiredSampleRate_Deprecated );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate VoiceResult GetVoiceDelegate( IntPtr self, [MarshalAs( UnmanagedType.U1 )] bool bWantCompressed, IntPtr pDestBuffer, uint cbDestBufferSize, ref uint nBytesWritten, [MarshalAs( UnmanagedType.U1 )] bool bWantUncompressed_Deprecated, IntPtr pUncompressedDestBuffer_Deprecated, uint cbUncompressedDestBufferSize_Deprecated, ref uint nUncompressBytesWritten_Deprecated, uint nUncompressedVoiceDesiredSampleRate_Deprecated );
+ private GetVoiceDelegate GetVoiceDelegatePointer;
+
+ #endregion
+ public VoiceResult GetVoice( [MarshalAs( UnmanagedType.U1 )] bool bWantCompressed, IntPtr pDestBuffer, uint cbDestBufferSize, ref uint nBytesWritten, [MarshalAs( UnmanagedType.U1 )] bool bWantUncompressed_Deprecated, IntPtr pUncompressedDestBuffer_Deprecated, uint cbUncompressedDestBufferSize_Deprecated, ref uint nUncompressBytesWritten_Deprecated, uint nUncompressedVoiceDesiredSampleRate_Deprecated )
+ {
+ return GetVoiceDelegatePointer( Self, bWantCompressed, pDestBuffer, cbDestBufferSize, ref nBytesWritten, bWantUncompressed_Deprecated, pUncompressedDestBuffer_Deprecated, cbUncompressedDestBufferSize_Deprecated, ref nUncompressBytesWritten_Deprecated, nUncompressedVoiceDesiredSampleRate_Deprecated );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate VoiceResult DecompressVoiceDelegate( IntPtr self, IntPtr pCompressed, uint cbCompressed, IntPtr pDestBuffer, uint cbDestBufferSize, ref uint nBytesWritten, uint nDesiredSampleRate );
+ private DecompressVoiceDelegate DecompressVoiceDelegatePointer;
+
+ #endregion
+ public VoiceResult DecompressVoice( IntPtr pCompressed, uint cbCompressed, IntPtr pDestBuffer, uint cbDestBufferSize, ref uint nBytesWritten, uint nDesiredSampleRate )
+ {
+ return DecompressVoiceDelegatePointer( Self, pCompressed, cbCompressed, pDestBuffer, cbDestBufferSize, ref nBytesWritten, nDesiredSampleRate );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate uint GetVoiceOptimalSampleRateDelegate( IntPtr self );
+ private GetVoiceOptimalSampleRateDelegate GetVoiceOptimalSampleRateDelegatePointer;
+
+ #endregion
+ public uint GetVoiceOptimalSampleRate()
+ {
+ return GetVoiceOptimalSampleRateDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate HAuthTicket GetAuthSessionTicketDelegate( IntPtr self, IntPtr pTicket, int cbMaxTicket, ref uint pcbTicket );
+ private GetAuthSessionTicketDelegate GetAuthSessionTicketDelegatePointer;
+
+ #endregion
+ public HAuthTicket GetAuthSessionTicket( IntPtr pTicket, int cbMaxTicket, ref uint pcbTicket )
+ {
+ return GetAuthSessionTicketDelegatePointer( Self, pTicket, cbMaxTicket, ref pcbTicket );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate BeginAuthSessionResult BeginAuthSessionDelegate( IntPtr self, IntPtr pAuthTicket, int cbAuthTicket, CSteamID steamID );
+ private BeginAuthSessionDelegate BeginAuthSessionDelegatePointer;
+
+ #endregion
+ public BeginAuthSessionResult BeginAuthSession( IntPtr pAuthTicket, int cbAuthTicket, CSteamID steamID )
+ {
+ return BeginAuthSessionDelegatePointer( Self, pAuthTicket, cbAuthTicket, steamID );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void EndAuthSessionDelegate( IntPtr self, CSteamID steamID );
+ private EndAuthSessionDelegate EndAuthSessionDelegatePointer;
+
+ #endregion
+ public void EndAuthSession( CSteamID steamID )
+ {
+ EndAuthSessionDelegatePointer( Self, steamID );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void CancelAuthTicketDelegate( IntPtr self, HAuthTicket hAuthTicket );
+ private CancelAuthTicketDelegate CancelAuthTicketDelegatePointer;
+
+ #endregion
+ public void CancelAuthTicket( HAuthTicket hAuthTicket )
+ {
+ CancelAuthTicketDelegatePointer( Self, hAuthTicket );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate UserHasLicenseForAppResult UserHasLicenseForAppDelegate( IntPtr self, CSteamID steamID, AppId_t appID );
+ private UserHasLicenseForAppDelegate UserHasLicenseForAppDelegatePointer;
+
+ #endregion
+ public UserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID )
+ {
+ return UserHasLicenseForAppDelegatePointer( Self, steamID, appID );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ [return: MarshalAs( UnmanagedType.I1 )]
+ public delegate bool BIsBehindNATDelegate( IntPtr self );
+ private BIsBehindNATDelegate BIsBehindNATDelegatePointer;
+
+ #endregion
+ public bool BIsBehindNAT()
+ {
+ return BIsBehindNATDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void AdvertiseGameDelegate( IntPtr self, CSteamID steamIDGameServer, uint unIPServer, ushort usPortServer );
+ private AdvertiseGameDelegate AdvertiseGameDelegatePointer;
+
+ #endregion
+ public void AdvertiseGame( CSteamID steamIDGameServer, uint unIPServer, ushort usPortServer )
+ {
+ AdvertiseGameDelegatePointer( Self, steamIDGameServer, unIPServer, usPortServer );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate SteamAPICall_t RequestEncryptedAppTicketDelegate( IntPtr self, IntPtr pDataToInclude, int cbDataToInclude );
+ private RequestEncryptedAppTicketDelegate RequestEncryptedAppTicketDelegatePointer;
+
+ #endregion
+ public async Task RequestEncryptedAppTicket( IntPtr pDataToInclude, int cbDataToInclude )
+ {
+ return await (new Result( RequestEncryptedAppTicketDelegatePointer( Self, pDataToInclude, cbDataToInclude ) )).GetResult();
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ [return: MarshalAs( UnmanagedType.I1 )]
+ public delegate bool GetEncryptedAppTicketDelegate( IntPtr self, IntPtr pTicket, int cbMaxTicket, ref uint pcbTicket );
+ private GetEncryptedAppTicketDelegate GetEncryptedAppTicketDelegatePointer;
+
+ #endregion
+ public bool GetEncryptedAppTicket( IntPtr pTicket, int cbMaxTicket, ref uint pcbTicket )
+ {
+ return GetEncryptedAppTicketDelegatePointer( Self, pTicket, cbMaxTicket, ref pcbTicket );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate int GetGameBadgeLevelDelegate( IntPtr self, int nSeries, [MarshalAs( UnmanagedType.U1 )] bool bFoil );
+ private GetGameBadgeLevelDelegate GetGameBadgeLevelDelegatePointer;
+
+ #endregion
+ public int GetGameBadgeLevel( int nSeries, [MarshalAs( UnmanagedType.U1 )] bool bFoil )
+ {
+ return GetGameBadgeLevelDelegatePointer( Self, nSeries, bFoil );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate int GetPlayerSteamLevelDelegate( IntPtr self );
+ private GetPlayerSteamLevelDelegate GetPlayerSteamLevelDelegatePointer;
+
+ #endregion
+ public int GetPlayerSteamLevel()
+ {
+ return GetPlayerSteamLevelDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate SteamAPICall_t RequestStoreAuthURLDelegate( IntPtr self, string pchRedirectURL );
+ private RequestStoreAuthURLDelegate RequestStoreAuthURLDelegatePointer;
+
+ #endregion
+ public async Task RequestStoreAuthURL( string pchRedirectURL )
+ {
+ return await (new Result( RequestStoreAuthURLDelegatePointer( Self, pchRedirectURL ) )).GetResult();
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ [return: MarshalAs( UnmanagedType.I1 )]
+ public delegate bool BIsPhoneVerifiedDelegate( IntPtr self );
+ private BIsPhoneVerifiedDelegate BIsPhoneVerifiedDelegatePointer;
+
+ #endregion
+ public bool BIsPhoneVerified()
+ {
+ return BIsPhoneVerifiedDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ [return: MarshalAs( UnmanagedType.I1 )]
+ public delegate bool BIsTwoFactorEnabledDelegate( IntPtr self );
+ private BIsTwoFactorEnabledDelegate BIsTwoFactorEnabledDelegatePointer;
+
+ #endregion
+ public bool BIsTwoFactorEnabled()
+ {
+ return BIsTwoFactorEnabledDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ [return: MarshalAs( UnmanagedType.I1 )]
+ public delegate bool BIsPhoneIdentifyingDelegate( IntPtr self );
+ private BIsPhoneIdentifyingDelegate BIsPhoneIdentifyingDelegatePointer;
+
+ #endregion
+ public bool BIsPhoneIdentifying()
+ {
+ return BIsPhoneIdentifyingDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ [return: MarshalAs( UnmanagedType.I1 )]
+ public delegate bool BIsPhoneRequiringVerificationDelegate( IntPtr self );
+ private BIsPhoneRequiringVerificationDelegate BIsPhoneRequiringVerificationDelegatePointer;
+
+ #endregion
+ public bool BIsPhoneRequiringVerification()
+ {
+ return BIsPhoneRequiringVerificationDelegatePointer( Self );
+ }
+
+ #region FunctionMeta
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate SteamAPICall_t GetMarketEligibilityDelegate( IntPtr self );
+ private GetMarketEligibilityDelegate GetMarketEligibilityDelegatePointer;
+
+ #endregion
+ public async Task GetMarketEligibility()
+ {
+ return await (new Result( GetMarketEligibilityDelegatePointer( Self ) )).GetResult();
+ }
+
+ }
+}
diff --git a/Facepunch.Steamworks/Redux/Classes/AuthTicket.cs b/Facepunch.Steamworks/Redux/Classes/AuthTicket.cs
new file mode 100644
index 0000000..3366a2f
--- /dev/null
+++ b/Facepunch.Steamworks/Redux/Classes/AuthTicket.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Steamworks
+{
+ public class AuthTicket : IDisposable
+ {
+ public byte[] Data;
+ public uint Handle;
+
+ ///
+ /// Cancels a ticket.
+ /// You should cancel your ticket when you close the game or leave a server.
+ ///
+ public void Cancel()
+ {
+ if ( Handle != 0 )
+ {
+ User.Internal.CancelAuthTicket( Handle );
+ }
+
+ Handle = 0;
+ Data = null;
+ }
+
+ public void Dispose()
+ {
+ Cancel();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Facepunch.Steamworks/Redux/User.cs b/Facepunch.Steamworks/Redux/User.cs
new file mode 100644
index 0000000..79ad38c
--- /dev/null
+++ b/Facepunch.Steamworks/Redux/User.cs
@@ -0,0 +1,265 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using SteamNative;
+
+namespace Steamworks
+{
+ ///
+ /// Undocumented Parental Settings
+ ///
+ public static class User
+ {
+ static Internal.ISteamUser _internal;
+ internal static Internal.ISteamUser Internal
+ {
+ get
+ {
+ if ( _internal == null )
+ {
+ _internal = new Internal.ISteamUser();
+
+ richPresence = new Dictionary();
+
+ SampleRate = OptimalSampleRate;
+ }
+
+ return _internal;
+ }
+ }
+
+ static Dictionary richPresence;
+
+ internal static void InstallEvents()
+ {
+ // new Event( x => OnBroadcastStarted?.Invoke() );
+ // new Event( x => OnBroadcastStopped?.Invoke( x.Result ) );
+ }
+
+ // public static event Action OnBroadcastStarted;
+
+ ///
+ /// Checks if the current user's Steam client is connected to the Steam servers.
+ /// If it's not then no real-time services provided by the Steamworks API will be enabled. The Steam
+ /// client will automatically be trying to recreate the connection as often as possible. When the
+ /// connection is restored a SteamServersConnected_t callback will be posted.
+ /// You usually don't need to check for this yourself. All of the API calls that rely on this will
+ /// check internally. Forcefully disabling stuff when the player loses access is usually not a
+ /// very good experience for the player and you could be preventing them from accessing APIs that do not
+ /// need a live connection to Steam.
+ ///
+ public static bool IsLoggedOn => Internal.BLoggedOn();
+
+ ///
+ /// Gets the Steam ID of the account currently logged into the Steam client. This is
+ /// commonly called the 'current user', or 'local user'.
+ /// A Steam ID is a unique identifier for a Steam accounts, Steam groups, Lobbies and Chat
+ /// rooms, and used to differentiate users in all parts of the Steamworks API.
+ ///
+ public static CSteamID SteamID => Internal.GetSteamID();
+
+
+ ///
+ /// Starts voice recording.
+ /// Once started, use GetAvailableVoice and GetVoice to get the data, and then call StopVoiceRecording
+ /// when the user has released their push-to-talk hotkey or the game session has completed.
+ ///
+ public static void StartVoiceRecording() => Internal.StartVoiceRecording();
+
+ ///
+ /// Stops voice recording.
+ /// Because people often release push-to-talk keys early, the system will keep recording for a little bit
+ /// after this function is called.As such, GetVoice should continue to be called until it returns k_EVoiceResultNotRecording,
+ /// only then will voice recording be stopped.
+ ///
+ public static void StopVoiceRecording() => Internal.StopVoiceRecording();
+
+
+ ///
+ /// Returns true if we have voice data waiting to be read
+ ///
+ public static bool HasVoiceData
+ {
+ get
+ {
+ uint szCompressed = 0, deprecated = 0;
+
+ if ( Internal.GetAvailableVoice( ref szCompressed, ref deprecated, 0 ) != VoiceResult.OK )
+ return false;
+
+ return szCompressed > 0;
+ }
+ }
+
+ static byte[] readBuffer = new byte[1024*128];
+
+ ///
+ /// Reads the voice data and returns the number of bytes written.
+ /// The compressed data can be transmitted by your application and decoded back into raw audio data using
+ /// DecompressVoice on the other side. The compressed data provided is in an arbitrary format and is not meant to be played directly.
+ /// This should be called once per frame, and at worst no more than four times a second to keep the microphone input delay as low as
+ /// possible. Calling this any less may result in gaps in the returned stream.
+ ///
+ public static unsafe int ReadVoiceData( System.IO.Stream stream )
+ {
+ if ( !HasVoiceData )
+ return 0;
+
+ uint szWritten = 0;
+ uint deprecated = 0;
+
+ fixed ( byte* b = readBuffer )
+ {
+ if ( Internal.GetVoice( true, (IntPtr)b, (uint)readBuffer.Length, ref szWritten, false, IntPtr.Zero, 0, ref deprecated, 0 ) != VoiceResult.OK )
+ return 0;
+ }
+
+ if ( szWritten == 0 )
+ return 0;
+
+ stream.Write( readBuffer, 0, (int) szWritten );
+
+ return (int) szWritten;
+ }
+
+ static uint sampleRate = 48000;
+
+ public static uint SampleRate
+ {
+ get => sampleRate;
+
+ set
+ {
+ if ( SampleRate < 11025 ) throw new System.Exception( "Sample Rate must be between 11025 and 48000" );
+ if ( SampleRate > 48000 ) throw new System.Exception( "Sample Rate must be between 11025 and 48000" );
+
+ sampleRate = value;
+ }
+ }
+
+ public static uint OptimalSampleRate => Internal.GetVoiceOptimalSampleRate();
+
+
+ ///
+ /// Decodes the compressed voice data returned by GetVoice.
+ /// The output data is raw single-channel 16-bit PCM audio.The decoder supports any sample rate from 11025 to 48000.
+ ///
+ public static unsafe int DecompressVoice( System.IO.Stream input, int length, System.IO.Stream output )
+ {
+ var from = Helpers.TakeBuffer( length );
+ var to = Helpers.TakeBuffer( 1024 * 64 );
+
+ //
+ // Copy from input stream to a pinnable buffer
+ //
+ using ( var s = new System.IO.MemoryStream( from ) )
+ {
+ input.CopyTo( s );
+ }
+
+ uint szWritten = 0;
+
+ fixed ( byte* frm = from )
+ fixed ( byte* dst = to )
+ {
+ if ( Internal.DecompressVoice( (IntPtr) frm, (uint) length, (IntPtr)dst, (uint)to.Length, ref szWritten, SampleRate ) != VoiceResult.OK )
+ return 0;
+ }
+
+ if ( szWritten == 0 )
+ return 0;
+
+ //
+ // Copy to output buffer
+ //
+ output.Write( to, 0, (int)szWritten );
+ return (int)szWritten;
+ }
+
+ ///
+ /// Retrieve a authentication ticket to be sent to the entity who wishes to authenticate you.
+ ///
+ public static unsafe AuthTicket GetAuthSessionTicket()
+ {
+ var data = Helpers.TakeBuffer( 1024 );
+
+ fixed ( byte* b = data )
+ {
+ uint ticketLength = 0;
+ uint ticket = Internal.GetAuthSessionTicket( (IntPtr)b, data.Length, ref ticketLength );
+
+ if ( ticket == 0 )
+ return null;
+
+ return new AuthTicket()
+ {
+ Data = data.Take( (int)ticketLength ).ToArray(),
+ Handle = ticket
+ };
+ }
+ }
+
+ public static unsafe BeginAuthSessionResult BeginAuthSession( byte[] ticketData, CSteamID steamid )
+ {
+ fixed ( byte* ptr = ticketData )
+ {
+ return Internal.BeginAuthSession( (IntPtr) ptr, ticketData.Length, steamid );
+ }
+ }
+
+ public static void EndAuthSession( CSteamID steamid ) => Internal.EndAuthSession( steamid );
+
+
+ // UserHasLicenseForApp - SERVER VERSION ( DLC CHECKING )
+
+ ///
+ /// Checks if the current users looks like they are behind a NAT device.
+ /// This is only valid if the user is connected to the Steam servers and may not catch all forms of NAT.
+ ///
+ public static bool IsBehindNAT => Internal.BIsBehindNAT();
+
+ ///
+ /// Gets the Steam level of the user, as shown on their Steam community profile.
+ ///
+ public static int SteamLevel => Internal.GetPlayerSteamLevel();
+
+ ///
+ /// Requests a URL which authenticates an in-game browser for store check-out, and then redirects to the specified URL.
+ /// As long as the in-game browser accepts and handles session cookies, Steam microtransaction checkout pages will automatically recognize the user instead of presenting a login page.
+ /// NOTE: The URL has a very short lifetime to prevent history-snooping attacks, so you should only call this API when you are about to launch the browser, or else immediately navigate to the result URL using a hidden browser window.
+ /// NOTE: The resulting authorization cookie has an expiration time of one day, so it would be a good idea to request and visit a new auth URL every 12 hours.
+ ///
+ public static async Task GetStoreAuthUrlAsync( string url )
+ {
+ var response = await Internal.RequestStoreAuthURL( url );
+ if ( !response.HasValue )
+ return null;
+
+ return response.Value.URL;
+ }
+
+ ///
+ /// Checks whether the current user has verified their phone number.
+ ///
+ public static bool IsPhoneVerified => Internal.BIsPhoneVerified();
+
+ ///
+ /// Checks whether the current user has Steam Guard two factor authentication enabled on their account.
+ ///
+ public static bool IsTwoFactorEnabled => Internal.BIsTwoFactorEnabled();
+
+ ///
+ /// Checks whether the user's phone number is used to uniquely identify them.
+ ///
+ public static bool IsPhoneIdentifying => Internal.BIsPhoneIdentifying();
+
+ ///
+ /// Checks whether the current user's phone number is awaiting (re)verification.
+ ///
+ public static bool IsPhoneRequiringVerification => Internal.BIsPhoneRequiringVerification();
+
+ }
+}
\ No newline at end of file
diff --git a/Facepunch.Steamworks/Server/Auth.cs b/Facepunch.Steamworks/Server/Auth.cs
index f94cddc..306a8ed 100644
--- a/Facepunch.Steamworks/Server/Auth.cs
+++ b/Facepunch.Steamworks/Server/Auth.cs
@@ -68,5 +68,6 @@ public void EndSession( ulong steamid )
server.native.gameServer.EndAuthSession( steamid );
}
- }
+
+ }
}
diff --git a/Facepunch.Steamworks/SteamNative/SteamNative.Helpers.cs b/Facepunch.Steamworks/SteamNative/SteamNative.Helpers.cs
index 6f2db07..a1b9344 100644
--- a/Facepunch.Steamworks/SteamNative/SteamNative.Helpers.cs
+++ b/Facepunch.Steamworks/SteamNative/SteamNative.Helpers.cs
@@ -36,5 +36,37 @@ public static StringBuilder TakeStringBuilder()
return StringBuilderPool[StringBuilderPoolIndex];
}
- }
+
+
+ private static byte[][] BufferPool;
+ private static int BufferPoolIndex;
+
+ ///
+ /// Returns a StringBuilder. This will get returned and reused later on.
+ ///
+ public static byte[] TakeBuffer( int minSize )
+ {
+ if ( BufferPool == null )
+ {
+ //
+ // The pool has 8 items.
+ //
+ BufferPool = new byte[8][];
+
+ for ( int i = 0; i < BufferPool.Length; i++ )
+ BufferPool[i] = new byte[ 1024 * 128 ];
+ }
+
+ BufferPoolIndex++;
+ if ( BufferPoolIndex >= BufferPool.Length )
+ BufferPoolIndex = 0;
+
+ if ( BufferPool[BufferPoolIndex].Length < minSize )
+ {
+ BufferPool[BufferPoolIndex] = new byte[minSize + 1024];
+ }
+
+ return BufferPool[BufferPoolIndex];
+ }
+ }
}
diff --git a/Generator/CodeWriter/CodeWriter.cs b/Generator/CodeWriter/CodeWriter.cs
index 3e20a2a..d5b90dd 100644
--- a/Generator/CodeWriter/CodeWriter.cs
+++ b/Generator/CodeWriter/CodeWriter.cs
@@ -97,6 +97,7 @@ public void ToFolder( string folder )
GenerateVTableClass( "ISteamParentalSettings", $"{folder}../Generated/Interfaces/ISteamParentalSettings.cs" );
GenerateVTableClass( "ISteamMusic", $"{folder}../Generated/Interfaces/ISteamMusic.cs" );
GenerateVTableClass( "ISteamVideo", $"{folder}../Generated/Interfaces/ISteamVideo.cs" );
+ GenerateVTableClass( "ISteamUser", $"{folder}../Generated/Interfaces/ISteamUser.cs" );
}
}
diff --git a/Generator/CodeWriter/Types/BaseType.cs b/Generator/CodeWriter/Types/BaseType.cs
index b979ad9..cba43a4 100644
--- a/Generator/CodeWriter/Types/BaseType.cs
+++ b/Generator/CodeWriter/Types/BaseType.cs
@@ -24,6 +24,7 @@ public static BaseType Parse( string type, string varname = null )
var basicType = type.Trim( ' ', '*' );
if ( basicType == "void" ) return new PointerType { NativeType = type, VarName = varname };
+ if ( basicType == "const void" ) return new PointerType { NativeType = type, VarName = varname };
if ( basicType == "int32" || basicType == "int" ) return new IntType { NativeType = type, VarName = varname };
if ( basicType == "uint32" ) return new UIntType { NativeType = type, VarName = varname };
if ( basicType == "uint8" ) return new UInt8Type { NativeType = type, VarName = varname };