aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlex Butler <[email protected]>2019-05-16 11:59:20 +0100
committerMatthew Collins <[email protected]>2019-05-16 17:35:04 +0100
commit7d2ed5ea57f3ea769e659d8c80fd52d60624553e (patch)
tree3156afb74c6f2249bd1beb37cb9761b1c40b723e /src
parentSkip fields with missing enum types (diff)
downloadsteamworks-rs-7d2ed5ea57f3ea769e659d8c80fd52d60624553e.tar.xz
steamworks-rs-7d2ed5ea57f3ea769e659d8c80fd52d60624553e.zip
Add stats & achievements API
Diffstat (limited to 'src')
-rw-r--r--src/user_stats.rs33
-rw-r--r--src/user_stats/stat_callback.rs113
-rw-r--r--src/user_stats/stats.rs80
3 files changed, 225 insertions, 1 deletions
diff --git a/src/user_stats.rs b/src/user_stats.rs
index 745b76a..2ab976b 100644
--- a/src/user_stats.rs
+++ b/src/user_stats.rs
@@ -1,4 +1,7 @@
+pub mod stats;
+mod stat_callback;
+pub use self::stat_callback::*;
use super::*;
/// Access to the steam user interface
@@ -141,6 +144,34 @@ impl <Manager> UserStats<Manager> {
});
}
}
+
+ /// Triggers a [`UserStatsReceived`](./struct.UserStatsReceived.html) callback.
+ pub fn request_current_stats(&self) {
+ unsafe { sys::SteamAPI_ISteamUserStats_RequestCurrentStats(self.user_stats); }
+ }
+
+ /// Send the changed stats and achievements data to the server for permanent storage.
+ ///
+ /// * Triggers a [`UserStatsStored`](../struct.UserStatsStored.html) callback if successful.
+ /// * Triggers a [`UserAchievementStored`](../struct.UserAchievementStored.html) callback
+ /// if achievements have been unlocked.
+ ///
+ /// Requires [`request_current_stats()`](#method.request_current_stats) to have been called
+ /// and a successful [`UserStatsReceived`](./struct.UserStatsReceived.html) callback processed.
+ pub fn store_stats(&self) -> Result<(), ()> {
+ let success = unsafe { sys::SteamAPI_ISteamUserStats_StoreStats(self.user_stats) };
+ if success { Ok(()) } else { Err(()) }
+ }
+
+ /// Access achievement API for a given achievement 'API Name'.
+ ///
+ /// Requires [`request_current_stats()`](#method.request_current_stats) to have been called
+ /// and a successful [`UserStatsReceived`](./struct.UserStatsReceived.html) callback processed.
+ #[inline]
+ #[must_use]
+ pub fn achievement(&self, name: &str) -> stats::AchievementHelper<'_, Manager> {
+ stats::AchievementHelper { name: CString::new(name).unwrap(), parent: self }
+ }
}
#[derive(Debug)]
@@ -211,4 +242,4 @@ fn test() {
single.run_callbacks();
::std::thread::sleep(::std::time::Duration::from_millis(100));
}
-} \ No newline at end of file
+}
diff --git a/src/user_stats/stat_callback.rs b/src/user_stats/stat_callback.rs
new file mode 100644
index 0000000..da62e6a
--- /dev/null
+++ b/src/user_stats/stat_callback.rs
@@ -0,0 +1,113 @@
+use super::*;
+
+/// Callback type after calling
+/// [`request_current_stats()`](struct.UserStats.html#method.request_current_stats).
+///
+/// # Example
+///
+/// ```no_run
+/// # use steamworks::*;
+/// # let (client, single) = steamworks::Client::init().unwrap();
+/// let callback_handle = client.register_callback(|val: UserStatsReceived| {
+/// if val.result.is_err() {
+/// // ...
+/// }
+/// });
+/// ```
+#[derive(Debug)]
+pub struct UserStatsReceived {
+ pub steam_id: SteamId,
+ pub game_id: GameId,
+ pub result: Result<(), SteamError>,
+}
+
+unsafe impl Callback for UserStatsReceived {
+ const ID: i32 = CALLBACK_BASE_ID + 1;
+ const SIZE: i32 = std::mem::size_of::<sys::UserStatsReceived_t>() as i32;
+
+ unsafe fn from_raw(raw: *mut libc::c_void) -> Self {
+ let val = &mut *(raw as *mut sys::UserStatsReceived_t);
+ Self {
+ steam_id: SteamId(val.m_steamIDUser.0),
+ game_id: GameId(val.m_nGameID),
+ result: match val.m_eResult {
+ sys::EResult::EResultOK => Ok(()),
+ err => Err(err.into()),
+ },
+ }
+ }
+}
+
+/// Callback triggered by [`store()`](stats/struct.StatsHelper.html#method.store).
+///
+/// # Example
+///
+/// ```no_run
+/// # use steamworks::*;
+/// # let (client, single) = steamworks::Client::init().unwrap();
+/// let callback_handle = client.register_callback(|val: UserStatsStored| {
+/// if val.result.is_err() {
+/// // ...
+/// }
+/// });
+/// ```
+#[derive(Debug)]
+pub struct UserStatsStored {
+ pub game_id: GameId,
+ pub result: Result<(), SteamError>,
+}
+
+unsafe impl Callback for UserStatsStored {
+ const ID: i32 = CALLBACK_BASE_ID + 2;
+ const SIZE: i32 = std::mem::size_of::<sys::UserStatsStored_t>() as i32;
+
+ unsafe fn from_raw(raw: *mut libc::c_void) -> Self {
+ let val = &mut *(raw as *mut sys::UserStatsStored_t);
+ Self {
+ game_id: GameId(val.m_nGameID),
+ result: match val.m_eResult {
+ sys::EResult::EResultOK => Ok(()),
+ err => Err(err.into()),
+ },
+ }
+ }
+}
+
+/// Result of a request to store the achievements on the server, or an "indicate progress" call.
+/// If both `current_progress` and `max_progress` are zero, that means the achievement has been
+/// fully unlocked.
+///
+/// # Example
+///
+/// ```no_run
+/// # use steamworks::*;
+/// # let (client, single) = steamworks::Client::init().unwrap();
+/// let callback_handle = client.register_callback(|val: UserAchievementStored| {
+/// // ...
+/// });
+/// ```
+#[derive(Debug)]
+pub struct UserAchievementStored {
+ pub game_id: GameId,
+ pub achievement_name: String,
+ /// Current progress towards the achievement.
+ pub current_progress: u32,
+ /// The total amount of progress required to unlock.
+ pub max_progress: u32,
+}
+
+unsafe impl Callback for UserAchievementStored {
+ const ID: i32 = CALLBACK_BASE_ID + 3;
+ const SIZE: i32 = std::mem::size_of::<sys::UserAchievementStored_t>() as i32;
+
+ unsafe fn from_raw(raw: *mut libc::c_void) -> Self {
+ let val = &mut *(raw as *mut sys::UserAchievementStored_t);
+ let name = CStr::from_ptr(val.m_rgchAchievementName.as_ptr()).to_owned();
+ Self {
+ game_id: GameId(val.m_nGameID),
+ achievement_name: name.into_string().unwrap(),
+ current_progress: val.m_nCurProgress,
+ max_progress: val.m_nMaxProgress,
+ }
+ }
+}
diff --git a/src/user_stats/stats.rs b/src/user_stats/stats.rs
new file mode 100644
index 0000000..56d650f
--- /dev/null
+++ b/src/user_stats/stats.rs
@@ -0,0 +1,80 @@
+use super::*;
+
+/// Achievement API.
+///
+/// Methods require
+/// [`request_current_stats()`](../struct.UserStats.html#method.request_current_stats)
+/// to have been called and a successful [`UserStatsReceived`](../struct.UserStatsReceived.html)
+/// callback processed.
+///
+/// # Example
+///
+/// ```no_run
+/// # use steamworks::*;
+/// # let (client, single) = steamworks::Client::init().unwrap();
+/// // Unlock the 'WIN_THE_GAME' achievement
+/// client.user_stats().achievement("WIN_THE_GAME").set()?;
+/// # Err(())
+/// ```
+pub struct AchievementHelper<'parent, M> {
+ pub(crate) name: CString,
+ pub(crate) parent: &'parent UserStats<M>,
+}
+
+impl<M> AchievementHelper<'_, M> {
+ /// Gets the unlock status of the Achievement.
+ ///
+ /// This call only modifies Steam's in-memory state so it is quite cheap. To send the unlock
+ /// status to the server and to trigger the Steam overlay notification you must call
+ /// [`store_stats()`](../struct.UserStats.html#method.store_stats).
+ ///
+ /// Fails if this achievement's 'API Name' is unknown, or unsuccessful
+ /// [`UserStatsReceived`](../struct.UserStatsReceived.html).
+ pub fn get(&self) -> Result<bool, ()> {
+ unsafe {
+ let mut achieved = false;
+ let success = sys::SteamAPI_ISteamUserStats_GetAchievement(
+ self.parent.user_stats,
+ self.name.as_ptr() as *const _,
+ &mut achieved as *mut _,
+ );
+ if success { Ok(achieved) } else { Err(()) }
+ }
+ }
+
+ /// Unlocks an achievement.
+ ///
+ /// This call only modifies Steam's in-memory state so it is quite cheap. To send the unlock
+ /// status to the server and to trigger the Steam overlay notification you must call
+ /// [`store_stats()`](../struct.UserStats.html#method.store_stats).
+ ///
+ /// Fails if this achievement's 'API Name' is unknown, or unsuccessful
+ /// [`UserStatsReceived`](../struct.UserStatsReceived.html).
+ pub fn set(&self) -> Result<(), ()> {
+ let success = unsafe {
+ sys::SteamAPI_ISteamUserStats_SetAchievement(
+ self.parent.user_stats,
+ self.name.as_ptr() as *const _,
+ )
+ };
+ if success { Ok(()) } else { Err(()) }
+ }
+
+ /// Resets the unlock status of an achievement.
+ ///
+ /// This call only modifies Steam's in-memory state so it is quite cheap. To send the unlock
+ /// status to the server and to trigger the Steam overlay notification you must call
+ /// [`store_stats()`](../struct.UserStats.html#method.store_stats).
+ ///
+ /// Fails if this achievement's 'API Name' is unknown, or unsuccessful
+ /// [`UserStatsReceived`](../struct.UserStatsReceived.html).
+ pub fn clear(&self) -> Result<(), ()> {
+ let success = unsafe {
+ sys::SteamAPI_ISteamUserStats_ClearAchievement(
+ self.parent.user_stats,
+ self.name.as_ptr() as *const _,
+ )
+ };
+ if success { Ok(()) } else { Err(()) }
+ }
+}