diff --git a/.gitignore b/.gitignore
index 7c5c9fc..04e083e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,4 +61,9 @@ Facepunch.Steamworks/bin/Debug/Facepunch.Steamworks.Api.dll
Facepunch.Steamworks/bin/Debug/Facepunch.Steamworks.dll
Facepunch.Steamworks/bin/Release/Facepunch.Steamworks.dll
*.opendb
-*.db
\ No newline at end of file
+*.db
+Facepunch.Steamworks.dll
+Facepunch.Steamworks.Test.dll
+*UnitTestFramework.dll
+mscorlib.dll
+*.nlp
\ No newline at end of file
diff --git a/Facepunch.Steamworks.Test/Client.cs b/Facepunch.Steamworks.Test/Client.cs
index b9063f8..a6ca188 100644
--- a/Facepunch.Steamworks.Test/Client.cs
+++ b/Facepunch.Steamworks.Test/Client.cs
@@ -9,7 +9,7 @@ namespace Facepunch.Steamworks.Test
public class Client
{
[TestMethod]
- public void ClientInit()
+ public void Init()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@@ -18,7 +18,7 @@ public void ClientInit()
}
[TestMethod]
- public void ClientName()
+ public void Name()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@@ -30,7 +30,7 @@ public void ClientName()
}
[TestMethod]
- public void ClientSteamId()
+ public void SteamId()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@@ -42,7 +42,7 @@ public void ClientSteamId()
}
[TestMethod]
- public void ClientAuthSessionTicket()
+ public void AuthSessionTicket()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@@ -59,7 +59,7 @@ public void ClientAuthSessionTicket()
}
[TestMethod]
- public void ClientVoiceOptimalSampleRate()
+ public void VoiceOptimalSampleRate()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@@ -69,7 +69,7 @@ public void ClientVoiceOptimalSampleRate()
}
[TestMethod]
- public void ClientUpdate()
+ public void Update()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@@ -82,7 +82,7 @@ public void ClientUpdate()
}
[TestMethod]
- public void ClientGetVoice()
+ public void GetVoice()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@@ -110,5 +110,26 @@ public void ClientGetVoice()
Console.Write( dataRead );
}
}
+
+ [TestMethod]
+ public void GetServers()
+ {
+ using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
+ {
+ var query = client.ServerList.Test();
+
+ for ( int i = 0; i < 100 ; i++ )
+ {
+ client.Update();
+ System.Threading.Thread.Sleep( 5 );
+
+ if ( query.Finished )
+ break;
+ }
+
+ Console.WriteLine( "Responded: " + query.Responded.Count.ToString() );
+ Console.WriteLine( "Unresponsive: " + query.Unresponsive.Count.ToString() );
+ }
+ }
}
}
diff --git a/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj b/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj
index d83c936..53bbb4a 100644
--- a/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj
+++ b/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj
@@ -34,6 +34,42 @@
prompt
4
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x64\Release\
+ TRACE
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE
+ full
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x86\Release\
+ TRACE
+ true
+ pdbonly
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+
diff --git a/Facepunch.Steamworks/Client.ServerList.Request.cs b/Facepunch.Steamworks/Client.ServerList.Request.cs
new file mode 100644
index 0000000..9537eb4
--- /dev/null
+++ b/Facepunch.Steamworks/Client.ServerList.Request.cs
@@ -0,0 +1,210 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using Valve.Steamworks;
+
+namespace Facepunch.Steamworks
+{
+ public partial class ServerList
+ {
+ public class Request : IDisposable
+ {
+ internal Client client;
+ internal uint Id;
+
+ private IntPtr m_pVTable;
+ private GCHandle m_pGCHandle;
+
+ public struct Server
+ {
+ public string Name { get; set; }
+ public int Ping { get; set; }
+ public string GameDir { get; set; }
+ public string Map { get; set; }
+ public string Description { get; set; }
+ public uint AppId { get; set; }
+ public int Players { get; set; }
+ public int MaxPlayers { get; set; }
+ public int BotPlayers { get; set; }
+ public bool Passworded { get; set; }
+ public bool Secure { get; set; }
+ public uint LastTimePlayed { get; set; }
+ public int Version { get; set; }
+ public string[] Tags { get; set; }
+ public ulong SteamId { get; set; }
+
+ internal static Server FromSteam( gameserveritem_t item )
+ {
+ return new Server()
+ {
+ Name = item.m_szServerName,
+ Ping = item.m_nPing,
+ GameDir = item.m_szGameDir,
+ Map = item.m_szMap,
+ Description = item.m_szGameDescription,
+ AppId = item.m_nAppID,
+ Players = item.m_nPlayers,
+ MaxPlayers = item.m_nMaxPlayers,
+ BotPlayers = item.m_nBotPlayers,
+ Passworded = item.m_bPassword,
+ Secure = item.m_bSecure,
+ LastTimePlayed = item.m_ulTimeLastPlayed,
+ Version = item.m_nServerVersion,
+ Tags = item.m_szGameTags.Split( ',' ),
+ SteamId = item.m_steamID
+ };
+ }
+ }
+
+
+ public servernetadr_t m_NetAdr;
+ public int m_nPing;
+ [MarshalAs(UnmanagedType.I1)]
+ public bool m_bHadSuccessfulResponse;
+ [MarshalAs(UnmanagedType.I1)]
+ public bool m_bDoNotRefresh;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string m_szGameDir; //char[32]
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string m_szMap; //char[32]
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string m_szGameDescription; //char[64]
+ public uint m_nAppID;
+ public int m_nPlayers;
+ public int m_nMaxPlayers;
+ public int m_nBotPlayers;
+ [MarshalAs(UnmanagedType.I1)]
+ public bool m_bPassword;
+ [MarshalAs(UnmanagedType.I1)]
+ public bool m_bSecure;
+ public uint m_ulTimeLastPlayed;
+ public int m_nServerVersion;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string m_szServerName; //char[64]
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public string m_szGameTags; //char[128]
+ public ulong m_steamID;
+
+ ///
+ /// A list of servers that responded. If you're only interested in servers that responded since you
+ /// last updated, then simply clear this list.
+ ///
+ public List Responded = new List();
+
+ ///
+ /// A list of servers that were in the master list but didn't respond.
+ ///
+ public List Unresponsive = new List();
+
+ ///
+ /// True when we have finished
+ ///
+ public bool Finished = false;
+
+ internal Request()
+ {
+ //
+ // Create a fake vtable for Steam to respond to
+ //
+ var vt = new VTable()
+ {
+ responded = OnServerResponded,
+ nonresponsive = NonResponsive,
+ complete = Complete
+ };
+
+ m_pVTable = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( VTable ) ) );
+ Marshal.StructureToPtr( vt, m_pVTable, false );
+ m_pGCHandle = GCHandle.Alloc( m_pVTable, GCHandleType.Pinned );
+ }
+
+ ~Request()
+ {
+ Dispose();
+ }
+
+ ///
+ /// Disposing will end the query
+ ///
+ public void Dispose()
+ {
+ //
+ // Cancel the query if it's still running
+ //
+ if ( !Finished && Id > 0 )
+ {
+ if ( client.Valid )
+ client._servers.CancelQuery( Id );
+
+ Id = 0;
+ }
+
+ //
+ // Release the pinned GC resources
+ //
+ if ( m_pVTable != IntPtr.Zero )
+ {
+ Marshal.FreeHGlobal( m_pVTable );
+ m_pVTable = IntPtr.Zero;
+ }
+
+ if ( m_pGCHandle.IsAllocated )
+ {
+ m_pGCHandle.Free();
+ }
+ }
+
+ private void Complete( IntPtr thisptr, uint RequestId, int response )
+ {
+ if ( RequestId != Id )
+ throw new Exception( "Request ID is invalid!" );
+
+ Finished = true;
+ Id = 0;
+ }
+
+ private void NonResponsive( IntPtr thisptr, uint RequestId, int iServer )
+ {
+ if ( RequestId != Id )
+ throw new Exception( "Request ID is invalid!" );
+
+ var info = client._servers.GetServerDetails( Id, iServer );
+ Unresponsive.Add( Server.FromSteam( info ) );
+ }
+
+ private void OnServerResponded( IntPtr thisptr, uint RequestId, int iServer )
+ {
+ if ( RequestId != Id )
+ throw new Exception( "Request ID is invalid!" );
+
+ var info = client._servers.GetServerDetails( Id, iServer );
+ Responded.Add( Server.FromSteam( info ) );
+ }
+
+ internal IntPtr GetVTablePointer()
+ {
+ return m_pGCHandle.AddrOfPinnedObject();
+ }
+
+ [StructLayout( LayoutKind.Sequential )]
+ internal class VTable
+ {
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ internal delegate void InternalServerResponded( IntPtr thisptr, uint hRequest, int iServer );
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ internal delegate void InternalServerFailedToRespond( IntPtr thisptr, uint hRequest, int iServer );
+ [UnmanagedFunctionPointer( CallingConvention.ThisCall )]
+ internal delegate void InternalRefreshComplete( IntPtr thisptr, uint hRequest, int response );
+
+ [NonSerialized, MarshalAs(UnmanagedType.FunctionPtr)]
+ internal InternalServerResponded responded;
+ [NonSerialized, MarshalAs(UnmanagedType.FunctionPtr)]
+ internal InternalServerFailedToRespond nonresponsive;
+ [NonSerialized, MarshalAs(UnmanagedType.FunctionPtr)]
+ internal InternalRefreshComplete complete;
+ }
+ }
+ }
+}
diff --git a/Facepunch.Steamworks/Client.ServerList.cs b/Facepunch.Steamworks/Client.ServerList.cs
index f014820..ad2ff2f 100644
--- a/Facepunch.Steamworks/Client.ServerList.cs
+++ b/Facepunch.Steamworks/Client.ServerList.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Text;
+using Valve.Steamworks;
namespace Facepunch.Steamworks
{
@@ -21,9 +23,40 @@ public ServerList ServerList
}
}
- public class ServerList
+
+
+ public partial class ServerList
{
internal Client client;
+ [StructLayout( LayoutKind.Sequential )]
+ private struct MatchPair
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+ public string key;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+ public string value;
+ }
+
+ public unsafe Request Test()
+ {
+ var filters = new List();
+ filters.Add( new MatchPair() { key = "gamedir", value = "rust" } );
+
+ var array = filters.ToArray();
+
+ //fixed ( void* a = array )
+ {
+
+ var request = new Request()
+ {
+ client = client
+ };
+
+ request.Id = client._servers.RequestInternetServerList( client.AppId, new IntPtr[] { }, request.GetVTablePointer() );
+
+ return request;
+ }
+ }
}
}
diff --git a/Facepunch.Steamworks/Client.cs b/Facepunch.Steamworks/Client.cs
index c85940f..6404ee3 100644
--- a/Facepunch.Steamworks/Client.cs
+++ b/Facepunch.Steamworks/Client.cs
@@ -18,7 +18,7 @@ public partial class Client : IDisposable
///
/// Current running program's AppId
///
- public int AppId;
+ public uint AppId;
///
/// Current user's Username
@@ -30,9 +30,9 @@ public partial class Client : IDisposable
///
public ulong SteamId;
- public Client( int appId )
+ public Client( uint appId )
{
- Valve.Steamworks.SteamAPI.Init( (uint) appId );
+ Valve.Steamworks.SteamAPI.Init( appId );
_client = Valve.Steamworks.SteamAPI.SteamClient();
if ( _client.GetIntPtr() == IntPtr.Zero )
diff --git a/Facepunch.Steamworks/Facepunch.Steamworks.csproj b/Facepunch.Steamworks/Facepunch.Steamworks.csproj
index ac414a1..bcf343e 100644
--- a/Facepunch.Steamworks/Facepunch.Steamworks.csproj
+++ b/Facepunch.Steamworks/Facepunch.Steamworks.csproj
@@ -34,6 +34,76 @@
true
+
+ true
+ bin\Debug64\
+ DEBUG;TRACE
+ true
+ full
+ AnyCPU
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE
+ true
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x64\Release\
+ TRACE
+ true
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ true
+ bin\x64\Debug64\
+ DEBUG;TRACE
+ true
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE
+ true
+ full
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x86\Release\
+ TRACE
+ true
+ true
+ pdbonly
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ true
+ bin\x86\Debug64\
+ DEBUG;TRACE
+ true
+ full
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+
@@ -45,6 +115,7 @@
+
diff --git a/Facepunch.Steamworks/steam_api_interop.cs b/Facepunch.Steamworks/steam_api_interop.cs
index 0344964..a3fb9dd 100644
--- a/Facepunch.Steamworks/steam_api_interop.cs
+++ b/Facepunch.Steamworks/steam_api_interop.cs
@@ -1816,7 +1816,7 @@ internal abstract class ISteamMatchmakingRulesResponse
internal abstract class ISteamMatchmakingServers
{
internal abstract IntPtr GetIntPtr();
- internal abstract uint RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, ISteamMatchmakingServerListResponse pRequestServersResponse );
+ internal abstract uint RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, IntPtr pRequestServersResponse );
internal abstract uint RequestLANServerList( uint iApp, ISteamMatchmakingServerListResponse pRequestServersResponse );
internal abstract uint RequestFriendsServerList( uint iApp, IntPtr[] ppchFilters, ISteamMatchmakingServerListResponse pRequestServersResponse );
internal abstract uint RequestFavoritesServerList( uint iApp, IntPtr[] ppchFilters, ISteamMatchmakingServerListResponse pRequestServersResponse );
@@ -3775,10 +3775,10 @@ private void CheckIfUsable()
throw new Exception( "Steam Pointer not configured" );
}
}
- internal override uint RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, ISteamMatchmakingServerListResponse pRequestServersResponse )
+ internal override uint RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, IntPtr pRequestServersResponse )
{
CheckIfUsable();
- uint result = NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_RequestInternetServerList(m_pSteamMatchmakingServers,iApp,ppchFilters,(uint) ppchFilters.Length,pRequestServersResponse.GetIntPtr());
+ uint result = NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_RequestInternetServerList(m_pSteamMatchmakingServers,iApp,ppchFilters,(uint) ppchFilters.Length,pRequestServersResponse);
return result;
}
internal override uint RequestLANServerList( uint iApp, ISteamMatchmakingServerListResponse pRequestServersResponse )