Skip to content

Commit d1be508

Browse files
committed
Initial commit
0 parents  commit d1be508

11 files changed

+524
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target/
2+
**/*.rs.bk
3+
Cargo.lock

Cargo.toml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "steamworks"
3+
version = "0.1.0"
4+
authors = ["matt"]
5+
6+
[workspace]
7+
members = [
8+
"./steamworks-sys"
9+
]
10+
11+
[dependencies]
12+
steamworks-sys = {path = "./steamworks-sys"}
13+
error-chain = "0.11.0"
14+
bitflags = "0.9.1"

src/app.rs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use super::*;
2+
3+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4+
pub struct AppId(pub u32);
5+
6+
pub struct Apps {
7+
pub(crate) apps: *mut sys::ISteamApps,
8+
pub(crate) _client: Rc<ClientInner>,
9+
}
10+
11+
impl Apps {
12+
13+
pub fn is_app_installed(&self, app_id: AppId) -> bool {
14+
unsafe {
15+
sys::SteamAPI_ISteamApps_BIsAppInstalled(self.apps, app_id.0) != 0
16+
}
17+
}
18+
19+
pub fn is_dlc_installed(&self, app_id: AppId) -> bool {
20+
unsafe {
21+
sys::SteamAPI_ISteamApps_BIsDlcInstalled(self.apps, app_id.0) != 0
22+
}
23+
}
24+
25+
pub fn is_subscribed_app(&self, app_id: AppId) -> bool {
26+
unsafe {
27+
sys::SteamAPI_ISteamApps_BIsSubscribedApp(self.apps, app_id.0) != 0
28+
}
29+
}
30+
31+
pub fn is_subscribed_from_free_weekend(&self) -> bool {
32+
unsafe {
33+
sys::SteamAPI_ISteamApps_BIsSubscribedFromFreeWeekend(self.apps) != 0
34+
}
35+
}
36+
37+
pub fn is_vac_banned(&self) -> bool {
38+
unsafe {
39+
sys::SteamAPI_ISteamApps_BIsVACBanned(self.apps) != 0
40+
}
41+
}
42+
43+
pub fn is_cybercafe(&self) -> bool {
44+
unsafe {
45+
sys::SteamAPI_ISteamApps_BIsCybercafe(self.apps) != 0
46+
}
47+
}
48+
49+
pub fn is_low_violence(&self) -> bool {
50+
unsafe {
51+
sys::SteamAPI_ISteamApps_BIsLowViolence(self.apps) != 0
52+
}
53+
}
54+
55+
pub fn is_subscribed(&self) -> bool {
56+
unsafe {
57+
sys::SteamAPI_ISteamApps_BIsSubscribed(self.apps) != 0
58+
}
59+
}
60+
61+
pub fn app_build_id(&self) -> i32 {
62+
unsafe {
63+
sys::SteamAPI_ISteamApps_GetAppBuildId(self.apps) as i32
64+
}
65+
}
66+
67+
pub fn app_install_dir(&self, app_id: AppId) -> String {
68+
unsafe {
69+
let buffer = vec![0; 2048];
70+
sys::SteamAPI_ISteamApps_GetAppInstallDir(self.apps, app_id.0, buffer.as_ptr(), buffer.len() as u32);
71+
let path = CStr::from_ptr(buffer.as_ptr());
72+
path.to_string_lossy().into_owned()
73+
}
74+
}
75+
76+
pub fn app_owner(&self) -> SteamId {
77+
unsafe {
78+
SteamId(sys::SteamAPI_ISteamApps_GetAppOwner(self.apps))
79+
}
80+
}
81+
82+
pub fn available_game_languages(&self) -> Vec<String> {
83+
unsafe {
84+
let langs = sys::SteamAPI_ISteamApps_GetAvailableGameLanguages(self.apps);
85+
let langs = CStr::from_ptr(langs);
86+
let langs = langs.to_string_lossy();
87+
langs.split(',')
88+
.map(|v| v.to_owned())
89+
.collect()
90+
}
91+
}
92+
93+
pub fn current_game_language(&self) -> Cow<str> {
94+
unsafe {
95+
let lang = sys::SteamAPI_ISteamApps_GetCurrentGameLanguage(self.apps);
96+
let lang = CStr::from_ptr(lang);
97+
lang.to_string_lossy()
98+
}
99+
}
100+
101+
pub fn current_beta_name(&self) -> Option<String> {
102+
unsafe {
103+
let buffer = vec![0; 256];
104+
if sys::SteamAPI_ISteamApps_GetCurrentBetaName(self.apps, buffer.as_ptr(), buffer.len() as _) != 0 {
105+
let path = CStr::from_ptr(buffer.as_ptr());
106+
Some(path.to_string_lossy().into_owned())
107+
} else {
108+
None
109+
}
110+
}
111+
}
112+
}

src/error.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
error_chain! {
3+
types {
4+
Error, ErrorKind, ResultExt, Result;
5+
}
6+
links {
7+
}
8+
9+
foreign_links {
10+
}
11+
12+
errors {
13+
InitFailed {
14+
description("failed to init the steamworks API"),
15+
}
16+
}
17+
}

src/friends.rs

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
2+
use super::*;
3+
4+
bitflags! {
5+
#[repr(C)]
6+
pub struct FriendFlags: u16 {
7+
const FRIEND_FLAG_NONE = 0x0000;
8+
const FRIEND_FLAG_BLOCKED = 0x0001;
9+
const FRIEND_FLAG_FRIENDSHIP_REQUESTED = 0x0002;
10+
const FRIEND_FLAG_IMMEDIATE = 0x0004;
11+
const FRIEND_FLAG_CLAN_MEMBER = 0x0008;
12+
const FRIEND_FLAG_ON_GAME_SERVER = 0x0010;
13+
// Unused
14+
// Unused
15+
const FRIEND_FLAG_REQUESTING_FRIENDSHIP = 0x0080;
16+
const FRIEND_FLAG_REQUESTING_INFO = 0x0100;
17+
const FRIEND_FLAG_IGNORED = 0x0200;
18+
const FRIEND_FLAG_IGNORED_FRIEND = 0x0400;
19+
// Unused
20+
const FRIEND_FLAG_CHAT_MEMBER = 0x1000;
21+
const FRIEND_FLAG_ALL = 0xFFFF;
22+
}
23+
}
24+
25+
pub struct Friends {
26+
pub(crate) friends: *mut sys::ISteamFriends,
27+
pub(crate) _client: Rc<ClientInner>,
28+
}
29+
30+
impl Friends {
31+
pub fn get_friends(&self, flags: FriendFlags) -> Vec<Friend> {
32+
unsafe {
33+
let count = sys::SteamAPI_ISteamFriends_GetFriendCount(self.friends, flags.bits() as _);
34+
let mut friends = Vec::with_capacity(count as usize);
35+
for idx in 0 .. count {
36+
let friend = SteamId(sys::SteamAPI_ISteamFriends_GetFriendByIndex(self.friends, idx, flags.bits() as _));
37+
friends.push(Friend {
38+
id: friend,
39+
friends: self.friends,
40+
_client: self._client.clone(),
41+
});
42+
}
43+
44+
friends
45+
}
46+
}
47+
}
48+
49+
pub struct Friend {
50+
id: SteamId,
51+
friends: *mut sys::ISteamFriends,
52+
_client: Rc<ClientInner>,
53+
}
54+
55+
impl Debug for Friend {
56+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
57+
write!(f, "Friend({:?})", self.id)
58+
}
59+
}
60+
61+
impl Friend {
62+
pub fn id(&self) -> SteamId {
63+
self.id
64+
}
65+
66+
pub fn name(&self) -> Cow<str> {
67+
unsafe {
68+
let name = sys::SteamAPI_ISteamFriends_GetFriendPersonaName(self.friends, self.id.0);
69+
let name = CStr::from_ptr(name);
70+
name.to_string_lossy()
71+
}
72+
}
73+
74+
pub fn state(&self) -> FriendState {
75+
unsafe {
76+
let state = sys::SteamAPI_ISteamFriends_GetFriendPersonaState(self.friends, self.id.0);
77+
match state {
78+
sys::PersonaState::Offline => FriendState::Offline,
79+
sys::PersonaState::Online => FriendState::Online,
80+
sys::PersonaState::Busy => FriendState::Busy,
81+
sys::PersonaState::Away => FriendState::Away,
82+
sys::PersonaState::Snooze => FriendState::Snooze,
83+
sys::PersonaState::LookingToPlay => FriendState::LookingToPlay,
84+
sys::PersonaState::LookingToTrade => FriendState::LookingToTrade,
85+
sys::PersonaState::Max => unreachable!(),
86+
}
87+
}
88+
}
89+
}
90+
91+
#[derive(Clone, Copy, Debug)]
92+
pub enum FriendState {
93+
Offline,
94+
Online,
95+
Busy,
96+
Away,
97+
Snooze,
98+
LookingToTrade,
99+
LookingToPlay,
100+
}

src/lib.rs

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
2+
extern crate steamworks_sys as sys;
3+
#[macro_use]
4+
extern crate error_chain;
5+
#[macro_use]
6+
extern crate bitflags;
7+
8+
pub mod error;
9+
pub use error::Result as SResult;
10+
use error::ErrorKind;
11+
12+
mod utils;
13+
pub use utils::*;
14+
mod app;
15+
pub use app::*;
16+
mod friends;
17+
pub use friends::*;
18+
19+
use std::rc::Rc;
20+
use std::ffi::CStr;
21+
use std::borrow::Cow;
22+
use std::fmt::{
23+
Debug, Formatter, self
24+
};
25+
26+
pub struct Client {
27+
inner: Rc<ClientInner>,
28+
}
29+
30+
struct ClientInner {
31+
client: *mut sys::ISteamClient,
32+
pipe: sys::HSteamPipe,
33+
}
34+
35+
impl Client {
36+
pub fn init() -> SResult<Client> {
37+
unsafe {
38+
if sys::SteamAPI_Init() == 0 {
39+
bail!(ErrorKind::InitFailed);
40+
}
41+
let client = sys::SteamInternal_CreateInterface(sys::STEAMCLIENT_INTERFACE_VERSION.as_ptr() as *const _);
42+
let client = Rc::new(ClientInner {
43+
client: client,
44+
pipe: sys::SteamAPI_ISteamClient_CreateSteamPipe(client),
45+
});
46+
Ok(Client {
47+
inner: client,
48+
})
49+
}
50+
}
51+
52+
pub fn utils(&self) -> Utils {
53+
unsafe {
54+
let utils = sys::SteamAPI_ISteamClient_GetISteamUtils(
55+
self.inner.client, self.inner.pipe,
56+
sys::STEAMUTILS_INTERFACE_VERSION.as_ptr() as *const _
57+
);
58+
assert!(!utils.is_null());
59+
Utils {
60+
utils: utils,
61+
_client: self.inner.clone(),
62+
}
63+
}
64+
}
65+
66+
pub fn apps(&self) -> Apps {
67+
unsafe {
68+
let user = sys::SteamAPI_ISteamClient_ConnectToGlobalUser(self.inner.client, self.inner.pipe);
69+
let apps = sys::SteamAPI_ISteamClient_GetISteamApps(
70+
self.inner.client, user, self.inner.pipe,
71+
sys::STEAMAPPS_INTERFACE_VERSION.as_ptr() as *const _
72+
);
73+
assert!(!apps.is_null());
74+
Apps {
75+
apps: apps,
76+
_client: self.inner.clone(),
77+
}
78+
}
79+
}
80+
81+
pub fn friends(&self) -> Friends {
82+
unsafe {
83+
let user = sys::SteamAPI_ISteamClient_ConnectToGlobalUser(self.inner.client, self.inner.pipe);
84+
let friends = sys::SteamAPI_ISteamClient_GetISteamFriends(
85+
self.inner.client, user, self.inner.pipe,
86+
sys::STEAMFRIENDS_INTERFACE_VERSION.as_ptr() as *const _
87+
);
88+
assert!(!friends.is_null());
89+
Friends {
90+
friends: friends,
91+
_client: self.inner.clone(),
92+
}
93+
}
94+
95+
}
96+
}
97+
98+
impl Drop for ClientInner {
99+
fn drop(&mut self) {
100+
unsafe {
101+
debug_assert!(sys::SteamAPI_ISteamClient_BReleaseSteamPipe(self.client, self.pipe) != 0);
102+
sys::SteamAPI_Shutdown();
103+
}
104+
}
105+
}
106+
107+
#[derive(Clone, Copy, Debug)]
108+
pub struct SteamId(pub(crate) u64);
109+
110+
#[cfg(test)]
111+
mod tests {
112+
use super::*;
113+
#[test]
114+
fn basic_test() {
115+
let client = Client::init().unwrap();
116+
117+
let utils = client.utils();
118+
println!("Utils:");
119+
println!("AppId: {:?}", utils.app_id());
120+
println!("UI Language: {}", utils.ui_language());
121+
122+
let apps = client.apps();
123+
println!("Apps");
124+
println!("IsInstalled(480): {}", apps.is_app_installed(AppId(480)));
125+
println!("InstallDir(480): {}", apps.app_install_dir(AppId(480)));
126+
println!("BuildId: {}", apps.app_build_id());
127+
println!("AppOwner: {:?}", apps.app_owner());
128+
println!("Langs: {:?}", apps.available_game_languages());
129+
println!("Lang: {}", apps.current_game_language());
130+
println!("Beta: {:?}", apps.current_beta_name());
131+
132+
let friends = client.friends();
133+
println!("Friends");
134+
let list = friends.get_friends(FRIEND_FLAG_IMMEDIATE);
135+
println!("{:?}", list);
136+
for f in &list {
137+
println!("Friend: {:?} - {}({:?})", f.id(), f.name(), f.state());
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)