using System.Threading; namespace Steamworks.Data { internal struct LobbyQuery { // TODO FILTERS // AddRequestLobbyListStringFilter // - WithoutKeyValue #region Distance Filter internal LobbyDistanceFilter? distance; /// /// only lobbies in the same immediate region will be returned /// internal LobbyQuery FilterDistanceClose() { distance = LobbyDistanceFilter.Close; return this; } /// /// only lobbies in the same immediate region will be returned /// internal LobbyQuery FilterDistanceFar() { distance = LobbyDistanceFilter.Far; return this; } /// /// only lobbies in the same immediate region will be returned /// internal LobbyQuery FilterDistanceWorldwide() { distance = LobbyDistanceFilter.Worldwide; return this; } #endregion #region String key/value filter internal Dictionary stringFilters; /// /// Filter by specified key/value pair; string parameters /// internal LobbyQuery WithKeyValue( string key, string value ) { if ( string.IsNullOrEmpty( key ) ) throw new System.ArgumentException( "Key string provided for LobbyQuery filter is null or empty", nameof( key ) ); if ( key.Length > SteamMatchmaking.MaxLobbyKeyLength ) throw new System.ArgumentException( $"Key length is longer than {SteamMatchmaking.MaxLobbyKeyLength}", nameof( key ) ); stringFilters ??= new Dictionary(); stringFilters.Add( key, value ); return this; } #endregion #region Numerical filters internal List numericalFilters; /// /// Numerical filter where value is less than the value provided /// internal LobbyQuery WithLower( string key, int value ) { AddNumericalFilter( key, value, LobbyComparison.LessThan ); return this; } /// /// Numerical filter where value is greater than the value provided /// internal LobbyQuery WithHigher( string key, int value ) { AddNumericalFilter( key, value, LobbyComparison.GreaterThan ); return this; } /// /// Numerical filter where value must be equal to the value provided /// internal LobbyQuery WithEqual( string key, int value ) { AddNumericalFilter( key, value, LobbyComparison.Equal ); return this; } /// /// Numerical filter where value must not equal the value provided /// internal LobbyQuery WithNotEqual( string key, int value ) { AddNumericalFilter( key, value, LobbyComparison.NotEqual ); return this; } /// /// Test key, initialize numerical filter list if necessary, then add new numerical filter /// internal void AddNumericalFilter( string key, int value, LobbyComparison compare ) { if ( string.IsNullOrEmpty( key ) ) throw new System.ArgumentException( "Key string provided for LobbyQuery filter is null or empty", nameof( key ) ); if ( key.Length > SteamMatchmaking.MaxLobbyKeyLength ) throw new System.ArgumentException( $"Key length is longer than {SteamMatchmaking.MaxLobbyKeyLength}", nameof( key ) ); if ( numericalFilters == null ) numericalFilters = new List(); numericalFilters.Add( new NumericalFilter( key, value, compare ) ); } #endregion #region Near value filter internal Dictionary nearValFilters; /// /// Order filtered results according to key/values nearest the provided key/value pair. /// Can specify multiple near value filters; each successive filter is lower priority than the previous. /// internal LobbyQuery OrderByNear( string key, int value ) { if ( string.IsNullOrEmpty( key ) ) throw new System.ArgumentException( "Key string provided for LobbyQuery filter is null or empty", nameof( key ) ); if ( key.Length > SteamMatchmaking.MaxLobbyKeyLength ) throw new System.ArgumentException( $"Key length is longer than {SteamMatchmaking.MaxLobbyKeyLength}", nameof( key ) ); if ( nearValFilters == null ) nearValFilters = new Dictionary(); nearValFilters.Add( key, value ); return this; } #endregion #region Slots Filter internal int? slotsAvailable; /// /// returns only lobbies with the specified number of slots available /// internal LobbyQuery WithSlotsAvailable( int minSlots ) { slotsAvailable = minSlots; return this; } #endregion #region Max results filter internal int? maxResults; /// /// sets how many results to return, the lower the count the faster it is to download the lobby results /// internal LobbyQuery WithMaxResults( int max ) { maxResults = max; return this; } #endregion void ApplyFilters() { if ( distance.HasValue ) { SteamMatchmaking.Internal.AddRequestLobbyListDistanceFilter( distance.Value ); } if ( slotsAvailable.HasValue ) { SteamMatchmaking.Internal.AddRequestLobbyListFilterSlotsAvailable( slotsAvailable.Value ); } if ( maxResults.HasValue ) { SteamMatchmaking.Internal.AddRequestLobbyListResultCountFilter( maxResults.Value ); } if ( stringFilters != null ) { foreach ( var k in stringFilters ) { SteamMatchmaking.Internal.AddRequestLobbyListStringFilter( k.Key, k.Value, LobbyComparison.Equal ); } } if ( numericalFilters != null ) { foreach ( var n in numericalFilters ) { SteamMatchmaking.Internal.AddRequestLobbyListNumericalFilter( n.Key, n.Value, n.Comparer ); } } if ( nearValFilters != null ) { foreach ( var v in nearValFilters ) { SteamMatchmaking.Internal.AddRequestLobbyListNearValueFilter( v.Key, v.Value ); } } } // // Only one lobby query can run at a time. If we run another while // one is running, the last query will be discarded and the async will never // complete! // static SemaphoreSlim lobbyQuerySemaphore = new SemaphoreSlim( 1 ); /// /// Run the query, get the matching lobbies /// internal async Task RequestAsync( CancellationToken ct ) { using var cts = CancellationTokenSource.CreateLinkedTokenSource( ct ); cts.CancelAfter( TimeSpan.FromSeconds( 30 ) ); ct = cts.Token; try { await lobbyQuerySemaphore.WaitAsync(); ApplyFilters(); var task = SteamMatchmaking.Internal.RequestLobbyList(); while ( !task.IsCompleted ) { await Task.Delay( 100, ct ); } LobbyMatchList_t? list = task.GetResult(); if ( !list.HasValue || list.Value.LobbiesMatching == 0 ) return null; Lobby[] lobbies = new Lobby[list.Value.LobbiesMatching]; for ( int i = 0; i < list.Value.LobbiesMatching; i++ ) { lobbies[i] = new Lobby { Id = SteamMatchmaking.Internal.GetLobbyByIndex( i ) }; } return lobbies; } finally { lobbyQuerySemaphore.Release(); } } } }