diff --git a/Facepunch.Steamworks.Test/Serverlist.cs b/Facepunch.Steamworks.Test/Serverlist.cs
index e0b45b0..d0c687a 100644
--- a/Facepunch.Steamworks.Test/Serverlist.cs
+++ b/Facepunch.Steamworks.Test/Serverlist.cs
@@ -306,7 +306,9 @@ namespace Facepunch.Steamworks.Test
for ( int i = 0; i < 1000; i++ )
{
+ GC.Collect();
client.Update();
+ GC.Collect();
System.Threading.Thread.Sleep( 10 );
if ( query.Responded.Count > 20 )
@@ -320,13 +322,17 @@ namespace Facepunch.Steamworks.Test
foreach ( var server in query.Responded.Take( 20 ) )
{
+ GC.Collect();
server.FetchRules();
+ GC.Collect();
int i = 0;
while ( !server.HasRules )
{
i++;
+ GC.Collect();
client.Update();
+ GC.Collect();
System.Threading.Thread.Sleep( 2 );
if ( i > 100 )
diff --git a/Facepunch.Steamworks/Facepunch.Steamworks.csproj b/Facepunch.Steamworks/Facepunch.Steamworks.csproj
index f32b152..d4ca9cb 100644
--- a/Facepunch.Steamworks/Facepunch.Steamworks.csproj
+++ b/Facepunch.Steamworks/Facepunch.Steamworks.csproj
@@ -119,6 +119,7 @@
+
@@ -126,10 +127,11 @@
+
-
+
diff --git a/Facepunch.Steamworks/Interop/ServerRules.cs b/Facepunch.Steamworks/Interop/ServerRules.cs
new file mode 100644
index 0000000..5c03445
--- /dev/null
+++ b/Facepunch.Steamworks/Interop/ServerRules.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Facepunch.Steamworks.Interop
+{
+ class ServerRules
+ {
+ private GCHandle thisPin;
+ private IntPtr vTablePtr;
+ private GCHandle vTableHandle;
+
+ private ServerList.Server Server;
+
+ public ServerRules( ServerList.Server server, uint address, int queryPort )
+ {
+ Server = server;
+
+ InstallVTable();
+
+ Valve.Interop.NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_ServerRules( Server.Client.native.servers.GetIntPtr(), address, (short) queryPort, GetPtr() );
+ }
+
+ void InstallVTable()
+ {
+ GC.KeepAlive( Server );
+ GC.SuppressFinalize( Server );
+
+ if ( Server.Client.UseThisCall )
+ {
+ var t = new ThisVTable()
+ {
+ m_VTRulesResponded = ( _, k, v ) => InternalOnRulesResponded( k, v ),
+ m_VTRulesFailedToRespond = ( _ ) => InternalOnRulesFailedToRespond(),
+ m_VTRulesRefreshComplete = ( _ ) => InternalOnRulesRefreshComplete(),
+ };
+ vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( ThisVTable ) ) );
+ Marshal.StructureToPtr( t, vTablePtr, false );
+
+ vTableHandle = GCHandle.Alloc( vTablePtr, GCHandleType.Pinned );
+
+ }
+ else
+ {
+ var t = new StdVTable()
+ {
+ m_VTRulesResponded = InternalOnRulesResponded,
+ m_VTRulesFailedToRespond = InternalOnRulesFailedToRespond,
+ m_VTRulesRefreshComplete = InternalOnRulesRefreshComplete
+ };
+ vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( StdVTable ) ) );
+ Marshal.StructureToPtr( t, vTablePtr, false );
+
+ vTableHandle = GCHandle.Alloc( vTablePtr, GCHandleType.Pinned );
+ }
+ }
+
+ void Unpin()
+ {
+ if ( vTablePtr != IntPtr.Zero )
+ {
+ Marshal.FreeHGlobal( vTablePtr );
+ }
+
+ if ( vTableHandle.IsAllocated )
+ {
+ vTableHandle.Free();
+ }
+
+ if ( thisPin.IsAllocated )
+ {
+ thisPin.Free();
+ }
+ }
+
+ private void InternalOnRulesResponded( IntPtr k, IntPtr v )
+ {
+ // Server.Rules.Add( k, v );
+ }
+ private void InternalOnRulesFailedToRespond()
+ {
+ Server.OnServerRulesReceiveFinished( false );
+ }
+ private void InternalOnRulesRefreshComplete()
+ {
+ Server.OnServerRulesReceiveFinished( true );
+ }
+
+ [StructLayout( LayoutKind.Sequential )]
+ private class StdVTable
+ {
+ [NonSerialized]
+ [MarshalAs(UnmanagedType.FunctionPtr)]
+ public InternalRulesResponded m_VTRulesResponded;
+
+ [NonSerialized]
+ [MarshalAs(UnmanagedType.FunctionPtr)]
+ public InternalRulesFailedToRespond m_VTRulesFailedToRespond;
+
+ [NonSerialized]
+ [MarshalAs(UnmanagedType.FunctionPtr)]
+ public InternalRulesRefreshComplete m_VTRulesRefreshComplete;
+
+ [UnmanagedFunctionPointer( CallingConvention.StdCall )]
+ public delegate void InternalRulesResponded( IntPtr pchRule, IntPtr pchValue );
+ [UnmanagedFunctionPointer( CallingConvention.StdCall )]
+ public delegate void InternalRulesFailedToRespond();
+ [UnmanagedFunctionPointer( CallingConvention.StdCall )]
+ public delegate void InternalRulesRefreshComplete();
+ }
+
+ [StructLayout( LayoutKind.Sequential )]
+ private class ThisVTable
+ {
+ [NonSerialized]
+ [MarshalAs(UnmanagedType.FunctionPtr)]
+ public InternalRulesResponded m_VTRulesResponded;
+
+ [NonSerialized]
+ [MarshalAs(UnmanagedType.FunctionPtr)]
+ public InternalRulesFailedToRespond m_VTRulesFailedToRespond;
+
+ [NonSerialized]
+ [MarshalAs(UnmanagedType.FunctionPtr)]
+ public InternalRulesRefreshComplete m_VTRulesRefreshComplete;
+
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void InternalRulesResponded( IntPtr thisptr, IntPtr pchRule, IntPtr pchValue );
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void InternalRulesFailedToRespond( IntPtr thisptr );
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ public delegate void InternalRulesRefreshComplete( IntPtr thisptr );
+ }
+
+ public System.IntPtr GetPtr()
+ {
+ return vTableHandle.AddrOfPinnedObject();
+ }
+ };
+}