aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthew Collins <[email protected]>2018-08-28 01:13:39 +0100
committerMatthew Collins <[email protected]>2018-08-28 01:13:39 +0100
commit9974fb675c5ed70327124e515a0e118695030a53 (patch)
tree53fed215dde3c8d2583e05f98ba5dea3957b242a /src
parentVersion 0.4.1 (diff)
downloadsteamworks-rs-9974fb675c5ed70327124e515a0e118695030a53.tar.xz
steamworks-rs-9974fb675c5ed70327124e515a0e118695030a53.zip
Add basic leaderboard handling (Work on #4)
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs13
-rw-r--r--src/user_stats.rs214
2 files changed, 227 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 00f8f08..d651f3b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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