-
Notifications
You must be signed in to change notification settings - Fork 118
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d1be508
Showing
11 changed files
with
524 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/target/ | ||
**/*.rs.bk | ||
Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "steamworks" | ||
version = "0.1.0" | ||
authors = ["matt"] | ||
|
||
[workspace] | ||
members = [ | ||
"./steamworks-sys" | ||
] | ||
|
||
[dependencies] | ||
steamworks-sys = {path = "./steamworks-sys"} | ||
error-chain = "0.11.0" | ||
bitflags = "0.9.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
use super::*; | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub struct AppId(pub u32); | ||
|
||
pub struct Apps { | ||
pub(crate) apps: *mut sys::ISteamApps, | ||
pub(crate) _client: Rc<ClientInner>, | ||
} | ||
|
||
impl Apps { | ||
|
||
pub fn is_app_installed(&self, app_id: AppId) -> bool { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_BIsAppInstalled(self.apps, app_id.0) != 0 | ||
} | ||
} | ||
|
||
pub fn is_dlc_installed(&self, app_id: AppId) -> bool { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_BIsDlcInstalled(self.apps, app_id.0) != 0 | ||
} | ||
} | ||
|
||
pub fn is_subscribed_app(&self, app_id: AppId) -> bool { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_BIsSubscribedApp(self.apps, app_id.0) != 0 | ||
} | ||
} | ||
|
||
pub fn is_subscribed_from_free_weekend(&self) -> bool { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_BIsSubscribedFromFreeWeekend(self.apps) != 0 | ||
} | ||
} | ||
|
||
pub fn is_vac_banned(&self) -> bool { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_BIsVACBanned(self.apps) != 0 | ||
} | ||
} | ||
|
||
pub fn is_cybercafe(&self) -> bool { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_BIsCybercafe(self.apps) != 0 | ||
} | ||
} | ||
|
||
pub fn is_low_violence(&self) -> bool { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_BIsLowViolence(self.apps) != 0 | ||
} | ||
} | ||
|
||
pub fn is_subscribed(&self) -> bool { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_BIsSubscribed(self.apps) != 0 | ||
} | ||
} | ||
|
||
pub fn app_build_id(&self) -> i32 { | ||
unsafe { | ||
sys::SteamAPI_ISteamApps_GetAppBuildId(self.apps) as i32 | ||
} | ||
} | ||
|
||
pub fn app_install_dir(&self, app_id: AppId) -> String { | ||
unsafe { | ||
let buffer = vec![0; 2048]; | ||
sys::SteamAPI_ISteamApps_GetAppInstallDir(self.apps, app_id.0, buffer.as_ptr(), buffer.len() as u32); | ||
let path = CStr::from_ptr(buffer.as_ptr()); | ||
path.to_string_lossy().into_owned() | ||
} | ||
} | ||
|
||
pub fn app_owner(&self) -> SteamId { | ||
unsafe { | ||
SteamId(sys::SteamAPI_ISteamApps_GetAppOwner(self.apps)) | ||
} | ||
} | ||
|
||
pub fn available_game_languages(&self) -> Vec<String> { | ||
unsafe { | ||
let langs = sys::SteamAPI_ISteamApps_GetAvailableGameLanguages(self.apps); | ||
let langs = CStr::from_ptr(langs); | ||
let langs = langs.to_string_lossy(); | ||
langs.split(',') | ||
.map(|v| v.to_owned()) | ||
.collect() | ||
} | ||
} | ||
|
||
pub fn current_game_language(&self) -> Cow<str> { | ||
unsafe { | ||
let lang = sys::SteamAPI_ISteamApps_GetCurrentGameLanguage(self.apps); | ||
let lang = CStr::from_ptr(lang); | ||
lang.to_string_lossy() | ||
} | ||
} | ||
|
||
pub fn current_beta_name(&self) -> Option<String> { | ||
unsafe { | ||
let buffer = vec![0; 256]; | ||
if sys::SteamAPI_ISteamApps_GetCurrentBetaName(self.apps, buffer.as_ptr(), buffer.len() as _) != 0 { | ||
let path = CStr::from_ptr(buffer.as_ptr()); | ||
Some(path.to_string_lossy().into_owned()) | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
error_chain! { | ||
types { | ||
Error, ErrorKind, ResultExt, Result; | ||
} | ||
links { | ||
} | ||
|
||
foreign_links { | ||
} | ||
|
||
errors { | ||
InitFailed { | ||
description("failed to init the steamworks API"), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
|
||
use super::*; | ||
|
||
bitflags! { | ||
#[repr(C)] | ||
pub struct FriendFlags: u16 { | ||
const FRIEND_FLAG_NONE = 0x0000; | ||
const FRIEND_FLAG_BLOCKED = 0x0001; | ||
const FRIEND_FLAG_FRIENDSHIP_REQUESTED = 0x0002; | ||
const FRIEND_FLAG_IMMEDIATE = 0x0004; | ||
const FRIEND_FLAG_CLAN_MEMBER = 0x0008; | ||
const FRIEND_FLAG_ON_GAME_SERVER = 0x0010; | ||
// Unused | ||
// Unused | ||
const FRIEND_FLAG_REQUESTING_FRIENDSHIP = 0x0080; | ||
const FRIEND_FLAG_REQUESTING_INFO = 0x0100; | ||
const FRIEND_FLAG_IGNORED = 0x0200; | ||
const FRIEND_FLAG_IGNORED_FRIEND = 0x0400; | ||
// Unused | ||
const FRIEND_FLAG_CHAT_MEMBER = 0x1000; | ||
const FRIEND_FLAG_ALL = 0xFFFF; | ||
} | ||
} | ||
|
||
pub struct Friends { | ||
pub(crate) friends: *mut sys::ISteamFriends, | ||
pub(crate) _client: Rc<ClientInner>, | ||
} | ||
|
||
impl Friends { | ||
pub fn get_friends(&self, flags: FriendFlags) -> Vec<Friend> { | ||
unsafe { | ||
let count = sys::SteamAPI_ISteamFriends_GetFriendCount(self.friends, flags.bits() as _); | ||
let mut friends = Vec::with_capacity(count as usize); | ||
for idx in 0 .. count { | ||
let friend = SteamId(sys::SteamAPI_ISteamFriends_GetFriendByIndex(self.friends, idx, flags.bits() as _)); | ||
friends.push(Friend { | ||
id: friend, | ||
friends: self.friends, | ||
_client: self._client.clone(), | ||
}); | ||
} | ||
|
||
friends | ||
} | ||
} | ||
} | ||
|
||
pub struct Friend { | ||
id: SteamId, | ||
friends: *mut sys::ISteamFriends, | ||
_client: Rc<ClientInner>, | ||
} | ||
|
||
impl Debug for Friend { | ||
fn fmt(&self, f: &mut Formatter) -> fmt::Result { | ||
write!(f, "Friend({:?})", self.id) | ||
} | ||
} | ||
|
||
impl Friend { | ||
pub fn id(&self) -> SteamId { | ||
self.id | ||
} | ||
|
||
pub fn name(&self) -> Cow<str> { | ||
unsafe { | ||
let name = sys::SteamAPI_ISteamFriends_GetFriendPersonaName(self.friends, self.id.0); | ||
let name = CStr::from_ptr(name); | ||
name.to_string_lossy() | ||
} | ||
} | ||
|
||
pub fn state(&self) -> FriendState { | ||
unsafe { | ||
let state = sys::SteamAPI_ISteamFriends_GetFriendPersonaState(self.friends, self.id.0); | ||
match state { | ||
sys::PersonaState::Offline => FriendState::Offline, | ||
sys::PersonaState::Online => FriendState::Online, | ||
sys::PersonaState::Busy => FriendState::Busy, | ||
sys::PersonaState::Away => FriendState::Away, | ||
sys::PersonaState::Snooze => FriendState::Snooze, | ||
sys::PersonaState::LookingToPlay => FriendState::LookingToPlay, | ||
sys::PersonaState::LookingToTrade => FriendState::LookingToTrade, | ||
sys::PersonaState::Max => unreachable!(), | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug)] | ||
pub enum FriendState { | ||
Offline, | ||
Online, | ||
Busy, | ||
Away, | ||
Snooze, | ||
LookingToTrade, | ||
LookingToPlay, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
|
||
extern crate steamworks_sys as sys; | ||
#[macro_use] | ||
extern crate error_chain; | ||
#[macro_use] | ||
extern crate bitflags; | ||
|
||
pub mod error; | ||
pub use error::Result as SResult; | ||
use error::ErrorKind; | ||
|
||
mod utils; | ||
pub use utils::*; | ||
mod app; | ||
pub use app::*; | ||
mod friends; | ||
pub use friends::*; | ||
|
||
use std::rc::Rc; | ||
use std::ffi::CStr; | ||
use std::borrow::Cow; | ||
use std::fmt::{ | ||
Debug, Formatter, self | ||
}; | ||
|
||
pub struct Client { | ||
inner: Rc<ClientInner>, | ||
} | ||
|
||
struct ClientInner { | ||
client: *mut sys::ISteamClient, | ||
pipe: sys::HSteamPipe, | ||
} | ||
|
||
impl Client { | ||
pub fn init() -> SResult<Client> { | ||
unsafe { | ||
if sys::SteamAPI_Init() == 0 { | ||
bail!(ErrorKind::InitFailed); | ||
} | ||
let client = sys::SteamInternal_CreateInterface(sys::STEAMCLIENT_INTERFACE_VERSION.as_ptr() as *const _); | ||
let client = Rc::new(ClientInner { | ||
client: client, | ||
pipe: sys::SteamAPI_ISteamClient_CreateSteamPipe(client), | ||
}); | ||
Ok(Client { | ||
inner: client, | ||
}) | ||
} | ||
} | ||
|
||
pub fn utils(&self) -> Utils { | ||
unsafe { | ||
let utils = sys::SteamAPI_ISteamClient_GetISteamUtils( | ||
self.inner.client, self.inner.pipe, | ||
sys::STEAMUTILS_INTERFACE_VERSION.as_ptr() as *const _ | ||
); | ||
assert!(!utils.is_null()); | ||
Utils { | ||
utils: utils, | ||
_client: self.inner.clone(), | ||
} | ||
} | ||
} | ||
|
||
pub fn apps(&self) -> Apps { | ||
unsafe { | ||
let user = sys::SteamAPI_ISteamClient_ConnectToGlobalUser(self.inner.client, self.inner.pipe); | ||
let apps = sys::SteamAPI_ISteamClient_GetISteamApps( | ||
self.inner.client, user, self.inner.pipe, | ||
sys::STEAMAPPS_INTERFACE_VERSION.as_ptr() as *const _ | ||
); | ||
assert!(!apps.is_null()); | ||
Apps { | ||
apps: apps, | ||
_client: self.inner.clone(), | ||
} | ||
} | ||
} | ||
|
||
pub fn friends(&self) -> Friends { | ||
unsafe { | ||
let user = sys::SteamAPI_ISteamClient_ConnectToGlobalUser(self.inner.client, self.inner.pipe); | ||
let friends = sys::SteamAPI_ISteamClient_GetISteamFriends( | ||
self.inner.client, user, self.inner.pipe, | ||
sys::STEAMFRIENDS_INTERFACE_VERSION.as_ptr() as *const _ | ||
); | ||
assert!(!friends.is_null()); | ||
Friends { | ||
friends: friends, | ||
_client: self.inner.clone(), | ||
} | ||
} | ||
|
||
} | ||
} | ||
|
||
impl Drop for ClientInner { | ||
fn drop(&mut self) { | ||
unsafe { | ||
debug_assert!(sys::SteamAPI_ISteamClient_BReleaseSteamPipe(self.client, self.pipe) != 0); | ||
sys::SteamAPI_Shutdown(); | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug)] | ||
pub struct SteamId(pub(crate) u64); | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
#[test] | ||
fn basic_test() { | ||
let client = Client::init().unwrap(); | ||
|
||
let utils = client.utils(); | ||
println!("Utils:"); | ||
println!("AppId: {:?}", utils.app_id()); | ||
println!("UI Language: {}", utils.ui_language()); | ||
|
||
let apps = client.apps(); | ||
println!("Apps"); | ||
println!("IsInstalled(480): {}", apps.is_app_installed(AppId(480))); | ||
println!("InstallDir(480): {}", apps.app_install_dir(AppId(480))); | ||
println!("BuildId: {}", apps.app_build_id()); | ||
println!("AppOwner: {:?}", apps.app_owner()); | ||
println!("Langs: {:?}", apps.available_game_languages()); | ||
println!("Lang: {}", apps.current_game_language()); | ||
println!("Beta: {:?}", apps.current_beta_name()); | ||
|
||
let friends = client.friends(); | ||
println!("Friends"); | ||
let list = friends.get_friends(FRIEND_FLAG_IMMEDIATE); | ||
println!("{:?}", list); | ||
for f in &list { | ||
println!("Friend: {:?} - {}({:?})", f.id(), f.name(), f.state()); | ||
} | ||
} | ||
} |
Oops, something went wrong.