diff --git a/Facepunch.Steamworks.Test/Serverlist.cs b/Facepunch.Steamworks.Test/Serverlist.cs index af08fa4..46c53e9 100644 --- a/Facepunch.Steamworks.Test/Serverlist.cs +++ b/Facepunch.Steamworks.Test/Serverlist.cs @@ -17,13 +17,24 @@ public void InternetList() { using ( var client = new Facepunch.Steamworks.Client( 252490 ) ) { - var query = client.ServerList.Test(); + var filter = new Dictionary(); + filter.Add( "appid", client.AppId.ToString() ); + filter.Add( "gamedir", "rust" ); + filter.Add( "secure", "1" ); - for ( int i = 0; i < 100; i++ ) + var query = client.ServerList.Internet( filter ); + + for ( int i = 0; i < 1000; i++ ) { client.Update(); System.Threading.Thread.Sleep( 10 ); + foreach ( var s in query.Responded ) + { + Assert.AreEqual( s.AppId, client.AppId ); + Assert.AreEqual( s.GameDir, "rust" ); + } + if ( query.Finished ) break; } @@ -48,8 +59,11 @@ public void MultipleInternetList() { var queries = new List< Facepunch.Steamworks.ServerList.Request >(); + var filter = new Dictionary(); + filter.Add( "map", "barren" ); + for ( int i = 0; i < 10; i++ ) - queries.Add( client.ServerList.Test() ); + queries.Add( client.ServerList.Internet( filter ) ); for ( int i = 0; i < 100; i++ ) { @@ -72,12 +86,47 @@ public void MultipleInternetList() } } + [TestMethod] + public void Filters() + { + using ( var client = new Facepunch.Steamworks.Client( 252490 ) ) + { + var filter = new Dictionary(); + filter.Add( "map", "barren" ); + + + var query = client.ServerList.Internet( filter ); + + while ( true ) + { + client.Update(); + System.Threading.Thread.Sleep( 2 ); + + if ( query.Finished ) + break; + } + + foreach ( var x in query.Responded ) + { + Assert.AreEqual( x.Map.ToLower(), "barren" ); + } + + query.Dispose(); + + for ( int i = 0; i < 100; i++ ) + { + client.Update(); + System.Threading.Thread.Sleep( 1 ); + } + } + } + [TestMethod] public void HistoryList() { using ( var client = new Facepunch.Steamworks.Client( 252490 ) ) { - var query = client.ServerList.History( new Dictionary() ); + var query = client.ServerList.History(); while ( true ) { @@ -91,6 +140,11 @@ public void HistoryList() Console.WriteLine( "Responded: " + query.Responded.Count.ToString() ); Console.WriteLine( "Unresponsive: " + query.Unresponsive.Count.ToString() ); + foreach ( var x in query.Responded ) + { + Console.WriteLine( x.Map ); + } + query.Dispose(); for ( int i = 0; i < 100; i++ ) diff --git a/Facepunch.Steamworks/Client.ServerList.Request.cs b/Facepunch.Steamworks/Client.ServerList.Request.cs index 5bd5637..398c352 100644 --- a/Facepunch.Steamworks/Client.ServerList.Request.cs +++ b/Facepunch.Steamworks/Client.ServerList.Request.cs @@ -14,6 +14,8 @@ public class Request : IDisposable internal Client client; internal IntPtr Id; + public Action OnUpdate; + public struct Server { public string Name { get; set; } @@ -73,7 +75,7 @@ internal static Server FromSteam( gameserveritem_t item ) Secure = item.m_bSecure, LastTimePlayed = item.m_ulTimeLastPlayed, Version = item.m_nServerVersion, - Tags = item.m_szGameTags.Split( ',' ), + Tags = item.m_szGameTags == null ? null : item.m_szGameTags.Split( ',' ), SteamId = item.m_steamID }; } @@ -116,6 +118,8 @@ private void Update() if ( Id == IntPtr.Zero ) return; + bool changes = false; + // // Add any servers we're not watching to our watch list // @@ -139,6 +143,7 @@ private void Update() if ( info.m_bHadSuccessfulResponse ) { OnServer( info ); + changes = true; return true; } @@ -164,7 +169,11 @@ private void Update() client.OnUpdate -= Update; client.native.servers.CancelQuery( Id ); Id = IntPtr.Zero; + changes = true; } + + if ( changes && OnUpdate != null) + OnUpdate(); } private void OnServer( gameserveritem_t info ) diff --git a/Facepunch.Steamworks/Client.ServerList.cs b/Facepunch.Steamworks/Client.ServerList.cs index 2f113a3..dd6bdeb 100644 --- a/Facepunch.Steamworks/Client.ServerList.cs +++ b/Facepunch.Steamworks/Client.ServerList.cs @@ -40,17 +40,29 @@ private struct MatchPair public unsafe Request Test() { - var filters = new List(); - filters.Add( new MatchPair() { key = "gamedir", value = "rust" } ); + var filters = new Dictionary(); + filters.Add("gamedir", "rust" ); var array = filters.ToArray(); //fixed ( void* a = array ) { + var pairs = filters.Select( x => new MatchMakingKeyValuePair_t() + { + m_szKey = x.Key, + m_szValue = x.Value + }).ToArray(); + + var request = new Request( client ); - request.Id = client.native.servers.RequestInternetServerList( client.AppId, new IntPtr[] { }, IntPtr.Zero ); + GCHandle h = GCHandle.Alloc( pairs, GCHandleType.Pinned ); + var a = h.AddrOfPinnedObject(); + + // request.Id = client.native.servers.RequestInternetServerList( client.AppId, h.AddrOfPinnedObject(), pairs.Length, IntPtr.Zero ); + + h.Free(); return request; } @@ -79,7 +91,64 @@ internal override void ServerResponded( uint hRequest, int iServer ) } } - public unsafe Request History( Dictionary< string, string > filter ) + private IntPtr m_pNativeArray; + private IntPtr m_pArrayEntries; + + internal void FilterStart( Dictionary filter ) + { + var filters = filter.Select( x => + { + return new MatchMakingKeyValuePair_t() + { + m_szKey = x.Key, + m_szValue = x.Value + }; + + } ).ToArray(); + + int sizeOfMMKVP = Marshal.SizeOf(typeof(MatchMakingKeyValuePair_t)); + m_pNativeArray = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( IntPtr ) ) * filters.Length ); + m_pArrayEntries = Marshal.AllocHGlobal( sizeOfMMKVP * filters.Length ); + + for ( int i = 0; i < filters.Length; ++i ) + { + Marshal.StructureToPtr( filters[i], new IntPtr( m_pArrayEntries.ToInt64() + ( i * sizeOfMMKVP ) ), false ); + } + + Marshal.WriteIntPtr( m_pNativeArray, m_pArrayEntries ); + } + + internal void FilterFree() + { + if ( m_pArrayEntries != IntPtr.Zero ) + { + Marshal.FreeHGlobal( m_pArrayEntries ); + } + + if ( m_pNativeArray != IntPtr.Zero ) + { + Marshal.FreeHGlobal( m_pNativeArray ); + } + } + + public Request Internet( Dictionary< string, string > filter ) + { + FilterStart( filter ); + + var request = new Request( client ); + request.Id = client.native.servers.RequestInternetServerList( client.AppId, m_pNativeArray, filter.Count, IntPtr.Zero ); + + FilterFree(); + + return request; + } + + + /// + /// History filters don't seem to work, so we don't bother. + /// You should apply them post process'dly + /// + public Request History() { var request = new Request( client ); request.Id = client.native.servers.RequestHistoryServerList( client.AppId, new IntPtr[] { }, IntPtr.Zero ); @@ -87,6 +156,18 @@ public unsafe Request History( Dictionary< string, string > filter ) return request; } + /// + /// Favourite filters don't seem to work, so we don't bother. + /// You should apply them post process'dly + /// + public Request Favourites() + { + var request = new Request( client ); + request.Id = client.native.servers.RequestFavoritesServerList( client.AppId, new IntPtr[] { }, IntPtr.Zero ); + + return request; + } + public void AddToHistory( Request.Server server ) { // client.native.matchmaking diff --git a/Facepunch.Steamworks/steam_api_interop.cs b/Facepunch.Steamworks/steam_api_interop.cs index 03765ac..3a9af4e 100644 --- a/Facepunch.Steamworks/steam_api_interop.cs +++ b/Facepunch.Steamworks/steam_api_interop.cs @@ -426,7 +426,7 @@ internal class NativeEntrypoints [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamMatchmakingRulesResponse_RulesRefreshComplete" )] internal static extern void SteamAPI_ISteamMatchmakingRulesResponse_RulesRefreshComplete( IntPtr instancePtr ); [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamMatchmakingServers_RequestInternetServerList" )] - internal static extern IntPtr SteamAPI_ISteamMatchmakingServers_RequestInternetServerList( IntPtr instancePtr, uint iApp, [In, Out] IntPtr[] ppchFilters, uint nFilters, IntPtr pRequestServersResponse ); + internal static extern IntPtr SteamAPI_ISteamMatchmakingServers_RequestInternetServerList( IntPtr instancePtr, uint iApp, [In, Out] IntPtr ppchFilters, uint nFilters, IntPtr pRequestServersResponse ); [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamMatchmakingServers_RequestLANServerList" )] internal static extern IntPtr SteamAPI_ISteamMatchmakingServers_RequestLANServerList( IntPtr instancePtr, uint iApp, IntPtr pRequestServersResponse ); [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamMatchmakingServers_RequestFriendsServerList" )] @@ -1816,7 +1816,7 @@ internal abstract class ISteamMatchmakingRulesResponse internal abstract class ISteamMatchmakingServers { internal abstract IntPtr GetIntPtr(); - internal abstract IntPtr RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, IntPtr pRequestServersResponse ); + internal abstract IntPtr RequestInternetServerList( uint iApp, IntPtr ppchFilters, int length, IntPtr pRequestServersResponse ); internal abstract IntPtr RequestLANServerList( uint iApp, IntPtr pRequestServersResponse ); internal abstract IntPtr RequestFriendsServerList( uint iApp, IntPtr[] ppchFilters, IntPtr pRequestServersResponse ); internal abstract IntPtr RequestFavoritesServerList( uint iApp, IntPtr[] ppchFilters, IntPtr pRequestServersResponse ); @@ -3775,10 +3775,10 @@ private void CheckIfUsable() throw new Exception( "Steam Pointer not configured" ); } } - internal override IntPtr RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, IntPtr pRequestServersResponse ) + internal override IntPtr RequestInternetServerList( uint iApp, IntPtr ppchFilters, int length, IntPtr pRequestServersResponse ) { CheckIfUsable(); - IntPtr result = NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_RequestInternetServerList(m_pSteamMatchmakingServers,iApp,ppchFilters,(uint) ppchFilters.Length,pRequestServersResponse); + IntPtr result = NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_RequestInternetServerList(m_pSteamMatchmakingServers,iApp,ppchFilters,(uint) length,pRequestServersResponse); return result; } internal override IntPtr RequestLANServerList( uint iApp, IntPtr pRequestServersResponse )