Struct packing, first pass

This commit is contained in:
Garry Newman 2016-10-10 15:39:18 +01:00
parent f0e09d4331
commit dfbbaaae47
11 changed files with 132 additions and 71 deletions

View File

@ -72,10 +72,15 @@ public enum MessageType : int
/// <summary> /// <summary>
/// Global callback type /// Global callback type
/// </summary> /// </summary>
internal void AddCallback<T, TSmall>( Action<T> Callback, int id )
{
var callback = new Callback<T, TSmall>( IsGameServer, id, Callback );
Disposables.Add( callback );
}
internal void AddCallback<T>( Action<T> Callback, int id ) internal void AddCallback<T>( Action<T> Callback, int id )
{ {
var callback = new Callback<T>( IsGameServer, id, Callback ); AddCallback<T, T>( Callback, id );
Disposables.Add( callback );
} }
public Action OnUpdate; public Action OnUpdate;

View File

@ -6,7 +6,7 @@
namespace Facepunch.Steamworks.Callbacks.Networking namespace Facepunch.Steamworks.Callbacks.Networking
{ {
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential, Pack = 1 )]
internal class P2PSessionRequest internal class P2PSessionRequest
{ {
public ulong SteamID; public ulong SteamID;
@ -14,7 +14,7 @@ internal class P2PSessionRequest
public const int CallbackId = Index.Networking + 2; public const int CallbackId = Index.Networking + 2;
}; };
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential, Pack = 1 )]
internal class P2PSessionConnectFail internal class P2PSessionConnectFail
{ {
public ulong SteamID; public ulong SteamID;

View File

@ -6,9 +6,15 @@
namespace Facepunch.Steamworks.Callbacks.User namespace Facepunch.Steamworks.Callbacks.User
{ {
[StructLayout( LayoutKind.Explicit )] [StructLayout( LayoutKind.Sequential, Pack = 1 )]
internal struct ValidateAuthTicketResponse internal struct ValidateAuthTicketResponse
{ {
public ulong SteamID;
public int AuthResponse;
public ulong OwnerSteamID;
public const int CallbackId = Index.User + 43;
public enum Response : int public enum Response : int
{ {
Okay = 0, // Steam has verified the user is online, the ticket is valid and ticket has not been reused. 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. 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 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;
}; };

View File

@ -4,70 +4,104 @@
namespace Facepunch.Steamworks.Callbacks.Workshop namespace Facepunch.Steamworks.Callbacks.Workshop
{ {
[StructLayout( LayoutKind.Explicit )] [StructLayout( LayoutKind.Sequential, Pack = 8 )]
internal class ItemInstalled internal struct ItemInstalled
{ {
[FieldOffset(0)]
public uint AppId; public uint AppId;
[FieldOffset(4)]
public ulong FileId; public ulong FileId;
public const int CallbackId = Index.UGC + 5; public const int CallbackId = Index.UGC + 5;
[StructLayout( LayoutKind.Sequential, Pack = 4 )]
internal struct Small
{
public uint AppId;
public ulong FileId;
};
}; };
[StructLayout( LayoutKind.Explicit )] [StructLayout( LayoutKind.Sequential, Pack = 8 )]
internal class DownloadResult internal struct DownloadResult
{ {
[FieldOffset(0)]
public uint AppId; public uint AppId;
[FieldOffset(4)]
public ulong FileId; public ulong FileId;
[FieldOffset(12)]
public Result Result; public Result Result;
public const int CallbackId = Index.UGC + 6; 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<QueryCompleted.Data>
internal class QueryCompleted : CallResult<QueryCompleted.Data, QueryCompleted.Data.Small>
{ {
public override int CallbackId { get { return Index.UGC + 1; } } public override int CallbackId { get { return Index.UGC + 1; } }
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential, Pack = 8 )]
internal struct Data internal struct Data
{ {
internal ulong Handle; internal ulong Handle;
internal int Result; internal int Result;
internal uint m_unNumResultsReturned; internal uint NumResultsReturned;
internal uint m_unTotalMatchingResults; internal uint TotalMatchingResults;
[MarshalAs(UnmanagedType.I1)] internal bool CachedData;
internal bool m_bCachedData; // indicates whether this data was retrieved from the local on-disk cache
[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<CreateItem.Data> internal class CreateItem : CallResult<CreateItem.Data, CreateItem.Data.Small>
{ {
public override int CallbackId { get { return Index.UGC + 3; } } public override int CallbackId { get { return Index.UGC + 3; } }
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential, Pack = 8 )]
internal struct Data internal struct Data
{ {
internal Result Result; internal Result Result;
internal ulong FileId; 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<SubmitItemUpdate.Data> internal class SubmitItemUpdate : CallResult<SubmitItemUpdate.Data, SubmitItemUpdate.Data.Small>
{ {
public override int CallbackId { get { return Index.UGC + 4; } } public override int CallbackId { get { return Index.UGC + 4; } }
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential, Pack = 8 )]
internal struct Data internal struct Data
{ {
internal Result Result; internal Result Result;
[MarshalAs(UnmanagedType.I1)]
internal bool NeedsLegalAgreement; internal bool NeedsLegalAgreement;
[StructLayout( LayoutKind.Sequential, Pack = 4 )]
internal struct Small
{
internal Result Result;
internal bool NeedsLegalAgreement;
};
}; };
} }
} }

View File

@ -18,6 +18,11 @@ public static class Config
/// </summary> /// </summary>
public static bool UseThisCall { get; set; } = true; public static bool UseThisCall { get; set; } = true;
/// <summary>
/// Set this to true on Linux and OSX
/// </summary>
public static bool PackSmall { get; set; } = false;
/// <summary> /// <summary>
/// The Native dll to look for. This is the steam_api.dll renamed. /// The Native dll to look for. This is the steam_api.dll renamed.

View File

@ -65,6 +65,7 @@ public void Download( bool highPriority = true )
private void OnFileDownloaded( ulong fileid, Callbacks.Result result ) private void OnFileDownloaded( ulong fileid, Callbacks.Result result )
{ {
if ( fileid != Id ) return;
workshop.OnFileDownloaded -= OnFileDownloaded; workshop.OnFileDownloaded -= OnFileDownloaded;
UpdateState(); UpdateState();
@ -75,6 +76,7 @@ private void OnFileDownloaded( ulong fileid, Callbacks.Result result )
private void OnItemInstalled( ulong fileid ) private void OnItemInstalled( ulong fileid )
{ {
if ( fileid != Id ) return;
workshop.OnItemInstalled -= OnItemInstalled; workshop.OnItemInstalled -= OnItemInstalled;
UpdateState(); UpdateState();

View File

@ -86,8 +86,8 @@ public unsafe void Run()
void OnResult( QueryCompleted.Data data ) void OnResult( QueryCompleted.Data data )
{ {
Items = new Item[data.m_unNumResultsReturned]; Items = new Item[data.NumResultsReturned];
for ( int i = 0; i < data.m_unNumResultsReturned; i++ ) for ( int i = 0; i < data.NumResultsReturned; i++ )
{ {
SteamUGCDetails_t details = new SteamUGCDetails_t(); SteamUGCDetails_t details = new SteamUGCDetails_t();
workshop.ugc.GetQueryUGCResult( data.Handle, (uint)i, ref details ); workshop.ugc.GetQueryUGCResult( data.Handle, (uint)i, ref details );
@ -95,7 +95,7 @@ void OnResult( QueryCompleted.Data data )
Items[i] = Item.From( details, workshop ); Items[i] = Item.From( details, workshop );
} }
TotalResults = (int)data.m_unTotalMatchingResults; TotalResults = (int)data.TotalMatchingResults;
Callback.Dispose(); Callback.Dispose();
Callback = null; Callback = null;

View File

@ -22,8 +22,8 @@ internal Workshop( BaseSteamworks steamworks, ISteamUGC ugc, ISteamRemoteStorage
this.steamworks = steamworks; this.steamworks = steamworks;
this.remoteStorage = remoteStorage; this.remoteStorage = remoteStorage;
steamworks.AddCallback<DownloadResult>( onDownloadResult, DownloadResult.CallbackId ); steamworks.AddCallback<DownloadResult, DownloadResult.Small>( onDownloadResult, DownloadResult.CallbackId );
steamworks.AddCallback<ItemInstalled>( onItemInstalled, ItemInstalled.CallbackId ); steamworks.AddCallback<ItemInstalled, DownloadResult.Small>( onItemInstalled, ItemInstalled.CallbackId );
} }
public void Dispose() public void Dispose()

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Valve.Steamworks; using Valve.Steamworks;
@ -19,14 +20,19 @@ public void Dispose()
internal abstract void Run( ISteamUtils utils ); internal abstract void Run( ISteamUtils utils );
} }
internal unsafe abstract class CallResult<T> : CallResult internal unsafe abstract class CallResult<T, TSmall> : CallResult where T: new()
{ {
public static FieldInfo[] SourceFields = typeof( TSmall ).GetFields();
public static FieldInfo[] DestFields = typeof( T ).GetFields();
public abstract int CallbackId { get; } public abstract int CallbackId { get; }
public Action<T> OnResult; public Action<T> OnResult;
internal override void Run( ISteamUtils utils ) 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 ]; var data = stackalloc byte[ datasize ];
bool failed = false; bool failed = false;
@ -36,10 +42,28 @@ internal override void Run( ISteamUtils utils )
return; return;
} }
if ( packSmall )
{
var dataTarget = new T();
var dataObject = (TSmall)Marshal.PtrToStructure( (IntPtr) data, typeof( TSmall ) );
for ( int i=0; i<SourceFields.Length; i++ )
{
DestFields[i].SetValue( dataTarget, SourceFields[i].GetValue( dataObject ) );
}
if ( OnResult != null )
OnResult( dataTarget );
}
else
{
var dataObject = (T)Marshal.PtrToStructure( (IntPtr) data, typeof( T ) ); var dataObject = (T)Marshal.PtrToStructure( (IntPtr) data, typeof( T ) );
if ( OnResult != null ) if ( OnResult != null )
OnResult( dataObject ); OnResult( dataObject );
} }
}
} }
} }

View File

@ -9,37 +9,28 @@ namespace Facepunch.Steamworks.Interop.VTable.This
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential )]
internal struct Callback internal struct Callback
{ {
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] [UnmanagedFunctionPointer( CallingConvention.ThisCall )] public delegate void Result( IntPtr thisptr, IntPtr pvParam );
public delegate void Result( IntPtr thisptr, IntPtr pvParam ); [UnmanagedFunctionPointer( CallingConvention.ThisCall )] public delegate int GetSize( IntPtr thisptr );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] [MarshalAs(UnmanagedType.FunctionPtr)] public Result RunCallResult;
public delegate int GetSize( IntPtr thisptr ); [MarshalAs(UnmanagedType.FunctionPtr)] public Result RunCallback;
[MarshalAs(UnmanagedType.FunctionPtr)] public GetSize GetCallbackSizeBytes;
[MarshalAs(UnmanagedType.FunctionPtr)] internal static IntPtr Get( Action<IntPtr, IntPtr> onRunCallback, int size, Interop.Callback cb )
public Result m_RunCallback;
[MarshalAs(UnmanagedType.FunctionPtr)]
public Result m_RunCallResult;
[MarshalAs(UnmanagedType.FunctionPtr)]
public GetSize m_GetCallbackSizeBytes;
internal static IntPtr Get( Action<IntPtr> onRunCallback, Func<int> getSize, Interop.Callback cb )
{ {
var size = Marshal.SizeOf( typeof( Callback ) ); var ptr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback ) ) );
var ptr = Marshal.AllocHGlobal( size );
Callback.Result da = ( _, p ) => onRunCallback( p ); Callback.Result da = ( _, p ) => { onRunCallback( _, p ); Console.WriteLine( "Callback.Result: {0}", _.ToInt64() ); };
Callback.GetSize dc = ( _ ) => getSize(); Callback.GetSize dc = ( _ ) => { return size; };
cb.AddHandle( GCHandle.Alloc( da ) ); cb.AddHandle( GCHandle.Alloc( da ) );
cb.AddHandle( GCHandle.Alloc( dc ) ); cb.AddHandle( GCHandle.Alloc( dc ) );
var table = new Callback() var table = new Callback()
{ {
m_RunCallResult = da, RunCallback = da,
m_RunCallback = da, RunCallResult = da,
m_GetCallbackSizeBytes = dc GetCallbackSizeBytes = dc
}; };
Marshal.StructureToPtr( table, ptr, false ); Marshal.StructureToPtr( table, ptr, false );

View File

@ -48,7 +48,7 @@ internal void AddHandle( GCHandle gCHandle )
} }
} }
internal partial class Callback<T> : Callback internal partial class Callback<T, TSmall> : Callback
{ {
public int CallbackId = 0; public int CallbackId = 0;
public bool GameServer = false; public bool GameServer = false;
@ -87,10 +87,13 @@ public override void Dispose()
base.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 ( callbackPin == null ) throw new System.Exception( "Callback wasn't pinned!" );
if ( vTablePtr == IntPtr.Zero ) throw new System.Exception( "vTablePtr 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) ); T data = (T) Marshal.PtrToStructure( ptr, typeof(T) );
Function( data ); Function( data );
@ -121,7 +124,7 @@ void InitVTable()
{ {
if ( Config.UseThisCall ) if ( Config.UseThisCall )
{ {
vTablePtr = VTable.This.Callback.Get( OnRunCallback, GetSize, this ); vTablePtr = VTable.This.Callback.Get( OnRunCallback, Marshal.SizeOf( typeof( T ) ), this );
return; return;
} }