diff options
| author | Matthew Collins <[email protected]> | 2018-08-28 01:13:39 +0100 |
|---|---|---|
| committer | Matthew Collins <[email protected]> | 2018-08-28 01:13:39 +0100 |
| commit | 9974fb675c5ed70327124e515a0e118695030a53 (patch) | |
| tree | 53fed215dde3c8d2583e05f98ba5dea3957b242a /src | |
| parent | Version 0.4.1 (diff) | |
| download | steamworks-rs-9974fb675c5ed70327124e515a0e118695030a53.tar.xz steamworks-rs-9974fb675c5ed70327124e515a0e118695030a53.zip | |
Add basic leaderboard handling (Work on #4)
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 13 | ||||
| -rw-r--r-- | src/user_stats.rs | 214 |
2 files changed, 227 insertions, 0 deletions
@@ -25,6 +25,8 @@ mod networking; pub use networking::*; mod user; pub use user::*; +mod user_stats; +pub use user_stats::*; use std::sync::{ Arc, Mutex }; use std::ffi::{CString, CStr}; @@ -242,7 +244,18 @@ impl <Manager> Client<Manager> { _inner: self.inner.clone(), } } + } + /// Returns an accessor to the steam user stats interface + pub fn user_stats(&self) -> UserStats<Manager> { + unsafe { + let us = sys::steam_rust_get_user_stats(); + debug_assert!(!us.is_null()); + UserStats { + user_stats: us, + inner: self.inner.clone(), + } + } } } diff --git a/src/user_stats.rs b/src/user_stats.rs new file mode 100644 index 0000000..33b92a1 --- /dev/null +++ b/src/user_stats.rs @@ -0,0 +1,214 @@ + +use super::*; + +/// Access to the steam user interface +pub struct UserStats<Manager> { + pub(crate) user_stats: *mut sys::ISteamUserStats, + pub(crate) inner: Arc<Inner<Manager>>, +} + +const CALLBACK_BASE_ID: i32 = 1100; + +impl <Manager> UserStats<Manager> { + + pub fn find_leaderboard<F>(&self, name: &str, mut cb: F) + where F: FnMut(Result<Option<Leaderboard>, SteamError>) + 'static + Send + Sync + { + unsafe { + let name = CString::new(name).unwrap(); + let api_call = sys::SteamAPI_ISteamUserStats_FindLeaderboard(self.user_stats, name.as_ptr() as *const _); + register_call_result::<sys::LeaderboardFindResult_t, _, _>( + &self.inner, api_call, CALLBACK_BASE_ID + 4, + move |v, io_error| { + cb(if io_error { + Err(SteamError::IOFailure) + } else { + Ok(if v.get_m_bLeaderboardFound() != 0 { + Some(Leaderboard(v.get_m_hSteamLeaderboard())) + } else { + None + }) + }) + }); + } + } + + pub fn find_or_create_leaderboard<F>(&self, name: &str, sort_method: LeaderboardSortMethod, display_type: LeaderboardDisplayType, mut cb: F) + where F: FnMut(Result<Option<Leaderboard>, SteamError>) + 'static + Send + Sync + { + unsafe { + let name = CString::new(name).unwrap(); + + let sort_method = match sort_method { + LeaderboardSortMethod::Ascending => sys::ELeaderboardSortMethod_k_ELeaderboardSortMethodAscending, + LeaderboardSortMethod::Descending => sys::ELeaderboardSortMethod_k_ELeaderboardSortMethodDescending, + }; + + let display_type = match display_type { + LeaderboardDisplayType::Numeric => sys::ELeaderboardDisplayType_k_ELeaderboardDisplayTypeNumeric, + LeaderboardDisplayType::TimeSeconds => sys::ELeaderboardDisplayType_k_ELeaderboardDisplayTypeTimeSeconds, + LeaderboardDisplayType::TimeMilliSeconds => sys::ELeaderboardDisplayType_k_ELeaderboardDisplayTypeTimeMilliSeconds, + }; + + let api_call = sys::SteamAPI_ISteamUserStats_FindOrCreateLeaderboard(self.user_stats, name.as_ptr() as *const _, sort_method, display_type); + register_call_result::<sys::LeaderboardFindResult_t, _, _>( + &self.inner, api_call, CALLBACK_BASE_ID + 4, + move |v, io_error| { + cb(if io_error { + Err(SteamError::IOFailure) + } else { + Ok(if v.get_m_bLeaderboardFound() != 0 { + Some(Leaderboard(v.get_m_hSteamLeaderboard())) + } else { + None + }) + }) + }); + } + } + + pub fn upload_leaderboard_score<F>(&self, leaderboard: &Leaderboard, method: UploadScoreMethod, score: i32, details: &[i32], mut cb: F) + where F: FnMut(Result<Option<LeaderboardScoreUploaded>, SteamError>) + 'static + Send + Sync + { + unsafe { + let method = match method { + UploadScoreMethod::KeepBest => sys::ELeaderboardUploadScoreMethod_k_ELeaderboardUploadScoreMethodKeepBest, + UploadScoreMethod::ForceUpdate => sys::ELeaderboardUploadScoreMethod_k_ELeaderboardUploadScoreMethodForceUpdate, + }; + let api_call = sys::SteamAPI_ISteamUserStats_UploadLeaderboardScore(self.user_stats, leaderboard.0, method, score, details.as_ptr(), details.len() as _); + register_call_result::<sys::LeaderboardScoreUploaded_t , _, _>( + &self.inner, api_call, CALLBACK_BASE_ID + 6, + move |v, io_error| { + cb(if io_error { + Err(SteamError::IOFailure) + } else { + Ok(if v.get_m_bSuccess() != 0 { + Some(LeaderboardScoreUploaded { + score: v.get_m_nScore(), + was_changed: v.get_m_bScoreChanged() != 0, + global_rank_new: v.get_m_nGlobalRankNew() as _, + global_rank_previous: v.get_m_nGlobalRankPrevious() as _, + }) + } else { + None + }) + }) + }); + } + } + + pub fn download_leaderboard_entries<F>( + &self, + leaderboard: &Leaderboard, + request: LeaderboardDataRequest, start: usize, end: usize, + max_details_len: usize, + mut cb: F + ) + where F: FnMut(Result<Vec<LeaderboardEntry>, SteamError>) + 'static + Send + Sync + { + unsafe { + let request = match request { + LeaderboardDataRequest::Global => sys::ELeaderboardDataRequest_k_ELeaderboardDataRequestGlobal, + LeaderboardDataRequest::GlobalAroundUser => sys::ELeaderboardDataRequest_k_ELeaderboardDataRequestGlobalAroundUser, + LeaderboardDataRequest::Friends => sys::ELeaderboardDataRequest_k_ELeaderboardDataRequestFriends, + }; + let api_call = sys::SteamAPI_ISteamUserStats_DownloadLeaderboardEntries(self.user_stats, leaderboard.0, request, start as _, end as _); + let user_stats = self.user_stats as isize; + register_call_result::<sys::LeaderboardScoresDownloaded_t , _, _>( + &self.inner, api_call, CALLBACK_BASE_ID + 5, + move |v, io_error| { + cb(if io_error { + Err(SteamError::IOFailure) + } else { + let len = v.get_m_cEntryCount(); + let mut entries = Vec::with_capacity(len as usize); + for idx in 0 .. len { + let mut entry = sys::create_empty_LeaderboardEntry_t(); + let mut details = Vec::with_capacity(max_details_len); + + sys::SteamAPI_ISteamUserStats_GetDownloadedLeaderboardEntry(user_stats as *mut _, v.get_m_hSteamLeaderboardEntries(), idx, &mut entry, details.as_mut_ptr(), max_details_len as _); + details.set_len(entry.get_m_cDetails() as usize); + + entries.push(LeaderboardEntry { + user: SteamId(entry.get_m_steamIDUser()), + global_rank: entry.get_m_nGlobalRank(), + score: entry.get_m_nScore(), + details, + }) + } + Ok(entries) + }) + }); + } + } +} + +#[derive(Debug)] +pub struct LeaderboardEntry { + pub user: SteamId, + pub global_rank: i32, + pub score: i32, + pub details: Vec<i32>, +} + +pub enum LeaderboardDataRequest { + Global, + GlobalAroundUser, + Friends, +} + +#[derive(Debug)] +pub struct LeaderboardScoreUploaded { + pub score: i32, + pub was_changed: bool, + pub global_rank_new: i32, + pub global_rank_previous: i32, +} + +pub enum UploadScoreMethod { + KeepBest, + ForceUpdate, +} + +pub enum LeaderboardSortMethod { + Ascending, + Descending, +} + +pub enum LeaderboardDisplayType { + Numeric, + TimeSeconds, + TimeMilliSeconds, +} + +#[derive(Debug)] +pub struct Leaderboard(u64); + +#[test] +fn test() { + let (client, single) = Client::init().unwrap(); + + let stats = client.user_stats(); + + stats.find_leaderboard("steamworks_test", |lb| { + println!("Got: {:?}", lb); + }); + let c2 = client.clone(); + stats.find_or_create_leaderboard("steamworks_test_created", LeaderboardSortMethod::Descending, LeaderboardDisplayType::TimeMilliSeconds, move |lb| { + println!("Got: {:?}", lb); + + if let Some(lb) = lb.ok().and_then(|v| v) { + c2.user_stats().upload_leaderboard_score(&lb, UploadScoreMethod::ForceUpdate, 1337, &[1, 2, 3, 4], |v| { + println!("Upload: {:?}", v); + }); + c2.user_stats().download_leaderboard_entries(&lb, LeaderboardDataRequest::Global, 0, 200, 10, |v| { + println!("Download: {:?}", v); + }); + } + }); + + for _ in 0 .. 50 { + single.run_callbacks(); + ::std::thread::sleep(::std::time::Duration::from_millis(100)); + } +}
\ No newline at end of file |