diff --git a/src/inventory.rs b/src/inventory.rs new file mode 100644 index 0000000..c2ca2f6 --- /dev/null +++ b/src/inventory.rs @@ -0,0 +1,176 @@ +use super::*; +use std::sync::Arc; +use crate::sys; + +/// Represents the result of an inventory operation, ready to be processed. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SteamInventoryResultReady { + pub handle: sys::SteamInventoryResult_t, + pub result: Result<(), SteamError>, +} + +unsafe impl Callback for SteamInventoryResultReady { + const ID: i32 = sys::SteamInventoryResultReady_t_k_iCallback as i32; + const SIZE: i32 = std::mem::size_of::() as i32; + + unsafe fn from_raw(raw: *mut c_void) -> Self { + let status = &*(raw as *mut sys::SteamInventoryResultReady_t); + Self { + handle: status.m_handle, + result: match status.m_result { + sys::EResult::k_EResultOK => Ok(()), + _ => Err(SteamError::from(status.m_result)), + }, + } + } +} + +/// Represents a full update event for the Steam inventory. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SteamInventoryFullUpdate { + pub handle: sys::SteamInventoryResult_t, +} + +unsafe impl Callback for SteamInventoryFullUpdate { + const ID: i32 = sys::SteamInventoryFullUpdate_t_k_iCallback as i32; + const SIZE: i32 = std::mem::size_of::() as i32; + + unsafe fn from_raw(raw: *mut c_void) -> Self { + let status = &*(raw as *mut sys::SteamInventoryFullUpdate_t); + Self { + handle: status.m_handle, + } + } +} + +/// Provides access to the Steam inventory interface. +pub struct Inventory { + pub(crate) inventory: *mut sys::ISteamInventory, + pub(crate) _inner: Arc>, +} + +impl Inventory { + /// Retrieves all items in the user's Steam inventory. + pub fn get_all_items(&self) -> Result { + let mut result_handle = sys::k_SteamInventoryResultInvalid; + unsafe { + if sys::SteamAPI_ISteamInventory_GetAllItems(self.inventory, &mut result_handle) { + Ok(result_handle) + } else { + Err(InventoryError::OperationFailed) + } + } + } + + /// Retrieves the detailed list of items from the inventory given a result handle. + pub fn get_result_items(&self, result_handle: sys::SteamInventoryResult_t) -> Result, InventoryError> { + let mut items_count = 0; + unsafe { + if !sys::SteamAPI_ISteamInventory_GetResultItems( + self.inventory, + result_handle, + std::ptr::null_mut(), + &mut items_count, + ) { + return Err(InventoryError::GetResultItemsFailed); + } + + let mut items_array: Vec = Vec::with_capacity(items_count as usize); + if sys::SteamAPI_ISteamInventory_GetResultItems( + self.inventory, + result_handle, + items_array.as_mut_ptr(), + &mut items_count, + ) { + items_array.set_len(items_count as usize); + let items = items_array.into_iter().map(|details| SteamItemDetails { + item_id: SteamItemInstanceID(details.m_itemId), + definition: SteamItemDef(details.m_iDefinition), + quantity: details.m_unQuantity, + flags: details.m_unFlags, + }).collect(); + Ok(items) + } else { + Err(InventoryError::GetResultItemsFailed) + } + } + } + + /// Destroy a result handle after use. + pub fn destroy_result(&self, result_handle: sys::SteamInventoryResult_t) { + unsafe { + sys::SteamAPI_ISteamInventory_DestroyResult( + self.inventory, + result_handle, + ); + } + } +} + +/// Represents an individual inventory item with its unique details. +#[derive(Clone, Debug)] +pub struct SteamItemDetails { + pub item_id: SteamItemInstanceID, + pub definition: SteamItemDef, + pub quantity: u16, + pub flags: u16, +} + +/// Represents a unique identifier for an inventory item instance. +#[derive(Clone, Debug)] +pub struct SteamItemInstanceID(pub u64); + +/// Represents a unique identifier for an item definition. +#[derive(Clone, Debug)] +pub struct SteamItemDef(pub i32); + +/// Enumerates possible errors that can occur during inventory operations. +#[derive(Debug, Error)] +pub enum InventoryError { + #[error("The inventory operation failed")] + OperationFailed, + #[error("Failed to retrieve result items")] + GetResultItemsFailed, + #[error("Invalid input")] + InvalidInput, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::mpsc; + + #[test] + fn test_get_result_items() { + let client = Client::init().unwrap(); + let (tx, rx) = mpsc::channel::(); + + client.register_callback(move |val: SteamInventoryResultReady| { + assert!(val.result.is_ok(), "SteamInventoryResultReady Failed."); + if let Ok(_) = val.result { + tx.send(val.handle).expect("Failed to send handle"); + } + }); + + client.register_callback(move |val: SteamInventoryFullUpdate| { + println!("SteamInventoryFullUpdate: {:?}", val) + }); + + let _result = client.inventory().get_all_items(); + + for _ in 0..50 { + client.run_callbacks(); + ::std::thread::sleep(::std::time::Duration::from_millis(100)); + if let Ok(handle) = rx.try_recv() { + let result_items = client.inventory().get_result_items(handle).unwrap(); + assert!(!result_items.is_empty(), "No items received"); + println!("Result items: {:?}", result_items); + client.inventory().destroy_result(handle); + return; + } + } + panic!("Timed out waiting for inventory result."); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 6cceadb..163b0ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,7 @@ pub use crate::ugc::*; pub use crate::user::*; pub use crate::user_stats::*; pub use crate::utils::*; +pub use crate::inventory::*; mod app; mod callback; @@ -61,6 +62,7 @@ mod ugc; mod user; mod user_stats; mod utils; +mod inventory; pub type SResult = Result; @@ -441,6 +443,18 @@ where } } + /// Returns an accessor to the steam inventory interface + pub fn inventory(&self) -> Inventory { + unsafe { + let inventory = sys::SteamAPI_SteamInventory_v003(); + debug_assert!(!inventory.is_null()); + Inventory { + inventory, + _inner: self.inner.clone(), + } + } + } + pub fn networking_messages(&self) -> networking_messages::NetworkingMessages { unsafe { let net = sys::SteamAPI_SteamNetworkingMessages_SteamAPI_v002();