Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Thinkofname committed Sep 6, 2017
0 parents commit d1be508
Show file tree
Hide file tree
Showing 11 changed files with 524 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target/
**/*.rs.bk
Cargo.lock
14 changes: 14 additions & 0 deletions Cargo.toml
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"
112 changes: 112 additions & 0 deletions src/app.rs
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
}
}
}
}
17 changes: 17 additions & 0 deletions src/error.rs
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"),
}
}
}
100 changes: 100 additions & 0 deletions src/friends.rs
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,
}
140 changes: 140 additions & 0 deletions src/lib.rs
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());
}
}
}
Loading

0 comments on commit d1be508

Please sign in to comment.