Pin the SererRules functions so they don't get collected etc

This commit is contained in:
Garry Newman 2016-09-30 11:52:20 +01:00
parent 01c8083869
commit 0cf2761166
2 changed files with 62 additions and 32 deletions

View File

@ -127,6 +127,7 @@
<Compile Include="Client\Screenshots.cs" /> <Compile Include="Client\Screenshots.cs" />
<Compile Include="Client\Stats.cs" /> <Compile Include="Client\Stats.cs" />
<Compile Include="Client\Voice.cs" /> <Compile Include="Client\Voice.cs" />
<Compile Include="Config.cs" />
<Compile Include="Interop\ServerRules.cs" /> <Compile Include="Interop\ServerRules.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="steam_api_interop.cs" /> <Compile Include="steam_api_interop.cs" />

View File

@ -8,52 +8,83 @@ namespace Facepunch.Steamworks.Interop
{ {
class ServerRules class ServerRules
{ {
private GCHandle thisPin; // Pins and pointers for the created vtable
private GCHandle vTablePin;
private IntPtr vTablePtr; private IntPtr vTablePtr;
private GCHandle vTableHandle;
// Pins for the functions
private GCHandle RulesRespondPin;
private GCHandle FailedRespondPin;
private GCHandle CompletePin;
// The server that called us
private ServerList.Server Server; private ServerList.Server Server;
public ServerRules( ServerList.Server server, uint address, int queryPort ) public ServerRules( ServerList.Server server, uint address, int queryPort )
{ {
Server = server; Server = server;
//
// Create a fake VTable to pass to c++
//
InstallVTable(); InstallVTable();
//
// Ask Steam to get the server rules, respond to our fake vtable
//
Valve.Interop.NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_ServerRules( Server.Client.native.servers.GetIntPtr(), address, (short) queryPort, GetPtr() ); Valve.Interop.NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_ServerRules( Server.Client.native.servers.GetIntPtr(), address, (short) queryPort, GetPtr() );
} }
void InstallVTable() void InstallVTable()
{ {
GC.KeepAlive( Server );
GC.SuppressFinalize( Server );
if ( Server.Client.UseThisCall ) //
// Depending on platform, we either use ThisCall or stdcall.
// This is a bit of a fuckabout but you need to define Client.UseThisCall
//
if ( Config.UseThisCall )
{ {
ThisVTable.InternalRulesResponded da = ( _, k, v ) => InternalOnRulesResponded( k, v );
ThisVTable.InternalRulesFailedToRespond db = ( _ ) => InternalOnRulesFailedToRespond();
ThisVTable.InternalRulesRefreshComplete dc = ( _ ) => InternalOnRulesRefreshComplete();
RulesRespondPin = GCHandle.Alloc( da );
FailedRespondPin = GCHandle.Alloc( db );
CompletePin = GCHandle.Alloc( dc );
var t = new ThisVTable() var t = new ThisVTable()
{ {
m_VTRulesResponded = ( _, k, v ) => InternalOnRulesResponded( k, v ), m_VTRulesResponded = da,
m_VTRulesFailedToRespond = ( _ ) => InternalOnRulesFailedToRespond(), m_VTRulesFailedToRespond = db,
m_VTRulesRefreshComplete = ( _ ) => InternalOnRulesRefreshComplete(), m_VTRulesRefreshComplete = dc,
}; };
vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( ThisVTable ) ) ); vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( ThisVTable ) ) );
Marshal.StructureToPtr( t, vTablePtr, false ); Marshal.StructureToPtr( t, vTablePtr, false );
vTableHandle = GCHandle.Alloc( vTablePtr, GCHandleType.Pinned ); vTablePin = GCHandle.Alloc( vTablePtr, GCHandleType.Pinned );
} }
else else
{ {
StdVTable.InternalRulesResponded da = InternalOnRulesResponded;
StdVTable.InternalRulesFailedToRespond db = InternalOnRulesFailedToRespond;
StdVTable.InternalRulesRefreshComplete dc = InternalOnRulesRefreshComplete;
RulesRespondPin = GCHandle.Alloc( da );
FailedRespondPin = GCHandle.Alloc( db );
CompletePin = GCHandle.Alloc( dc );
var t = new StdVTable() var t = new StdVTable()
{ {
m_VTRulesResponded = InternalOnRulesResponded, m_VTRulesResponded = da,
m_VTRulesFailedToRespond = InternalOnRulesFailedToRespond, m_VTRulesFailedToRespond = db,
m_VTRulesRefreshComplete = InternalOnRulesRefreshComplete m_VTRulesRefreshComplete = dc
}; };
vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( StdVTable ) ) ); vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( StdVTable ) ) );
Marshal.StructureToPtr( t, vTablePtr, false ); Marshal.StructureToPtr( t, vTablePtr, false );
vTableHandle = GCHandle.Alloc( vTablePtr, GCHandleType.Pinned ); vTablePin = GCHandle.Alloc( vTablePtr, GCHandleType.Pinned );
} }
} }
@ -62,22 +93,26 @@ void Unpin()
if ( vTablePtr != IntPtr.Zero ) if ( vTablePtr != IntPtr.Zero )
{ {
Marshal.FreeHGlobal( vTablePtr ); Marshal.FreeHGlobal( vTablePtr );
vTablePtr = IntPtr.Zero;
} }
if ( vTableHandle.IsAllocated ) if ( vTablePin.IsAllocated )
{ vTablePin.Free();
vTableHandle.Free();
} if ( RulesRespondPin.IsAllocated )
vTablePin.Free();
if ( FailedRespondPin.IsAllocated )
vTablePin.Free();
if ( CompletePin.IsAllocated )
vTablePin.Free();
if ( thisPin.IsAllocated )
{
thisPin.Free();
}
} }
private void InternalOnRulesResponded( IntPtr k, IntPtr v ) private void InternalOnRulesResponded( string k, string v )
{ {
// Server.Rules.Add( k, v ); Server.Rules.Add( k, v );
} }
private void InternalOnRulesFailedToRespond() private void InternalOnRulesFailedToRespond()
{ {
@ -91,20 +126,17 @@ private void InternalOnRulesRefreshComplete()
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential )]
private class StdVTable private class StdVTable
{ {
[NonSerialized]
[MarshalAs(UnmanagedType.FunctionPtr)] [MarshalAs(UnmanagedType.FunctionPtr)]
public InternalRulesResponded m_VTRulesResponded; public InternalRulesResponded m_VTRulesResponded;
[NonSerialized]
[MarshalAs(UnmanagedType.FunctionPtr)] [MarshalAs(UnmanagedType.FunctionPtr)]
public InternalRulesFailedToRespond m_VTRulesFailedToRespond; public InternalRulesFailedToRespond m_VTRulesFailedToRespond;
[NonSerialized]
[MarshalAs(UnmanagedType.FunctionPtr)] [MarshalAs(UnmanagedType.FunctionPtr)]
public InternalRulesRefreshComplete m_VTRulesRefreshComplete; public InternalRulesRefreshComplete m_VTRulesRefreshComplete;
[UnmanagedFunctionPointer( CallingConvention.StdCall )] [UnmanagedFunctionPointer( CallingConvention.StdCall )]
public delegate void InternalRulesResponded( IntPtr pchRule, IntPtr pchValue ); public delegate void InternalRulesResponded( string pchRule, string pchValue );
[UnmanagedFunctionPointer( CallingConvention.StdCall )] [UnmanagedFunctionPointer( CallingConvention.StdCall )]
public delegate void InternalRulesFailedToRespond(); public delegate void InternalRulesFailedToRespond();
[UnmanagedFunctionPointer( CallingConvention.StdCall )] [UnmanagedFunctionPointer( CallingConvention.StdCall )]
@ -114,20 +146,17 @@ private class StdVTable
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential )]
private class ThisVTable private class ThisVTable
{ {
[NonSerialized]
[MarshalAs(UnmanagedType.FunctionPtr)] [MarshalAs(UnmanagedType.FunctionPtr)]
public InternalRulesResponded m_VTRulesResponded; public InternalRulesResponded m_VTRulesResponded;
[NonSerialized]
[MarshalAs(UnmanagedType.FunctionPtr)] [MarshalAs(UnmanagedType.FunctionPtr)]
public InternalRulesFailedToRespond m_VTRulesFailedToRespond; public InternalRulesFailedToRespond m_VTRulesFailedToRespond;
[NonSerialized]
[MarshalAs(UnmanagedType.FunctionPtr)] [MarshalAs(UnmanagedType.FunctionPtr)]
public InternalRulesRefreshComplete m_VTRulesRefreshComplete; public InternalRulesRefreshComplete m_VTRulesRefreshComplete;
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate void InternalRulesResponded( IntPtr thisptr, IntPtr pchRule, IntPtr pchValue ); public delegate void InternalRulesResponded( IntPtr thisptr, string pchRule, string pchValue );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate void InternalRulesFailedToRespond( IntPtr thisptr ); public delegate void InternalRulesFailedToRespond( IntPtr thisptr );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )] [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
@ -136,7 +165,7 @@ private class ThisVTable
public System.IntPtr GetPtr() public System.IntPtr GetPtr()
{ {
return vTableHandle.AddrOfPinnedObject(); return vTablePin.AddrOfPinnedObject();
} }
}; };
} }