SteamApps events

This commit is contained in:
Garry Newman 2019-04-14 20:48:14 +01:00
parent 027a110c61
commit cd30b85444
5 changed files with 255 additions and 6 deletions

View File

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using SteamNative;
namespace Steamworks
{
//
// Created on registration of a callback
//
internal class Event<T> : IDisposable where T : struct, Steamworks.ISteamCallback
{
public Action<T> Action;
List<GCHandle> Allocations = new List<GCHandle>();
internal IntPtr vTablePtr;
internal GCHandle PinnedCallback;
public void Dispose()
{
UnregisterCallback();
foreach ( var a in Allocations )
{
if ( a.IsAllocated )
a.Free();
}
Allocations.Clear();
if ( PinnedCallback.IsAllocated )
PinnedCallback.Free();
if ( vTablePtr != IntPtr.Zero )
{
Marshal.FreeHGlobal( vTablePtr );
vTablePtr = IntPtr.Zero;
}
}
private void UnregisterCallback()
{
if ( !PinnedCallback.IsAllocated )
return;
Steam.UnregisterCallback( PinnedCallback.AddrOfPinnedObject() );
}
public virtual bool IsValid { get { return true; } }
T template;
internal Event( Action<T> onresult, bool gameserver = false )
{
Action = onresult;
template = new T();
//
// Create the functions we need for the vtable
//
if ( Facepunch.Steamworks.Config.UseThisCall )
{
//
// Create the VTable by manually allocating the memory and copying across
//
if ( Platform.IsWindows )
{
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 ( Platform.IsWindows )
{
vTablePtr = Callback.VTableWin.GetVTable( OnResult, OnResultWithInfo, OnGetSize, Allocations );
}
else
{
vTablePtr = Callback.VTable.GetVTable( OnResult, OnResultWithInfo, OnGetSize, Allocations );
}
}
//
// Create the callback object
//
var cb = new Callback();
cb.vTablePtr = vTablePtr;
cb.CallbackFlags = gameserver ? (byte)0x02 : (byte)0;
cb.CallbackId = 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 );
//
// Register the callback with Steam
//
Steam.RegisterCallback( PinnedCallback.AddrOfPinnedObject(), cb.CallbackId );
}
[MonoPInvokeCallback] internal void OnResultThis( IntPtr self, IntPtr param ) => OnResult( param );
[MonoPInvokeCallback] internal void OnResultWithInfoThis( IntPtr self, IntPtr param, bool failure, SteamNative.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 );
[MonoPInvokeCallback]
internal void OnResultWithInfo( IntPtr param, bool failure, SteamNative.SteamAPICall_t call )
{
if ( failure ) return;
var value = (T)template.Fill( param );
Action( value );
}
}
}

View File

@ -13,4 +13,13 @@ public static class SteamApi
[DllImport( "Steam_api64", EntryPoint = "SteamAPI_GetHSteamUser", CallingConvention = CallingConvention.Cdecl )]
public static extern int GetHSteamUser();
[DllImport( "Steam_api64", EntryPoint = "SteamAPI_RunCallbacks", CallingConvention = CallingConvention.Cdecl )]
public static extern int RunCallbacks();
[DllImport( "Steam_api64", EntryPoint = "SteamAPI_RegisterCallback", CallingConvention = CallingConvention.Cdecl )]
public static extern int RegisterCallback( IntPtr pCallback, int callback );
[DllImport( "Steam_api64", EntryPoint = "SteamAPI_UnregisterCallback", CallingConvention = CallingConvention.Cdecl )]
public static extern int UnregisterCallback( IntPtr pCallback );
}

View File

@ -1,4 +1,5 @@
using System;
using SteamNative;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
@ -24,6 +25,24 @@ internal static Internal.ISteamApps steamapps
}
}
internal static void InstallEvents()
{
new Event<DlcInstalled_t>( x => OnDlcInstalled( x.AppID ) );
new Event<NewUrlLaunchParameters_t>( x => OnNewLaunchParameters() );
}
/// <summary>
/// posted after the user gains ownership of DLC & that DLC is installed
/// </summary>
public static event Action< AppId > OnDlcInstalled;
/// <summary>
/// posted after the user gains executes a Steam URL with command line or query parameters
/// such as steam://run/appid//-commandline/?param1=value1&param2=value2&param3=value3 etc
/// while the game is already running. The new params can be queried
/// with GetLaunchQueryParam and GetLaunchCommandLine
/// </summary>
public static event Action OnNewLaunchParameters;
/// <summary>
/// Checks if the active user is subscribed to the current App ID

View File

@ -31,5 +31,15 @@ public static void Init( uint appid )
throw new System.Exception( "GetHSteamUser returned 0" );
}
}
internal static void RegisterCallback( IntPtr intPtr, int callbackId )
{
SteamApi.RegisterCallback( intPtr, callbackId );
}
internal static void UnregisterCallback( IntPtr intPtr )
{
SteamApi.UnregisterCallback( intPtr );
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using Facepunch.Steamworks;
namespace SteamNative
@ -27,6 +28,26 @@ public class VTable
public ResultD ResultA;
public ResultWithInfoD ResultB;
public GetSizeD GetSize;
internal static IntPtr GetVTable( ResultD onResultThis, ResultWithInfoD onResultWithInfoThis, GetSizeD onGetSizeThis, List<GCHandle> allocations )
{
var vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback.VTable ) ) );
var vTable = new Callback.VTable
{
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;
}
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
@ -39,6 +60,26 @@ public class VTableWin
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;
}
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
@ -46,6 +87,27 @@ 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, SteamNative.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;
@ -63,6 +125,26 @@ public class VTableWinThis
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;
}
}
};