From 14e78ad4ed7523feaf60230ab5b1f2e0286e30cc Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Sat, 19 Aug 2023 22:29:44 +0300 Subject: [PATCH 01/10] add lobby list request filtering --- src/matchmaking.rs | 270 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 57cc886..8d31213 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -309,6 +309,276 @@ impl Matchmaking { false => Err(SteamError::IOFailure), } } + /// Adds a string comparison filter to the lobby list request. + /// + /// This method adds a filter that compares a specific string attribute in lobbies + /// with the provided value. Lobbies matching this criterion will be included in the result. + /// + /// # Arguments + /// + /// * `key`: The attribute key to compare. + /// * `value`: The value to compare against. + /// + pub fn set_request_lobby_list_string_filter(&self, key: &str, value: &str) { + unsafe { + sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListStringFilter( + self.mm, + key.as_ptr() as _, + value.as_ptr() as _, + sys::ELobbyComparison::k_ELobbyComparisonEqual, + ); + } + } + /// Adds a numerical comparison filter to the lobby list request. + /// + /// This method adds a filter that compares a specific numerical attribute in lobbies + /// with the provided value. Lobbies matching this criterion will be included in the result. + /// + /// # Arguments + /// + /// * `key`: The attribute key to compare. + /// * `value`: The value to compare against. + /// + pub fn set_request_lobby_list_numerical_filter(&self, key: &str, value: i32) { + unsafe { + sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListNumericalFilter( + self.mm, + key.as_ptr() as _, + value, + sys::ELobbyComparison::k_ELobbyComparisonEqual, + ); + } + } + /// Adds a near value filter to the lobby list request. + /// + /// This method adds a filter that sorts the lobby results based on their closeness + /// to a specific value. No actual filtering is performed; lobbies are sorted based on proximity. + /// + /// # Arguments + /// + /// * `key`: The attribute key to use for sorting. + /// * `value`: The reference value for sorting. + /// + pub fn set_request_lobby_list_near_value_filter(&self, key: &str, value: i32) { + unsafe { + sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListNearValueFilter( + self.mm, + key.as_ptr() as _, + value, + ); + } + } + /// Adds a filter for available open slots to the lobby list request. + /// + /// This method adds a filter that includes lobbies having a specific number of open slots. + /// + /// # Arguments + /// + /// * `open_slots`: The number of open slots in a lobby to filter by. + /// + pub fn set_request_lobby_list_slots_available_filter(&self, open_slots: u8) { + unsafe { + sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListFilterSlotsAvailable( + self.mm, + open_slots as i32, + ); + } + } + /// Adds a distance filter to the lobby list request. + /// + /// This method adds a filter that includes lobbies within a certain distance criterion. + /// + /// # Arguments + /// + /// * `distance`: The `DistanceFilter` indicating the distance criterion for the filter. + /// + pub fn set_request_lobby_list_distance_filter(&self, distance: DistanceFilter) { + unsafe { + sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListDistanceFilter( + self.mm, + distance.into(), + ); + } + } + /// Adds a result count filter to the lobby list request. + /// + /// This method adds a filter to limit the number of lobby results returned by the request. + /// + /// # Arguments + /// + /// * `count`: The maximum number of lobby results to include in the response. + /// + pub fn set_request_lobby_list_result_count_filter(&self, count: u64) { + unsafe { + sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListResultCountFilter( + self.mm, + count as i32, + ); + } + } + + /// Sets filters for the lobbies to be returned from [`request_lobby_list`]. + /// + /// This method is used to apply various filters to the lobby list retrieval process. + /// Call this method before calling `request_lobby_list` to ensure that the specified filters + /// are taken into account when fetching the list of available lobbies. + /// + /// # Arguments + /// + /// * `filter`: A [`LobbyListFilter`] struct containing the filter criteria to be applied. + /// + /// [`request_lobby_list`]: #method.request_lobby_list + /// [`LobbyListFilter`]: struct.LobbyListFilter.html + /// + /// # Example + /// + /// ```no_run + /// # use steamworks::*; + /// fn main() { + /// let (client, single) = Client::init().unwrap(); + /// client.matchmaking().set_lobby_list_filter( + /// LobbyListFilter { + /// string: Some(("name", "My Lobby")), + /// ..Default::default() + /// } + /// ).request_lobby_list(|lobbies| { + /// println!("Lobbies: {:?}", lobbies); + /// }); + /// } + /// ``` + pub fn set_lobby_list_filter(&self, filter: LobbyListFilter) -> &Self { + if let Some((key, value)) = filter.string { + self.set_request_lobby_list_string_filter(key, value); + } + if let Some((key, value)) = filter.number { + self.set_request_lobby_list_numerical_filter(key, value); + } + if let Some((key, value)) = filter.near_value { + self.set_request_lobby_list_near_value_filter(key, value); + } + if let Some(distance) = filter.distance { + self.set_request_lobby_list_distance_filter(distance); + } + if let Some(open_slots) = filter.open_slots { + self.set_request_lobby_list_slots_available_filter(open_slots); + } + if let Some(count) = filter.count { + self.set_request_lobby_list_result_count_filter(count); + } + self + } +} + +/// Filters for the lobbies to be returned from `request_lobby_list`. +/// +/// This struct is designed to be used as part of the filtering process +/// when calling the [`set_lobby_list_filter`] method. +/// +/// # Fields +/// +/// - `string`: A string comparison filter that matches lobby attributes with specific strings. +/// - `number`: A number comparison filter that matches lobby attributes with specific integer values. +/// - `near_value`: Specifies a value, and the results will be sorted closest to this value (no actual filtering). +/// - `open_slots`: Filters lobbies based on the number of open slots they have. +/// - `distance`: Filters lobbies based on a distance criterion. +/// - `count`: Specifies the maximum number of lobby results to be returned. +#[derive(Debug, Default, Clone, PartialEq)] +pub struct LobbyListFilter<'a> { + /// A string comparison filter that matches lobby attributes with specific strings. + string: Option<(&'a str, &'a str)>, + /// A number comparison filter that matches lobby attributes with specific integer values + number: Option<(&'a str, i32)>, + /// Specifies a value, and the results will be sorted closest to this value (no actual filtering) + near_value: Option<(&'a str, i32)>, + /// Filters lobbies based on the number of open slots they have + open_slots: Option, + /// Filters lobbies based on a distance criterion + distance: Option, + /// Specifies the maximum number of lobby results to be returned + count: Option, +} + +impl<'a> LobbyListFilter<'a> { + /// Sets the string comparison filter for the lobby list filter. + /// + /// # Arguments + /// + /// * `string`: A tuple containing the attribute name and the target string value to match. + /// + pub fn set_string(&mut self, string: Option<(&'a str, &'a str)>) { + self.string = string; + } + + /// Sets the number comparison filter for the lobby list filter. + /// + /// # Arguments + /// + /// * `number`: A tuple containing the attribute name and the target integer value to match. + /// + pub fn set_number(&mut self, number: Option<(&'a str, i32)>) { + self.number = number; + } + + /// Sets the near value filter for the lobby list filter. + /// + /// # Arguments + /// + /// * `near_value`: A tuple containing the attribute name and the reference integer value. + /// Lobby results will be sorted based on their closeness to this value. + /// + pub fn set_near_value(&mut self, near_value: Option<(&'a str, i32)>) { + self.near_value = near_value; + } + + /// Sets the open slots filter for the lobby list filter. + /// + /// # Arguments + /// + /// * `open_slots`: The number of open slots to filter lobbies by. + /// + pub fn set_open_slots(&mut self, open_slots: Option) { + self.open_slots = open_slots; + } + + /// Sets the distance filter for the lobby list filter. + /// + /// # Arguments + /// + /// * `distance`: A distance filter that specifies a distance criterion for filtering lobbies. + /// + pub fn set_distance(&mut self, distance: Option) { + self.distance = distance; + } + + /// Sets the maximum number of lobby results to be returned. + /// + /// # Arguments + /// + /// * `count`: The maximum number of lobby results to retrieve. + /// + pub fn set_count(&mut self, count: Option) { + self.count = count; + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub enum DistanceFilter { + Close, + #[default] + Default, + Far, + Worldwide, +} + +impl From for sys::ELobbyDistanceFilter { + fn from(filter: DistanceFilter) -> Self { + match filter { + DistanceFilter::Close => sys::ELobbyDistanceFilter::k_ELobbyDistanceFilterClose, + DistanceFilter::Default => sys::ELobbyDistanceFilter::k_ELobbyDistanceFilterDefault, + DistanceFilter::Far => sys::ELobbyDistanceFilter::k_ELobbyDistanceFilterFar, + DistanceFilter::Worldwide => sys::ELobbyDistanceFilter::k_ELobbyDistanceFilterWorldwide, + } + } } /// Flags describing how a users lobby state has changed. This is provided from `LobbyChatUpdate`. From 407430969b52c37631c444591ba3a9c1cdb57920 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Sat, 19 Aug 2023 22:34:43 +0300 Subject: [PATCH 02/10] return &Self to enable use of the builder pattern --- src/matchmaking.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 8d31213..5013280 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -319,7 +319,7 @@ impl Matchmaking { /// * `key`: The attribute key to compare. /// * `value`: The value to compare against. /// - pub fn set_request_lobby_list_string_filter(&self, key: &str, value: &str) { + pub fn set_request_lobby_list_string_filter(&self, key: &str, value: &str) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListStringFilter( self.mm, @@ -328,6 +328,7 @@ impl Matchmaking { sys::ELobbyComparison::k_ELobbyComparisonEqual, ); } + self } /// Adds a numerical comparison filter to the lobby list request. /// @@ -339,7 +340,7 @@ impl Matchmaking { /// * `key`: The attribute key to compare. /// * `value`: The value to compare against. /// - pub fn set_request_lobby_list_numerical_filter(&self, key: &str, value: i32) { + pub fn set_request_lobby_list_numerical_filter(&self, key: &str, value: i32) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListNumericalFilter( self.mm, @@ -348,6 +349,7 @@ impl Matchmaking { sys::ELobbyComparison::k_ELobbyComparisonEqual, ); } + self } /// Adds a near value filter to the lobby list request. /// @@ -359,7 +361,7 @@ impl Matchmaking { /// * `key`: The attribute key to use for sorting. /// * `value`: The reference value for sorting. /// - pub fn set_request_lobby_list_near_value_filter(&self, key: &str, value: i32) { + pub fn set_request_lobby_list_near_value_filter(&self, key: &str, value: i32) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListNearValueFilter( self.mm, @@ -367,6 +369,7 @@ impl Matchmaking { value, ); } + self } /// Adds a filter for available open slots to the lobby list request. /// @@ -376,13 +379,14 @@ impl Matchmaking { /// /// * `open_slots`: The number of open slots in a lobby to filter by. /// - pub fn set_request_lobby_list_slots_available_filter(&self, open_slots: u8) { + pub fn set_request_lobby_list_slots_available_filter(&self, open_slots: u8) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListFilterSlotsAvailable( self.mm, open_slots as i32, ); } + self } /// Adds a distance filter to the lobby list request. /// @@ -392,13 +396,14 @@ impl Matchmaking { /// /// * `distance`: The `DistanceFilter` indicating the distance criterion for the filter. /// - pub fn set_request_lobby_list_distance_filter(&self, distance: DistanceFilter) { + pub fn set_request_lobby_list_distance_filter(&self, distance: DistanceFilter) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListDistanceFilter( self.mm, distance.into(), ); } + self } /// Adds a result count filter to the lobby list request. /// @@ -408,13 +413,14 @@ impl Matchmaking { /// /// * `count`: The maximum number of lobby results to include in the response. /// - pub fn set_request_lobby_list_result_count_filter(&self, count: u64) { + pub fn set_request_lobby_list_result_count_filter(&self, count: u64) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListResultCountFilter( self.mm, count as i32, ); } + self } /// Sets filters for the lobbies to be returned from [`request_lobby_list`]. From 7b2c0f90b7c6ec7ac9a37a1cd723f12c6769e429 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Sat, 19 Aug 2023 22:50:09 +0300 Subject: [PATCH 03/10] added serde support --- src/matchmaking.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 5013280..8c3e845 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -489,6 +489,7 @@ impl Matchmaking { /// - `distance`: Filters lobbies based on a distance criterion. /// - `count`: Specifies the maximum number of lobby results to be returned. #[derive(Debug, Default, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct LobbyListFilter<'a> { /// A string comparison filter that matches lobby attributes with specific strings. string: Option<(&'a str, &'a str)>, @@ -568,6 +569,7 @@ impl<'a> LobbyListFilter<'a> { } #[derive(Debug, Clone, Copy, PartialEq, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum DistanceFilter { Close, #[default] From fba2784ccad8a56aba677b53533eccc7790ed38b Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Sun, 20 Aug 2023 23:32:24 +0300 Subject: [PATCH 04/10] fix lifetime issues --- src/matchmaking.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 8c3e845..ad23a3f 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -492,10 +492,13 @@ impl Matchmaking { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct LobbyListFilter<'a> { /// A string comparison filter that matches lobby attributes with specific strings. + #[cfg_attr(feature = "serde", serde(borrow))] string: Option<(&'a str, &'a str)>, /// A number comparison filter that matches lobby attributes with specific integer values + #[cfg_attr(feature = "serde", serde(borrow))] number: Option<(&'a str, i32)>, /// Specifies a value, and the results will be sorted closest to this value (no actual filtering) + #[cfg_attr(feature = "serde", serde(borrow))] near_value: Option<(&'a str, i32)>, /// Filters lobbies based on the number of open slots they have open_slots: Option, From 8892c4a33279e95098d8b675d5114332bf0d3d30 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Sun, 20 Aug 2023 23:33:49 +0300 Subject: [PATCH 05/10] fix struct field visibility --- src/matchmaking.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index ad23a3f..7da44c6 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -493,19 +493,19 @@ impl Matchmaking { pub struct LobbyListFilter<'a> { /// A string comparison filter that matches lobby attributes with specific strings. #[cfg_attr(feature = "serde", serde(borrow))] - string: Option<(&'a str, &'a str)>, + pub string: Option<(&'a str, &'a str)>, /// A number comparison filter that matches lobby attributes with specific integer values #[cfg_attr(feature = "serde", serde(borrow))] - number: Option<(&'a str, i32)>, + pub number: Option<(&'a str, i32)>, /// Specifies a value, and the results will be sorted closest to this value (no actual filtering) #[cfg_attr(feature = "serde", serde(borrow))] - near_value: Option<(&'a str, i32)>, + pub near_value: Option<(&'a str, i32)>, /// Filters lobbies based on the number of open slots they have - open_slots: Option, + pub open_slots: Option, /// Filters lobbies based on a distance criterion - distance: Option, + pub distance: Option, /// Specifies the maximum number of lobby results to be returned - count: Option, + pub count: Option, } impl<'a> LobbyListFilter<'a> { From dc655cae02de6d46c1337cb24ac6fba4f058ce07 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Mon, 21 Aug 2023 15:35:51 +0300 Subject: [PATCH 06/10] add multiple key value pair filters support --- src/matchmaking.rs | 181 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 153 insertions(+), 28 deletions(-) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 7da44c6..21a2270 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -319,7 +319,10 @@ impl Matchmaking { /// * `key`: The attribute key to compare. /// * `value`: The value to compare against. /// - pub fn set_request_lobby_list_string_filter(&self, key: &str, value: &str) -> &Self { + pub fn set_request_lobby_list_string_filter( + &self, + StringFilter(key, value): StringFilter, + ) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListStringFilter( self.mm, @@ -340,13 +343,16 @@ impl Matchmaking { /// * `key`: The attribute key to compare. /// * `value`: The value to compare against. /// - pub fn set_request_lobby_list_numerical_filter(&self, key: &str, value: i32) -> &Self { + pub fn set_request_lobby_list_numerical_filter( + &self, + NumberFilter(key, value, comparison): NumberFilter, + ) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListNumericalFilter( self.mm, key.as_ptr() as _, value, - sys::ELobbyComparison::k_ELobbyComparisonEqual, + comparison.into(), ); } self @@ -361,7 +367,10 @@ impl Matchmaking { /// * `key`: The attribute key to use for sorting. /// * `value`: The reference value for sorting. /// - pub fn set_request_lobby_list_near_value_filter(&self, key: &str, value: i32) -> &Self { + pub fn set_request_lobby_list_near_value_filter( + &self, + NearFilter(key, value): NearFilter, + ) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListNearValueFilter( self.mm, @@ -444,24 +453,49 @@ impl Matchmaking { /// let (client, single) = Client::init().unwrap(); /// client.matchmaking().set_lobby_list_filter( /// LobbyListFilter { - /// string: Some(("name", "My Lobby")), - /// ..Default::default() - /// } - /// ).request_lobby_list(|lobbies| { + /// string: Some(&[ + /// StringFilter("name", "My Lobby"), + /// StringFilter("gamemode", "ffa"), + /// ]), + /// number: Some(&[ + /// NumberFilter("elo", 1500, ComparisonFilter::GreaterThan), + /// NumberFilter("elo", 2000, ComparisonFilter::LessThan) + /// ]), + /// ..Default::default() + /// } + /// ).request_lobby_list(|lobbies| { /// println!("Lobbies: {:?}", lobbies); /// }); /// } /// ``` - pub fn set_lobby_list_filter(&self, filter: LobbyListFilter) -> &Self { - if let Some((key, value)) = filter.string { - self.set_request_lobby_list_string_filter(key, value); - } - if let Some((key, value)) = filter.number { - self.set_request_lobby_list_numerical_filter(key, value); - } - if let Some((key, value)) = filter.near_value { - self.set_request_lobby_list_near_value_filter(key, value); - } + pub fn set_lobby_list_filter<'a>(&self, filter: LobbyListFilter<'a>) -> &Self { + filter + .string + .into_iter() + .flatten() + .into_iter() + .map(Clone::clone) + .for_each(|str_filter| { + self.set_request_lobby_list_string_filter(str_filter); + }); + filter + .number + .into_iter() + .flatten() + .into_iter() + .map(Clone::clone) + .for_each(|num_filter| { + self.set_request_lobby_list_numerical_filter(num_filter); + }); + filter + .near_value + .into_iter() + .flatten() + .into_iter() + .map(|value| *value) + .for_each(|near_filter| { + self.set_request_lobby_list_near_value_filter(near_filter); + }); if let Some(distance) = filter.distance { self.set_request_lobby_list_distance_filter(distance); } @@ -488,18 +522,18 @@ impl Matchmaking { /// - `open_slots`: Filters lobbies based on the number of open slots they have. /// - `distance`: Filters lobbies based on a distance criterion. /// - `count`: Specifies the maximum number of lobby results to be returned. -#[derive(Debug, Default, Clone, PartialEq)] +#[derive(Debug, Clone, Default, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct LobbyListFilter<'a> { /// A string comparison filter that matches lobby attributes with specific strings. #[cfg_attr(feature = "serde", serde(borrow))] - pub string: Option<(&'a str, &'a str)>, + pub string: Option>, /// A number comparison filter that matches lobby attributes with specific integer values #[cfg_attr(feature = "serde", serde(borrow))] - pub number: Option<(&'a str, i32)>, + pub number: Option>, /// Specifies a value, and the results will be sorted closest to this value (no actual filtering) #[cfg_attr(feature = "serde", serde(borrow))] - pub near_value: Option<(&'a str, i32)>, + pub near_value: Option>, /// Filters lobbies based on the number of open slots they have pub open_slots: Option, /// Filters lobbies based on a distance criterion @@ -508,6 +542,57 @@ pub struct LobbyListFilter<'a> { pub count: Option, } +pub type StringFilters<'a> = &'a [StringFilter<'a>]; +pub type NumberFilters<'a> = &'a [NumberFilter<'a>]; +pub type NearFilters<'a> = &'a [NearFilter<'a>]; + +/// A filter used for string based key value comparisons. +/// +/// # Fields +/// +/// * `0`: The attribute key for comparison. +/// * `1`: The target string value for matching. +#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct StringFilter<'a>(pub &'a str, pub &'a str); + +/// A filter used for numerical attribute comparison in lobby filtering. +/// +/// # Fields +/// +/// * `key`: The attribute key for comparison. +/// * `value`: The target numerical value for matching. +/// * `comparison`: The comparison mode indicating how the numerical values should be compared. +/// +/// # Example +/// +/// ```no_run +/// let elo_filter = NumberFilter { +/// key: "lobby_elo", +/// value: 1500, +/// comparison: ComparisonFilter::GreaterThan, +/// }; +/// ``` +/// +#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct NumberFilter<'a>(pub &'a str, pub i32, pub ComparisonFilter); + +/// A filter used for near-value sorting in lobby filtering. +/// +/// This struct enables sorting the lobby results based on their closeness to a reference value. +/// It includes two fields: the attribute key to use for sorting and the reference numerical value. +/// +/// This filter does not perform actual filtering but rather sorts the results based on proximity. +/// +/// # Fields +/// +/// * `0`: The attribute key to use for sorting. +/// * `1`: The reference numerical value used for sorting proximity. +#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct NearFilter<'a>(pub &'a str, pub i32); + impl<'a> LobbyListFilter<'a> { /// Sets the string comparison filter for the lobby list filter. /// @@ -515,8 +600,9 @@ impl<'a> LobbyListFilter<'a> { /// /// * `string`: A tuple containing the attribute name and the target string value to match. /// - pub fn set_string(&mut self, string: Option<(&'a str, &'a str)>) { + pub fn set_string(mut self, string: Option>) -> Self { self.string = string; + self } /// Sets the number comparison filter for the lobby list filter. @@ -525,8 +611,9 @@ impl<'a> LobbyListFilter<'a> { /// /// * `number`: A tuple containing the attribute name and the target integer value to match. /// - pub fn set_number(&mut self, number: Option<(&'a str, i32)>) { + pub fn set_number(mut self, number: Option>) -> Self { self.number = number; + self } /// Sets the near value filter for the lobby list filter. @@ -536,8 +623,9 @@ impl<'a> LobbyListFilter<'a> { /// * `near_value`: A tuple containing the attribute name and the reference integer value. /// Lobby results will be sorted based on their closeness to this value. /// - pub fn set_near_value(&mut self, near_value: Option<(&'a str, i32)>) { + pub fn set_near_value(mut self, near_value: Option>) -> Self { self.near_value = near_value; + self } /// Sets the open slots filter for the lobby list filter. @@ -546,8 +634,9 @@ impl<'a> LobbyListFilter<'a> { /// /// * `open_slots`: The number of open slots to filter lobbies by. /// - pub fn set_open_slots(&mut self, open_slots: Option) { + pub fn set_open_slots(mut self, open_slots: Option) -> Self { self.open_slots = open_slots; + self } /// Sets the distance filter for the lobby list filter. @@ -556,8 +645,9 @@ impl<'a> LobbyListFilter<'a> { /// /// * `distance`: A distance filter that specifies a distance criterion for filtering lobbies. /// - pub fn set_distance(&mut self, distance: Option) { + pub fn set_distance(mut self, distance: Option) -> Self { self.distance = distance; + self } /// Sets the maximum number of lobby results to be returned. @@ -566,8 +656,9 @@ impl<'a> LobbyListFilter<'a> { /// /// * `count`: The maximum number of lobby results to retrieve. /// - pub fn set_count(&mut self, count: Option) { + pub fn set_count(mut self, count: Option) -> Self { self.count = count; + self } } @@ -592,6 +683,35 @@ impl From for sys::ELobbyDistanceFilter { } } +#[derive(Debug, Clone, Copy, PartialEq, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ComparisonFilter { + #[default] + Equal, + NotEqual, + GreaterThan, + GreaterThanEqualTo, + LessThan, + LessThanEqualTo, +} + +impl From for sys::ELobbyComparison { + fn from(filter: ComparisonFilter) -> Self { + match filter { + ComparisonFilter::Equal => sys::ELobbyComparison::k_ELobbyComparisonEqual, + ComparisonFilter::NotEqual => sys::ELobbyComparison::k_ELobbyComparisonNotEqual, + ComparisonFilter::GreaterThan => sys::ELobbyComparison::k_ELobbyComparisonGreaterThan, + ComparisonFilter::GreaterThanEqualTo => { + sys::ELobbyComparison::k_ELobbyComparisonEqualToOrGreaterThan + } + ComparisonFilter::LessThan => sys::ELobbyComparison::k_ELobbyComparisonLessThan, + ComparisonFilter::LessThanEqualTo => { + sys::ELobbyComparison::k_ELobbyComparisonEqualToOrLessThan + } + } + } +} + /// Flags describing how a users lobby state has changed. This is provided from `LobbyChatUpdate`. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -698,6 +818,11 @@ fn test_lobby() { println!("Create: {:?}", v); }); + mm.set_lobby_list_filter(LobbyListFilter { + string: Some(&[StringFilter("name", "My Lobby")]), + ..Default::default() + }); + for _ in 0..100 { single.run_callbacks(); ::std::thread::sleep(::std::time::Duration::from_millis(100)); From 7f741d9d5cca5dc2123b6b1d48a32bd0fbc67c42 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Mon, 21 Aug 2023 15:39:51 +0300 Subject: [PATCH 07/10] change api function names --- src/matchmaking.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 21a2270..678bd81 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -319,7 +319,7 @@ impl Matchmaking { /// * `key`: The attribute key to compare. /// * `value`: The value to compare against. /// - pub fn set_request_lobby_list_string_filter( + pub fn add_request_lobby_list_string_filter( &self, StringFilter(key, value): StringFilter, ) -> &Self { @@ -343,7 +343,7 @@ impl Matchmaking { /// * `key`: The attribute key to compare. /// * `value`: The value to compare against. /// - pub fn set_request_lobby_list_numerical_filter( + pub fn add_request_lobby_list_numerical_filter( &self, NumberFilter(key, value, comparison): NumberFilter, ) -> &Self { @@ -367,7 +367,7 @@ impl Matchmaking { /// * `key`: The attribute key to use for sorting. /// * `value`: The reference value for sorting. /// - pub fn set_request_lobby_list_near_value_filter( + pub fn add_request_lobby_list_near_value_filter( &self, NearFilter(key, value): NearFilter, ) -> &Self { @@ -476,7 +476,7 @@ impl Matchmaking { .into_iter() .map(Clone::clone) .for_each(|str_filter| { - self.set_request_lobby_list_string_filter(str_filter); + self.add_request_lobby_list_string_filter(str_filter); }); filter .number @@ -485,7 +485,7 @@ impl Matchmaking { .into_iter() .map(Clone::clone) .for_each(|num_filter| { - self.set_request_lobby_list_numerical_filter(num_filter); + self.add_request_lobby_list_numerical_filter(num_filter); }); filter .near_value @@ -494,7 +494,7 @@ impl Matchmaking { .into_iter() .map(|value| *value) .for_each(|near_filter| { - self.set_request_lobby_list_near_value_filter(near_filter); + self.add_request_lobby_list_near_value_filter(near_filter); }); if let Some(distance) = filter.distance { self.set_request_lobby_list_distance_filter(distance); From 75bf0540c1a22cc39a593641e3c418b73ba5b46b Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Mon, 21 Aug 2023 15:43:18 +0300 Subject: [PATCH 08/10] add string exclude filter --- src/matchmaking.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 678bd81..3ca686a 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -321,14 +321,14 @@ impl Matchmaking { /// pub fn add_request_lobby_list_string_filter( &self, - StringFilter(key, value): StringFilter, + StringFilter(key, value, kind): StringFilter, ) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListStringFilter( self.mm, key.as_ptr() as _, value.as_ptr() as _, - sys::ELobbyComparison::k_ELobbyComparisonEqual, + kind.into(), ); } self @@ -454,8 +454,8 @@ impl Matchmaking { /// client.matchmaking().set_lobby_list_filter( /// LobbyListFilter { /// string: Some(&[ - /// StringFilter("name", "My Lobby"), - /// StringFilter("gamemode", "ffa"), + /// StringFilter("name", "My Lobby", StringFilterKind::Include), + /// StringFilter("gamemode", "ffa", StringFilterKind::Include), /// ]), /// number: Some(&[ /// NumberFilter("elo", 1500, ComparisonFilter::GreaterThan), @@ -554,7 +554,23 @@ pub type NearFilters<'a> = &'a [NearFilter<'a>]; /// * `1`: The target string value for matching. #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct StringFilter<'a>(pub &'a str, pub &'a str); +pub struct StringFilter<'a>(pub &'a str, pub &'a str, pub StringFilterKind); + +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub enum StringFilterKind { + #[default] + Include, + Exclude, +} + +impl From for sys::ELobbyComparison { + fn from(filter: StringFilterKind) -> Self { + match filter { + StringFilterKind::Include => sys::ELobbyComparison::k_ELobbyComparisonEqual, + StringFilterKind::Exclude => sys::ELobbyComparison::k_ELobbyComparisonNotEqual, + } + } +} /// A filter used for numerical attribute comparison in lobby filtering. /// @@ -819,7 +835,7 @@ fn test_lobby() { }); mm.set_lobby_list_filter(LobbyListFilter { - string: Some(&[StringFilter("name", "My Lobby")]), + string: Some(&[StringFilter("name", "My Lobby", StringFilterKind::Include)]), ..Default::default() }); From 3fe9476d22e243c0093873615f97a9e57b295388 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Mon, 21 Aug 2023 16:02:31 +0300 Subject: [PATCH 09/10] add lobby key length validation --- src/matchmaking.rs | 94 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 10 deletions(-) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 3ca686a..40f4145 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use super::*; #[cfg(test)] use serial_test::serial; @@ -321,7 +323,7 @@ impl Matchmaking { /// pub fn add_request_lobby_list_string_filter( &self, - StringFilter(key, value, kind): StringFilter, + StringFilter(LobbyKey(key), value, kind): StringFilter, ) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListStringFilter( @@ -345,7 +347,7 @@ impl Matchmaking { /// pub fn add_request_lobby_list_numerical_filter( &self, - NumberFilter(key, value, comparison): NumberFilter, + NumberFilter(LobbyKey(key), value, comparison): NumberFilter, ) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListNumericalFilter( @@ -369,7 +371,7 @@ impl Matchmaking { /// pub fn add_request_lobby_list_near_value_filter( &self, - NearFilter(key, value): NearFilter, + NearFilter(LobbyKey(key), value): NearFilter, ) -> &Self { unsafe { sys::SteamAPI_ISteamMatchmaking_AddRequestLobbyListNearValueFilter( @@ -454,8 +456,12 @@ impl Matchmaking { /// client.matchmaking().set_lobby_list_filter( /// LobbyListFilter { /// string: Some(&[ - /// StringFilter("name", "My Lobby", StringFilterKind::Include), - /// StringFilter("gamemode", "ffa", StringFilterKind::Include), + /// StringFilter( + /// LobbyKey::new("name"), "My Lobby", StringFilterKind::Include + /// ), + /// StringFilter( + /// LobbyKey::new("gamemode"), "ffa", StringFilterKind::Include + /// ), /// ]), /// number: Some(&[ /// NumberFilter("elo", 1500, ComparisonFilter::GreaterThan), @@ -542,6 +548,69 @@ pub struct LobbyListFilter<'a> { pub count: Option, } +/// A wrapper for a lobby key string. +/// +/// This struct provides a wrapper for a lobby key string. It is used to validate +/// constructed keys and to ensure that they do not exceed the maximum allowed length. +#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct LobbyKey<'a>(pub(crate) &'a str); + +impl<'a> std::ops::Deref for LobbyKey<'a> { + type Target = &'a str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Default, Error)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct LobbyKeyTooLongError; + +impl Display for LobbyKeyTooLongError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "Lobby key is greater than {} characters", + sys::k_nMaxLobbyKeyLength + ) + } +} + +impl<'a> LobbyKey<'a> { + /// Attempts to create a new `LobbyKey` from a provided string key. + /// + /// # Arguments + /// + /// * `key`: The string key to create a `LobbyKey` from. + /// + /// # Errors + /// + /// This function will return an error of type [`LobbyKeyTooLongError`] if the provided key's length + /// exceeds k_nMaxLobbyKeyLength (255 characters). + pub fn try_new(key: &'a str) -> Result { + if key.len() > sys::k_nMaxLobbyKeyLength as usize { + Err(LobbyKeyTooLongError) + } else { + Ok(LobbyKey(key)) + } + } + /// Creates a new `LobbyKey` from a provided string key. + /// + /// # Arguments + /// + /// * `key`: The string key to create a `LobbyKey` from. + /// + /// # Panics + /// + /// This function will panic if the provided key's length exceeds 255 characters. + /// ``` + pub fn new(key: &'a str) -> Self { + Self::try_new(key).unwrap() + } +} + pub type StringFilters<'a> = &'a [StringFilter<'a>]; pub type NumberFilters<'a> = &'a [NumberFilter<'a>]; pub type NearFilters<'a> = &'a [NearFilter<'a>]; @@ -554,9 +623,10 @@ pub type NearFilters<'a> = &'a [NearFilter<'a>]; /// * `1`: The target string value for matching. #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct StringFilter<'a>(pub &'a str, pub &'a str, pub StringFilterKind); +pub struct StringFilter<'a>(pub LobbyKey<'a>, pub &'a str, pub StringFilterKind); -#[derive(Debug, Clone, Copy, PartialEq, Default)] +#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum StringFilterKind { #[default] Include, @@ -592,7 +662,7 @@ impl From for sys::ELobbyComparison { /// #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct NumberFilter<'a>(pub &'a str, pub i32, pub ComparisonFilter); +pub struct NumberFilter<'a>(pub LobbyKey<'a>, pub i32, pub ComparisonFilter); /// A filter used for near-value sorting in lobby filtering. /// @@ -607,7 +677,7 @@ pub struct NumberFilter<'a>(pub &'a str, pub i32, pub ComparisonFilter); /// * `1`: The reference numerical value used for sorting proximity. #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct NearFilter<'a>(pub &'a str, pub i32); +pub struct NearFilter<'a>(pub LobbyKey<'a>, pub i32); impl<'a> LobbyListFilter<'a> { /// Sets the string comparison filter for the lobby list filter. @@ -835,7 +905,11 @@ fn test_lobby() { }); mm.set_lobby_list_filter(LobbyListFilter { - string: Some(&[StringFilter("name", "My Lobby", StringFilterKind::Include)]), + string: Some(&[StringFilter( + LobbyKey::new("name"), + "My Lobby", + StringFilterKind::Include, + )]), ..Default::default() }); From 430ea98fe1bc1c2a9fc01e4113b6d4b1e351da53 Mon Sep 17 00:00:00 2001 From: wyvernbw Date: Wed, 23 Aug 2023 23:59:08 +0300 Subject: [PATCH 10/10] fix serde lifetime errors --- src/matchmaking.rs | 59 ++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 40f4145..199088c 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -455,7 +455,7 @@ impl Matchmaking { /// let (client, single) = Client::init().unwrap(); /// client.matchmaking().set_lobby_list_filter( /// LobbyListFilter { - /// string: Some(&[ + /// string: Some(vec![ /// StringFilter( /// LobbyKey::new("name"), "My Lobby", StringFilterKind::Include /// ), @@ -463,7 +463,7 @@ impl Matchmaking { /// LobbyKey::new("gamemode"), "ffa", StringFilterKind::Include /// ), /// ]), - /// number: Some(&[ + /// number: Some(vec![ /// NumberFilter("elo", 1500, ComparisonFilter::GreaterThan), /// NumberFilter("elo", 2000, ComparisonFilter::LessThan) /// ]), @@ -474,31 +474,17 @@ impl Matchmaking { /// }); /// } /// ``` - pub fn set_lobby_list_filter<'a>(&self, filter: LobbyListFilter<'a>) -> &Self { - filter - .string - .into_iter() - .flatten() - .into_iter() - .map(Clone::clone) - .for_each(|str_filter| { - self.add_request_lobby_list_string_filter(str_filter); - }); - filter - .number - .into_iter() - .flatten() - .into_iter() - .map(Clone::clone) - .for_each(|num_filter| { - self.add_request_lobby_list_numerical_filter(num_filter); - }); + pub fn set_lobby_list_filter(&self, filter: LobbyListFilter<'_>) -> &Self { + filter.string.into_iter().flatten().for_each(|str_filter| { + self.add_request_lobby_list_string_filter(str_filter); + }); + filter.number.into_iter().flatten().for_each(|num_filter| { + self.add_request_lobby_list_numerical_filter(num_filter); + }); filter .near_value .into_iter() .flatten() - .into_iter() - .map(|value| *value) .for_each(|near_filter| { self.add_request_lobby_list_near_value_filter(near_filter); }); @@ -532,7 +518,7 @@ impl Matchmaking { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct LobbyListFilter<'a> { /// A string comparison filter that matches lobby attributes with specific strings. - #[cfg_attr(feature = "serde", serde(borrow))] + //#[cfg_attr(feature = "serde", serde(borrow))] pub string: Option>, /// A number comparison filter that matches lobby attributes with specific integer values #[cfg_attr(feature = "serde", serde(borrow))] @@ -611,9 +597,9 @@ impl<'a> LobbyKey<'a> { } } -pub type StringFilters<'a> = &'a [StringFilter<'a>]; -pub type NumberFilters<'a> = &'a [NumberFilter<'a>]; -pub type NearFilters<'a> = &'a [NearFilter<'a>]; +pub type StringFilters<'a> = Vec>; +pub type NumberFilters<'a> = Vec>; +pub type NearFilters<'a> = Vec>; /// A filter used for string based key value comparisons. /// @@ -623,7 +609,11 @@ pub type NearFilters<'a> = &'a [NearFilter<'a>]; /// * `1`: The target string value for matching. #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct StringFilter<'a>(pub LobbyKey<'a>, pub &'a str, pub StringFilterKind); +pub struct StringFilter<'a>( + #[cfg_attr(feature = "serde", serde(borrow))] pub LobbyKey<'a>, + pub &'a str, + pub StringFilterKind, +); #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -662,7 +652,11 @@ impl From for sys::ELobbyComparison { /// #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct NumberFilter<'a>(pub LobbyKey<'a>, pub i32, pub ComparisonFilter); +pub struct NumberFilter<'a>( + #[cfg_attr(feature = "serde", serde(borrow))] pub LobbyKey<'a>, + pub i32, + pub ComparisonFilter, +); /// A filter used for near-value sorting in lobby filtering. /// @@ -677,7 +671,10 @@ pub struct NumberFilter<'a>(pub LobbyKey<'a>, pub i32, pub ComparisonFilter); /// * `1`: The reference numerical value used for sorting proximity. #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct NearFilter<'a>(pub LobbyKey<'a>, pub i32); +pub struct NearFilter<'a>( + #[cfg_attr(feature = "serde", serde(borrow))] pub LobbyKey<'a>, + pub i32, +); impl<'a> LobbyListFilter<'a> { /// Sets the string comparison filter for the lobby list filter. @@ -905,7 +902,7 @@ fn test_lobby() { }); mm.set_lobby_list_filter(LobbyListFilter { - string: Some(&[StringFilter( + string: Some(vec![StringFilter( LobbyKey::new("name"), "My Lobby", StringFilterKind::Include,