Event cleanup

This commit is contained in:
Garry Newman 2019-04-30 14:27:45 +01:00
parent cbc154da66
commit 10a82adca9
18 changed files with 98 additions and 253 deletions

View File

@ -7,35 +7,43 @@ namespace Steamworks
{
internal partial class Callback
{
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public class VTable
internal static class VTable
{
[UnmanagedFunctionPointer( CallingConvention.StdCall )] public delegate void ResultD( IntPtr pvParam );
[UnmanagedFunctionPointer( CallingConvention.StdCall )] public delegate void ResultWithInfoD( IntPtr pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall );
[UnmanagedFunctionPointer( CallingConvention.StdCall )] public delegate int GetSizeD();
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate void Run( IntPtr thisptr, IntPtr pvParam );
public ResultD ResultA;
public ResultWithInfoD ResultB;
public GetSizeD GetSize;
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate void RunCall( IntPtr thisptr, IntPtr pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall );
internal static IntPtr GetVTable( ResultD onResultThis, ResultWithInfoD onResultWithInfoThis, GetSizeD onGetSizeThis, List<GCHandle> allocations )
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate int GetCallbackSizeBytes( IntPtr thisptr );
internal static IntPtr GetVTable( Run run, RunCall runInfo, GetCallbackSizeBytes size, List<GCHandle> allocations )
{
var vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback.VTable ) ) );
allocations.Add( GCHandle.Alloc( run ) );
allocations.Add( GCHandle.Alloc( runInfo ) );
allocations.Add( GCHandle.Alloc( size ) );
var vTable = new Callback.VTable
var a = Marshal.GetFunctionPointerForDelegate<Run>( run );
var b = Marshal.GetFunctionPointerForDelegate<RunCall>( runInfo );
var c = Marshal.GetFunctionPointerForDelegate<GetCallbackSizeBytes>( size );
var vt = Marshal.AllocHGlobal( IntPtr.Size * 3 );
if ( Config.Os == OsType.Windows )
{
ResultA = onResultThis,
ResultB = onResultWithInfoThis,
GetSize = onGetSizeThis,
};
Marshal.WriteIntPtr( vt, IntPtr.Size * 0, b );
Marshal.WriteIntPtr( vt, IntPtr.Size * 1, a );
Marshal.WriteIntPtr( vt, IntPtr.Size * 2, c );
}
else
{
Marshal.WriteIntPtr( vt, IntPtr.Size * 0, a );
Marshal.WriteIntPtr( vt, IntPtr.Size * 1, b );
Marshal.WriteIntPtr( vt, IntPtr.Size * 2, c );
}
allocations.Add( GCHandle.Alloc( vTable.ResultA ) );
allocations.Add( GCHandle.Alloc( vTable.ResultB ) );
allocations.Add( GCHandle.Alloc( vTable.GetSize ) );
Marshal.StructureToPtr( vTable, vTablePtr, false );
return vTablePtr;
return vt;
}
}
};

View File

@ -1,43 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using Steamworks.Data;
namespace Steamworks
{
internal partial class Callback
{
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public class VTableThis
{
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] public delegate void ResultD( IntPtr thisptr, IntPtr pvParam );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] public delegate void ResultWithInfoD( IntPtr thisptr, IntPtr pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall );
internal static IntPtr GetVTable( ResultD onResultThis, ResultWithInfoD onResultWithInfoThis, GetSizeD onGetSizeThis, List<GCHandle> allocations )
{
var vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback.VTableThis ) ) );
var vTable = new Callback.VTableThis
{
ResultA = onResultThis,
ResultB = onResultWithInfoThis,
GetSize = onGetSizeThis,
};
allocations.Add( GCHandle.Alloc( vTable.ResultA ) );
allocations.Add( GCHandle.Alloc( vTable.ResultB ) );
allocations.Add( GCHandle.Alloc( vTable.GetSize ) );
Marshal.StructureToPtr( vTable, vTablePtr, false );
return vTablePtr;
}
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] public delegate int GetSizeD( IntPtr thisptr );
public ResultD ResultA;
public ResultWithInfoD ResultB;
public GetSizeD GetSize;
}
};
}

View File

@ -1,42 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using Steamworks.Data;
namespace Steamworks
{
internal partial class Callback
{
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public class VTableWin
{
[UnmanagedFunctionPointer( CallingConvention.StdCall )] public delegate void ResultD( IntPtr pvParam );
[UnmanagedFunctionPointer( CallingConvention.StdCall )] public delegate void ResultWithInfoD( IntPtr pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall );
[UnmanagedFunctionPointer( CallingConvention.StdCall )] public delegate int GetSizeD();
public ResultWithInfoD ResultB;
public ResultD ResultA;
public GetSizeD GetSize;
internal static IntPtr GetVTable( ResultD onResultThis, ResultWithInfoD onResultWithInfoThis, GetSizeD onGetSizeThis, List<GCHandle> allocations )
{
var vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback.VTableWin ) ) );
var vTable = new Callback.VTableWin
{
ResultA = onResultThis,
ResultB = onResultWithInfoThis,
GetSize = onGetSizeThis,
};
allocations.Add( GCHandle.Alloc( vTable.ResultA ) );
allocations.Add( GCHandle.Alloc( vTable.ResultB ) );
allocations.Add( GCHandle.Alloc( vTable.GetSize ) );
Marshal.StructureToPtr( vTable, vTablePtr, false );
return vTablePtr;
}
}
};
}

View File

@ -1,42 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using Steamworks.Data;
namespace Steamworks
{
internal partial class Callback
{
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public class VTableWinThis
{
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] public delegate void ResultD( IntPtr thisptr, IntPtr pvParam );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] public delegate void ResultWithInfoD( IntPtr thisptr, IntPtr pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] public delegate int GetSizeD( IntPtr thisptr );
public ResultWithInfoD ResultB;
public ResultD ResultA;
public GetSizeD GetSize;
internal static IntPtr GetVTable( ResultD onResultThis, ResultWithInfoD onResultWithInfoThis, GetSizeD onGetSizeThis, List<GCHandle> allocations )
{
var vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback.VTableWinThis ) ) );
var vTable = new Callback.VTableWinThis
{
ResultA = onResultThis,
ResultB = onResultWithInfoThis,
GetSize = onGetSizeThis,
};
allocations.Add( GCHandle.Alloc( vTable.ResultA ) );
allocations.Add( GCHandle.Alloc( vTable.ResultB ) );
allocations.Add( GCHandle.Alloc( vTable.GetSize ) );
Marshal.StructureToPtr( vTable, vTablePtr, false );
return vTablePtr;
}
}
};
}

View File

@ -36,9 +36,10 @@ namespace Steamworks
//
// Created on registration of a callback
//
internal class Event<T> : IDisposable where T : struct, Steamworks.ISteamCallback
internal class Event : IDisposable
{
public Action<T> Action;
Steamworks.ISteamCallback template;
public Action<Steamworks.ISteamCallback> Action;
bool IsAllocated;
List<GCHandle> Allocations = new List<GCHandle>();
@ -79,86 +80,60 @@ namespace Steamworks
public virtual bool IsValid { get { return true; } }
T template;
internal static Event CreateEvent<T>( Action<T> onresult, bool gameserver = false ) where T: struct, Steamworks.ISteamCallback
{
var r = new Event();
internal Event( Action<T> onresult, bool gameserver = false )
{
Action = onresult;
r.Action = ( x ) => onresult( (T) x );
template = new T();
//
// Create the functions we need for the vtable
//
if ( Config.UseThisCall )
{
//
// Create the VTable by manually allocating the memory and copying across
//
if ( Config.Os == OsType.Windows )
{
vTablePtr = Callback.VTableWinThis.GetVTable( OnResultThis, OnResultWithInfoThis, OnGetSizeThis, Allocations );
}
else
{
vTablePtr = Callback.VTableThis.GetVTable( OnResultThis, OnResultWithInfoThis, OnGetSizeThis, Allocations );
}
}
else
{
//
// Create the VTable by manually allocating the memory and copying across
//
if ( Config.Os == OsType.Windows )
{
vTablePtr = Callback.VTableWin.GetVTable( OnResult, OnResultWithInfo, OnGetSize, Allocations );
}
else
{
vTablePtr = Callback.VTable.GetVTable( OnResult, OnResultWithInfo, OnGetSize, Allocations );
}
}
r.template = new T();
r.vTablePtr = Callback.VTable.GetVTable( r.OnResult, RunStub, SizeStub, r.Allocations );
//
// Create the callback object
//
var cb = new Callback();
cb.vTablePtr = vTablePtr;
cb.vTablePtr = r.vTablePtr;
cb.CallbackFlags = gameserver ? (byte)0x02 : (byte)0;
cb.CallbackId = template.GetCallbackId();
cb.CallbackId = r.template.GetCallbackId();
//
// Pin the callback, so it doesn't get garbage collected and we can pass the pointer to native
//
PinnedCallback = GCHandle.Alloc( cb, GCHandleType.Pinned );
r.PinnedCallback = GCHandle.Alloc( cb, GCHandleType.Pinned );
//
// Register the callback with Steam
//
SteamClient.RegisterCallback( PinnedCallback.AddrOfPinnedObject(), cb.CallbackId );
SteamClient.RegisterCallback( r.PinnedCallback.AddrOfPinnedObject(), cb.CallbackId );
IsAllocated = true;
r.IsAllocated = true;
if ( gameserver )
Events.AllServer.Add( this );
Events.AllServer.Add( r );
else
Events.AllClient.Add( this );
}
Events.AllClient.Add( r );
[MonoPInvokeCallback] internal void OnResultThis( IntPtr self, IntPtr param ) => OnResult( param );
[MonoPInvokeCallback] internal void OnResultWithInfoThis( IntPtr self, IntPtr param, bool failure, SteamAPICall_t call ) => OnResultWithInfo( param, failure, call );
[MonoPInvokeCallback] internal int OnGetSizeThis( IntPtr self ) => OnGetSize();
[MonoPInvokeCallback] internal int OnGetSize() => template.GetStructSize();
[MonoPInvokeCallback] internal void OnResult( IntPtr param ) => OnResultWithInfo( param, false, 0 );
return r;
}
[MonoPInvokeCallback]
internal void OnResultWithInfo( IntPtr param, bool failure, SteamAPICall_t call )
internal void OnResult( IntPtr self, IntPtr param )
{
if ( failure ) return;
var value = (T)template.Fill( param );
var value = template.Fill( param );
Action( value );
}
[MonoPInvokeCallback]
static void RunStub( IntPtr self, IntPtr param, bool failure, SteamAPICall_t call )
{
throw new System.Exception( "Something changed in the Steam API and now CCallbackBack is calling the CallResult function [Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall )]" );
}
[MonoPInvokeCallback]
static int SizeStub( IntPtr self )
{
throw new System.Exception( "Something changed in the Steam API and now CCallbackBack is calling the GetSize function [GetCallbackSizeBytes()]" );
}
}
}

View File

@ -38,17 +38,6 @@ namespace Steamworks
}
public static bool PackSmall => Os != OsType.Windows;
/// <summary>
/// Some platforms allow/need CallingConvention.ThisCall. If you're crashing with argument null
/// errors on certain platforms, try flipping this to true.
///
/// I owe this logic to Riley Labrecque's hard work on Steamworks.net - I don't have the knowledge
/// or patience to find this shit on my own, so massive thanks to him. And also massive thanks to him
/// for releasing his shit open source under the MIT license so we can all learn and iterate.
///
/// </summary>
public static bool UseThisCall { get; set; } = true;
}
public enum OsType

View File

@ -32,8 +32,8 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<DlcInstalled_t>( x => OnDlcInstalled?.Invoke( x.AppID ) );
new Event<NewUrlLaunchParameters_t>( x => OnNewLaunchParameters?.Invoke() );
Event.CreateEvent<DlcInstalled_t>( x => OnDlcInstalled?.Invoke( x.AppID ) );
Event.CreateEvent<NewUrlLaunchParameters_t>( x => OnNewLaunchParameters?.Invoke() );
}
/// <summary>

View File

@ -36,13 +36,13 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<FriendStateChange_t>( x => OnPersonaStateChange?.Invoke( new Friend( x.SteamID ) ) );
new Event<GameRichPresenceJoinRequested_t>( x => OnGameRichPresenceJoinRequested?.Invoke( new Friend( x.SteamIDFriend), x.Connect ) );
new Event<GameConnectedFriendChatMsg_t>( OnFriendChatMessage );
new Event<GameOverlayActivated_t>( x => OnGameOverlayActivated?.Invoke() );
new Event<GameServerChangeRequested_t>( x => OnGameServerChangeRequested?.Invoke( x.Server, x.Password ) );
new Event<GameLobbyJoinRequested_t>( x => OnGameLobbyJoinRequested?.Invoke( x.SteamIDLobby, x.SteamIDFriend ) );
new Event<FriendRichPresenceUpdate_t>( x => OnFriendRichPresenceUpdate?.Invoke( new Friend( x.SteamIDFriend ) ) );
Event.CreateEvent<FriendStateChange_t>( x => OnPersonaStateChange?.Invoke( new Friend( x.SteamID ) ) );
Event.CreateEvent<GameRichPresenceJoinRequested_t>( x => OnGameRichPresenceJoinRequested?.Invoke( new Friend( x.SteamIDFriend), x.Connect ) );
Event.CreateEvent<GameConnectedFriendChatMsg_t>( OnFriendChatMessage );
Event.CreateEvent<GameOverlayActivated_t>( x => OnGameOverlayActivated?.Invoke() );
Event.CreateEvent<GameServerChangeRequested_t>( x => OnGameServerChangeRequested?.Invoke( x.Server, x.Password ) );
Event.CreateEvent<GameLobbyJoinRequested_t>( x => OnGameLobbyJoinRequested?.Invoke( x.SteamIDLobby, x.SteamIDFriend ) );
Event.CreateEvent<FriendRichPresenceUpdate_t>( x => OnFriendRichPresenceUpdate?.Invoke( new Friend( x.SteamIDFriend ) ) );
}
/// <summary>

View File

@ -32,11 +32,11 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<SteamInventoryFullUpdate_t>( x => OnInventoryUpdated?.Invoke() );
new Event<SteamInventoryDefinitionUpdate_t>( x => DefinitionsUpdated() );
Event.CreateEvent<SteamInventoryFullUpdate_t>( x => OnInventoryUpdated?.Invoke( x.Handle ) );
Event.CreateEvent<SteamInventoryDefinitionUpdate_t>( x => DefinitionsUpdated() );
}
public static event Action OnInventoryUpdated;
public static event Action<int> OnInventoryUpdated;
public static event Action OnDefinitionsUpdated;
internal static int defUpdateCount = 0;

View File

@ -30,8 +30,8 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<PlaybackStatusHasChanged_t>( x => OnPlaybackChanged?.Invoke() );
new Event<VolumeHasChanged_t>( x => OnVolumeChanged?.Invoke( x.NewVolume ) );
Event.CreateEvent<PlaybackStatusHasChanged_t>( x => OnPlaybackChanged?.Invoke() );
Event.CreateEvent<VolumeHasChanged_t>( x => OnVolumeChanged?.Invoke( x.NewVolume ) );
}
/// <summary>

View File

@ -30,7 +30,7 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<SteamParentalSettingsChanged_t>( x => OnSettingsChanged?.Invoke() );
Event.CreateEvent<SteamParentalSettingsChanged_t>( x => OnSettingsChanged?.Invoke() );
}
/// <summary>

View File

@ -30,8 +30,8 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<ScreenshotRequested_t>( x => OnScreenshotRequested?.Invoke() );
new Event<ScreenshotReady_t>( x =>
Event.CreateEvent<ScreenshotRequested_t>( x => OnScreenshotRequested?.Invoke() );
Event.CreateEvent<ScreenshotReady_t>( x =>
{
if ( x.Result != Result.OK )
OnScreenshotFailed?.Invoke( x.Result );

View File

@ -30,7 +30,7 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<ValidateAuthTicketResponse_t>( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ), true );
Event.CreateEvent<ValidateAuthTicketResponse_t>( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ), true );
SteamServerInventory.InstallEvents();
}

View File

@ -29,7 +29,7 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<SteamInventoryDefinitionUpdate_t>( x => DefinitionsUpdated(), true );
Event.CreateEvent<SteamInventoryDefinitionUpdate_t>( x => DefinitionsUpdated(), true );
}
public static event Action OnDefinitionsUpdated;

View File

@ -40,14 +40,14 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<SteamServersConnected_t>( x => OnSteamServersConnected?.Invoke() );
new Event<SteamServerConnectFailure_t>( x => OnSteamServerConnectFailure?.Invoke() );
new Event<SteamServersDisconnected_t>( x => OnSteamServersDisconnected?.Invoke() );
new Event<ClientGameServerDeny_t>( x => OnClientGameServerDeny?.Invoke() );
new Event<LicensesUpdated_t>( x => OnLicensesUpdated?.Invoke() );
new Event<ValidateAuthTicketResponse_t>( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ) );
new Event<MicroTxnAuthorizationResponse_t>( x => OnMicroTxnAuthorizationResponse?.Invoke( x.AppID, x.OrderID, x.Authorized != 0 ) );
new Event<GameWebCallback_t>( x => OnGameWebCallback?.Invoke( x.URL ) );
Event.CreateEvent<SteamServersConnected_t>( x => OnSteamServersConnected?.Invoke() );
Event.CreateEvent<SteamServerConnectFailure_t>( x => OnSteamServerConnectFailure?.Invoke() );
Event.CreateEvent<SteamServersDisconnected_t>( x => OnSteamServersDisconnected?.Invoke() );
Event.CreateEvent<ClientGameServerDeny_t>( x => OnClientGameServerDeny?.Invoke() );
Event.CreateEvent<LicensesUpdated_t>( x => OnLicensesUpdated?.Invoke() );
Event.CreateEvent<ValidateAuthTicketResponse_t>( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ) );
Event.CreateEvent<MicroTxnAuthorizationResponse_t>( x => OnMicroTxnAuthorizationResponse?.Invoke( x.AppID, x.OrderID, x.Authorized != 0 ) );
Event.CreateEvent<GameWebCallback_t>( x => OnGameWebCallback?.Invoke( x.URL ) );
}
/// <summary>

View File

@ -32,7 +32,7 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<UserStatsReceived_t>( x =>
Event.CreateEvent<UserStatsReceived_t>( x =>
{
if ( x.SteamIDUser == SteamClient.SteamId )
StatsRecieved = true;
@ -40,9 +40,9 @@ namespace Steamworks
OnUserStatsReceived?.Invoke( x.SteamIDUser, x.Result );
} );
new Event<UserStatsStored_t>( x => OnUserStatsStored?.Invoke( x.Result ) );
new Event<UserAchievementStored_t>( x => OnAchievementProgress?.Invoke( x.AchievementName, (int) x.CurProgress, (int)x.MaxProgress ) );
new Event<UserStatsUnloaded_t>( x => OnUserStatsUnloaded?.Invoke( x.SteamIDUser ) );
Event.CreateEvent<UserStatsStored_t>( x => OnUserStatsStored?.Invoke( x.Result ) );
Event.CreateEvent<UserAchievementStored_t>( x => OnAchievementProgress?.Invoke( x.AchievementName, (int) x.CurProgress, (int)x.MaxProgress ) );
Event.CreateEvent<UserStatsUnloaded_t>( x => OnUserStatsUnloaded?.Invoke( x.SteamIDUser ) );
}
/// <summary>

View File

@ -31,10 +31,10 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<IPCountry_t>( x => OnIpCountryChanged?.Invoke() );
new Event<LowBatteryPower_t>( x => OnLowBatteryPower?.Invoke( x.MinutesBatteryLeft ) );
new Event<SteamShutdown_t>( x => OnSteamShutdown?.Invoke() );
new Event<GamepadTextInputDismissed_t>( x => OnGamepadTextInputDismissed?.Invoke( x.Submitted ) );
Event.CreateEvent<IPCountry_t>( x => OnIpCountryChanged?.Invoke() );
Event.CreateEvent<LowBatteryPower_t>( x => OnLowBatteryPower?.Invoke( x.MinutesBatteryLeft ) );
Event.CreateEvent<SteamShutdown_t>( x => OnSteamShutdown?.Invoke() );
Event.CreateEvent<GamepadTextInputDismissed_t>( x => OnGamepadTextInputDismissed?.Invoke( x.Submitted ) );
}
/// <summary>
@ -166,7 +166,7 @@ namespace Steamworks
/// Asynchronous call to check if an executable file has been signed using the public key set on the signing tab
/// of the partner site, for example to refuse to load modified executable files.
/// </summary>
public static async Task<CheckFileSignature> CheckFileSignature( string filename )
public static async Task<CheckFileSignature> CheckFileSignatureAsync( string filename )
{
var r = await Internal.CheckFileSignature( filename );

View File

@ -31,8 +31,8 @@ namespace Steamworks
internal static void InstallEvents()
{
new Event<BroadcastUploadStart_t>( x => OnBroadcastStarted?.Invoke() );
new Event<BroadcastUploadStop_t>( x => OnBroadcastStopped?.Invoke( x.Result ) );
Event.CreateEvent<BroadcastUploadStart_t>( x => OnBroadcastStarted?.Invoke() );
Event.CreateEvent<BroadcastUploadStop_t>( x => OnBroadcastStopped?.Invoke( x.Result ) );
}
public static event Action OnBroadcastStarted;