diff --git a/Facepunch.Steamworks/BaseSteamworks.cs b/Facepunch.Steamworks/BaseSteamworks.cs index 76803fb..ee06870 100644 --- a/Facepunch.Steamworks/BaseSteamworks.cs +++ b/Facepunch.Steamworks/BaseSteamworks.cs @@ -72,10 +72,15 @@ public enum MessageType : int /// /// Global callback type /// + internal void AddCallback( Action Callback, int id ) + { + var callback = new Callback( IsGameServer, id, Callback ); + Disposables.Add( callback ); + } + internal void AddCallback( Action Callback, int id ) { - var callback = new Callback( IsGameServer, id, Callback ); - Disposables.Add( callback ); + AddCallback( Callback, id ); } public Action OnUpdate; diff --git a/Facepunch.Steamworks/Callbacks/Networking.cs b/Facepunch.Steamworks/Callbacks/Networking.cs index 1f7add2..dd77ffd 100644 --- a/Facepunch.Steamworks/Callbacks/Networking.cs +++ b/Facepunch.Steamworks/Callbacks/Networking.cs @@ -6,7 +6,7 @@ namespace Facepunch.Steamworks.Callbacks.Networking { - [StructLayout( LayoutKind.Sequential )] + [StructLayout( LayoutKind.Sequential, Pack = 1 )] internal class P2PSessionRequest { public ulong SteamID; @@ -14,7 +14,7 @@ internal class P2PSessionRequest public const int CallbackId = Index.Networking + 2; }; - [StructLayout( LayoutKind.Sequential )] + [StructLayout( LayoutKind.Sequential, Pack = 1 )] internal class P2PSessionConnectFail { public ulong SteamID; diff --git a/Facepunch.Steamworks/Callbacks/User.cs b/Facepunch.Steamworks/Callbacks/User.cs index b988c2d..2838f99 100644 --- a/Facepunch.Steamworks/Callbacks/User.cs +++ b/Facepunch.Steamworks/Callbacks/User.cs @@ -6,9 +6,15 @@ namespace Facepunch.Steamworks.Callbacks.User { - [StructLayout( LayoutKind.Explicit )] + [StructLayout( LayoutKind.Sequential, Pack = 1 )] internal struct ValidateAuthTicketResponse { + public ulong SteamID; + public int AuthResponse; + public ulong OwnerSteamID; + + public const int CallbackId = Index.User + 43; + public enum Response : int { Okay = 0, // Steam has verified the user is online, the ticket is valid and ticket has not been reused. @@ -22,15 +28,6 @@ public enum Response : int AuthTicketInvalid = 8, // This ticket is not from a user instance currently connected to steam. PublisherIssuedBan = 9, // The user is banned for this game. The ban came via the web api and not VAC }; - - [FieldOffset(0)] - public ulong SteamID; - [FieldOffset(8)] - public Response AuthResponse; - [FieldOffset(12)] - public ulong OwnerSteamID; - - public const int CallbackId = Index.User + 43; }; diff --git a/Facepunch.Steamworks/Callbacks/Workshop.cs b/Facepunch.Steamworks/Callbacks/Workshop.cs index c6a8a44..9d09270 100644 --- a/Facepunch.Steamworks/Callbacks/Workshop.cs +++ b/Facepunch.Steamworks/Callbacks/Workshop.cs @@ -4,70 +4,104 @@ namespace Facepunch.Steamworks.Callbacks.Workshop { - [StructLayout( LayoutKind.Explicit )] - internal class ItemInstalled + [StructLayout( LayoutKind.Sequential, Pack = 8 )] + internal struct ItemInstalled { - [FieldOffset(0)] public uint AppId; - [FieldOffset(4)] public ulong FileId; public const int CallbackId = Index.UGC + 5; + + [StructLayout( LayoutKind.Sequential, Pack = 4 )] + internal struct Small + { + public uint AppId; + public ulong FileId; + }; }; - [StructLayout( LayoutKind.Explicit )] - internal class DownloadResult + [StructLayout( LayoutKind.Sequential, Pack = 8 )] + internal struct DownloadResult { - [FieldOffset(0)] public uint AppId; - [FieldOffset(4)] public ulong FileId; - [FieldOffset(12)] public Result Result; public const int CallbackId = Index.UGC + 6; + + [StructLayout( LayoutKind.Sequential, Pack = 4 )] + internal struct Small + { + public uint AppId; + public ulong FileId; + public Result Result; + }; }; - internal class QueryCompleted : CallResult + + internal class QueryCompleted : CallResult { public override int CallbackId { get { return Index.UGC + 1; } } - [StructLayout( LayoutKind.Sequential )] + [StructLayout( LayoutKind.Sequential, Pack = 8 )] internal struct Data { internal ulong Handle; internal int Result; - internal uint m_unNumResultsReturned; - internal uint m_unTotalMatchingResults; - [MarshalAs(UnmanagedType.I1)] - internal bool m_bCachedData; // indicates whether this data was retrieved from the local on-disk cache + internal uint NumResultsReturned; + internal uint TotalMatchingResults; + internal bool CachedData; + + [StructLayout( LayoutKind.Sequential, Pack = 4 )] + internal struct Small + { + internal ulong Handle; + internal int Result; + internal uint NumResultsReturned; + internal uint TotalMatchingResults; + internal bool CachedData; + }; }; } - internal class CreateItem : CallResult + internal class CreateItem : CallResult { public override int CallbackId { get { return Index.UGC + 3; } } - [StructLayout( LayoutKind.Sequential )] + [StructLayout( LayoutKind.Sequential, Pack = 8 )] internal struct Data { internal Result Result; internal ulong FileId; - [MarshalAs(UnmanagedType.I1)] - internal bool NeedsLegalAgreement; + internal bool NeedsLegalAgreement; + + [StructLayout( LayoutKind.Sequential, Pack = 4 )] + internal struct Small + { + internal Result Result; + internal ulong FileId; + + internal bool NeedsLegalAgreement; + }; }; } - internal class SubmitItemUpdate : CallResult + internal class SubmitItemUpdate : CallResult { public override int CallbackId { get { return Index.UGC + 4; } } - [StructLayout( LayoutKind.Sequential )] + [StructLayout( LayoutKind.Sequential, Pack = 8 )] internal struct Data { internal Result Result; - [MarshalAs(UnmanagedType.I1)] internal bool NeedsLegalAgreement; + + [StructLayout( LayoutKind.Sequential, Pack = 4 )] + internal struct Small + { + internal Result Result; + internal bool NeedsLegalAgreement; + }; }; } } diff --git a/Facepunch.Steamworks/Config.cs b/Facepunch.Steamworks/Config.cs index 9f94f34..27d8392 100644 --- a/Facepunch.Steamworks/Config.cs +++ b/Facepunch.Steamworks/Config.cs @@ -18,6 +18,11 @@ public static class Config /// public static bool UseThisCall { get; set; } = true; + /// + /// Set this to true on Linux and OSX + /// + public static bool PackSmall { get; set; } = false; + /// /// The Native dll to look for. This is the steam_api.dll renamed. diff --git a/Facepunch.Steamworks/Interfaces/Workshop.Item.cs b/Facepunch.Steamworks/Interfaces/Workshop.Item.cs index 96eaaf7..58e01da 100644 --- a/Facepunch.Steamworks/Interfaces/Workshop.Item.cs +++ b/Facepunch.Steamworks/Interfaces/Workshop.Item.cs @@ -65,6 +65,7 @@ public void Download( bool highPriority = true ) private void OnFileDownloaded( ulong fileid, Callbacks.Result result ) { + if ( fileid != Id ) return; workshop.OnFileDownloaded -= OnFileDownloaded; UpdateState(); @@ -75,6 +76,7 @@ private void OnFileDownloaded( ulong fileid, Callbacks.Result result ) private void OnItemInstalled( ulong fileid ) { + if ( fileid != Id ) return; workshop.OnItemInstalled -= OnItemInstalled; UpdateState(); diff --git a/Facepunch.Steamworks/Interfaces/Workshop.Query.cs b/Facepunch.Steamworks/Interfaces/Workshop.Query.cs index dd453a2..835b8c3 100644 --- a/Facepunch.Steamworks/Interfaces/Workshop.Query.cs +++ b/Facepunch.Steamworks/Interfaces/Workshop.Query.cs @@ -86,8 +86,8 @@ public unsafe void Run() void OnResult( QueryCompleted.Data data ) { - Items = new Item[data.m_unNumResultsReturned]; - for ( int i = 0; i < data.m_unNumResultsReturned; i++ ) + Items = new Item[data.NumResultsReturned]; + for ( int i = 0; i < data.NumResultsReturned; i++ ) { SteamUGCDetails_t details = new SteamUGCDetails_t(); workshop.ugc.GetQueryUGCResult( data.Handle, (uint)i, ref details ); @@ -95,7 +95,7 @@ void OnResult( QueryCompleted.Data data ) Items[i] = Item.From( details, workshop ); } - TotalResults = (int)data.m_unTotalMatchingResults; + TotalResults = (int)data.TotalMatchingResults; Callback.Dispose(); Callback = null; diff --git a/Facepunch.Steamworks/Interfaces/Workshop.cs b/Facepunch.Steamworks/Interfaces/Workshop.cs index af57e54..a99dc61 100644 --- a/Facepunch.Steamworks/Interfaces/Workshop.cs +++ b/Facepunch.Steamworks/Interfaces/Workshop.cs @@ -22,8 +22,8 @@ internal Workshop( BaseSteamworks steamworks, ISteamUGC ugc, ISteamRemoteStorage this.steamworks = steamworks; this.remoteStorage = remoteStorage; - steamworks.AddCallback( onDownloadResult, DownloadResult.CallbackId ); - steamworks.AddCallback( onItemInstalled, ItemInstalled.CallbackId ); + steamworks.AddCallback( onDownloadResult, DownloadResult.CallbackId ); + steamworks.AddCallback( onItemInstalled, ItemInstalled.CallbackId ); } public void Dispose() diff --git a/Facepunch.Steamworks/Interop/CallResult.cs b/Facepunch.Steamworks/Interop/CallResult.cs index 73bc612..bf40610 100644 --- a/Facepunch.Steamworks/Interop/CallResult.cs +++ b/Facepunch.Steamworks/Interop/CallResult.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Runtime.InteropServices; using System.Text; using Valve.Steamworks; @@ -19,14 +20,19 @@ public void Dispose() internal abstract void Run( ISteamUtils utils ); } - internal unsafe abstract class CallResult : CallResult + internal unsafe abstract class CallResult : CallResult where T: new() { + public static FieldInfo[] SourceFields = typeof( TSmall ).GetFields(); + public static FieldInfo[] DestFields = typeof( T ).GetFields(); + public abstract int CallbackId { get; } - public Action OnResult; + public Action OnResult; internal override void Run( ISteamUtils utils ) { - var datasize = Marshal.SizeOf( typeof( T ) ); + var packSmall = Config.PackSmall; + + var datasize = packSmall ? Marshal.SizeOf( typeof( TSmall ) ) : Marshal.SizeOf( typeof( T ) ); var data = stackalloc byte[ datasize ]; bool failed = false; @@ -36,10 +42,28 @@ internal override void Run( ISteamUtils utils ) return; } - var dataObject = (T)Marshal.PtrToStructure( (IntPtr) data, typeof( T ) ); + if ( packSmall ) + { + var dataTarget = new T(); + var dataObject = (TSmall)Marshal.PtrToStructure( (IntPtr) data, typeof( TSmall ) ); + + for ( int i=0; i onRunCallback, Func getSize, Interop.Callback cb ) + internal static IntPtr Get( Action onRunCallback, int size, Interop.Callback cb ) { - var size = Marshal.SizeOf( typeof( Callback ) ); - var ptr = Marshal.AllocHGlobal( size ); + var ptr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback ) ) ); - Callback.Result da = ( _, p ) => onRunCallback( p ); - Callback.GetSize dc = ( _ ) => getSize(); + Callback.Result da = ( _, p ) => { onRunCallback( _, p ); Console.WriteLine( "Callback.Result: {0}", _.ToInt64() ); }; + Callback.GetSize dc = ( _ ) => { return size; }; cb.AddHandle( GCHandle.Alloc( da ) ); cb.AddHandle( GCHandle.Alloc( dc ) ); var table = new Callback() { - m_RunCallResult = da, - m_RunCallback = da, - m_GetCallbackSizeBytes = dc + RunCallback = da, + RunCallResult = da, + GetCallbackSizeBytes = dc }; Marshal.StructureToPtr( table, ptr, false ); diff --git a/Facepunch.Steamworks/Interop/Callback.cs b/Facepunch.Steamworks/Interop/Callback.cs index c6667f8..e20ecb5 100644 --- a/Facepunch.Steamworks/Interop/Callback.cs +++ b/Facepunch.Steamworks/Interop/Callback.cs @@ -48,7 +48,7 @@ internal void AddHandle( GCHandle gCHandle ) } } - internal partial class Callback : Callback + internal partial class Callback : Callback { public int CallbackId = 0; public bool GameServer = false; @@ -87,10 +87,13 @@ public override void Dispose() base.Dispose(); } - private void OnRunCallback( IntPtr ptr ) + private void OnRunCallback( IntPtr thisObject, IntPtr ptr ) { if ( callbackPin == null ) throw new System.Exception( "Callback wasn't pinned!" ); if ( vTablePtr == IntPtr.Zero ) throw new System.Exception( "vTablePtr wasn't pinned!" ); + if ( thisObject != IntPtr.Zero && thisObject != callbackPin.AddrOfPinnedObject() ) throw new System.Exception( "This wasn't valid!" ); + + if ( Config.PackSmall && typeof(T) != typeof( TSmall ) ) throw new System.Exception( "Callback should use PackSmall" ); T data = (T) Marshal.PtrToStructure( ptr, typeof(T) ); Function( data ); @@ -121,7 +124,7 @@ void InitVTable() { if ( Config.UseThisCall ) { - vTablePtr = VTable.This.Callback.Get( OnRunCallback, GetSize, this ); + vTablePtr = VTable.This.Callback.Get( OnRunCallback, Marshal.SizeOf( typeof( T ) ), this ); return; }