aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Collins <[email protected]>2018-02-28 16:12:31 +0000
committerMatthew Collins <[email protected]>2018-02-28 16:12:31 +0000
commit844887aea84cb17a135d070304726a33f17b30b7 (patch)
treedd26d34956283c9ef261fbae9c7e56c9992ea2ce
parentImplement user authentication session methods (diff)
downloadsteamworks-rs-844887aea84cb17a135d070304726a33f17b30b7.tar.xz
steamworks-rs-844887aea84cb17a135d070304726a33f17b30b7.zip
Initial support for servers
-rw-r--r--Cargo.toml2
-rw-r--r--src/lib.rs84
-rw-r--r--src/server.rs299
-rw-r--r--src/user.rs2
-rw-r--r--steamworks-sys/Cargo.toml2
-rw-r--r--steamworks-sys/src/lib.cpp24
-rw-r--r--steamworks-sys/src/lib.rs28
7 files changed, 398 insertions, 43 deletions
diff --git a/Cargo.toml b/Cargo.toml
index b0b7494..0466b65 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,7 @@ members = [
]
[dependencies]
-steamworks-sys = {path = "./steamworks-sys", version = "0.1.1"}
+steamworks-sys = {path = "./steamworks-sys", version = "0.2.0"}
failure = "0.1.1"
bitflags = "1.0.1"
libc = "0.2.36"
diff --git a/src/lib.rs b/src/lib.rs
index e3d310c..cd9923c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,6 +9,8 @@ extern crate bitflags;
mod error;
pub use error::*;
+mod server;
+pub use server::*;
mod utils;
pub use utils::*;
mod app;
@@ -38,25 +40,28 @@ pub type SResult<T> = Result<T, SteamError>;
/// The main entry point into the steam client.
///
-/// This provides access to all of the steamworks api.
+/// This provides access to all of the steamworks api that
+/// clients can use.
#[derive(Clone)]
pub struct Client<Manager = ClientManager> {
inner: Arc<Inner<Manager>>,
+ _client: *mut sys::ISteamClient,
}
struct Inner<Manager> {
_manager: Manager,
- _client: *mut sys::ISteamClient,
- callbacks: Mutex<ClientCallbacks>,
+ callbacks: Mutex<Callbacks>,
}
-struct ClientCallbacks {
+struct Callbacks {
callbacks: Vec<*mut libc::c_void>,
call_results: HashMap<sys::SteamAPICall, *mut libc::c_void>,
}
unsafe impl <Manager: Send + Sync> Send for Inner<Manager> {}
unsafe impl <Manager: Send + Sync> Sync for Inner<Manager> {}
+unsafe impl <Manager: Send + Sync> Send for Client<Manager> {}
+unsafe impl <Manager: Send + Sync> Sync for Client<Manager> {}
impl Client<ClientManager> {
/// Attempts to initialize the steamworks api and returns
@@ -81,17 +86,17 @@ impl Client<ClientManager> {
if sys::SteamAPI_Init() == 0 {
return Err(SteamError::InitFailed);
}
- let client = sys::steam_rust_get_client();
+ let raw_client = sys::steam_rust_get_client();
let client = Arc::new(Inner {
_manager: ClientManager { _priv: () },
- _client: client,
- callbacks: Mutex::new(ClientCallbacks {
+ callbacks: Mutex::new(Callbacks {
callbacks: Vec::new(),
call_results: HashMap::new(),
}),
});
Ok(Client {
inner: client,
+ _client: raw_client,
})
}
}
@@ -121,35 +126,7 @@ impl <Manager> Client<Manager> {
F: FnMut(C) + 'static + Send + Sync
{
unsafe {
- let userdata = Box::into_raw(Box::new(f));
-
- extern "C" fn run_func<C, F>(userdata: *mut libc::c_void, param: *mut libc::c_void)
- where C: Callback,
- F: FnMut(C) + 'static + Send + Sync
- {
- unsafe {
- let func: &mut F = &mut *(userdata as *mut F);
- let param = C::from_raw(param);
- func(param);
- }
- }
- extern "C" fn dealloc<C, F>(userdata: *mut libc::c_void)
- where C: Callback,
- F: FnMut(C) + 'static + Send + Sync
- {
- let func: Box<F> = unsafe { Box::from_raw(userdata as _) };
- drop(func);
- }
-
- let ptr = sys::register_rust_steam_callback(
- C::size() as _,
- userdata as _,
- run_func::<C, F>,
- dealloc::<C, F>,
- C::id() as _
- );
- let mut cbs = self.inner.callbacks.lock().unwrap();
- cbs.callbacks.push(ptr);
+ register_callback(&self.inner, f, false);
}
}
@@ -233,6 +210,41 @@ impl <Manager> Drop for Inner<Manager> {
}
+pub(crate) unsafe fn register_callback<C, F, Manager>(inner: &Arc<Inner<Manager>>, f: F, game_server: bool)
+ where C: Callback,
+ F: FnMut(C) + 'static + Send + Sync
+{
+ let userdata = Box::into_raw(Box::new(f));
+
+ extern "C" fn run_func<C, F>(userdata: *mut libc::c_void, param: *mut libc::c_void)
+ where C: Callback,
+ F: FnMut(C) + 'static + Send + Sync
+ {
+ unsafe {
+ let func: &mut F = &mut *(userdata as *mut F);
+ let param = C::from_raw(param);
+ func(param);
+ }
+ }
+ extern "C" fn dealloc<C, F>(userdata: *mut libc::c_void)
+ where C: Callback,
+ F: FnMut(C) + 'static + Send + Sync
+ {
+ let func: Box<F> = unsafe { Box::from_raw(userdata as _) };
+ drop(func);
+ }
+
+ let ptr = sys::register_rust_steam_callback(
+ C::size() as _,
+ userdata as _,
+ run_func::<C, F>,
+ dealloc::<C, F>,
+ C::id() as _,
+ game_server as _,
+ );
+ let mut cbs = inner.callbacks.lock().unwrap();
+ cbs.callbacks.push(ptr);
+}
pub(crate) unsafe fn register_call_result<C, F, Manager>(inner: &Arc<Inner<Manager>>, api_call: sys::SteamAPICall, callback_id: i32, f: F)
where F: for <'a> FnMut(&'a C, bool) + 'static + Send + Sync
diff --git a/src/server.rs b/src/server.rs
new file mode 100644
index 0000000..ee37cac
--- /dev/null
+++ b/src/server.rs
@@ -0,0 +1,299 @@
+use super::*;
+
+use std::net::Ipv4Addr;
+
+/// The main entry point into the steam client for servers.
+///
+/// This provides access to all of the steamworks api that
+/// servers can use.
+#[derive(Clone)]
+pub struct Server {
+ inner: Arc<Inner<ServerManager>>,
+ server: *mut sys::ISteamGameServer,
+}
+
+unsafe impl Send for Server {}
+unsafe impl Sync for Server {}
+
+/// Used to set the mode that a gameserver will run in
+pub enum ServerMode {
+ /// Don't authenticate user logins.
+ ///
+ /// The server will not appear on the server list
+ NoAuthentication,
+ /// Authenticate users
+ ///
+ /// The server will appear on the server list and
+ /// VAC will not be run on clients.
+ Authentication,
+ /// Authenticate users and use anti-cheat.
+ ///
+ /// The server will appear on the server list and
+ /// VAC will be run on clients.
+ AuthenticationAndSecure,
+}
+
+impl Server {
+ /// Attempts to initialize the steamworks api and returns
+ /// a server to access the rest of the api.
+ ///
+ /// This should only ever have one instance per a program.
+ ///
+ /// Currently the steamworks api doesn't support IPv6.
+ ///
+ /// # Errors
+ ///
+ /// This can fail if:
+ /// * The steam client isn't running
+ /// * The app ID of the game couldn't be determined.
+ ///
+ /// If the game isn't being run through steam this can be provided by
+ /// placing a `steam_appid.txt` with the ID inside in the current
+ /// working directory
+ /// * The game isn't running on the same user/level as the steam client
+ /// * The user doesn't own a license for the game.
+ /// * The app ID isn't completely set up.
+ pub fn init(
+ ip: Ipv4Addr, steam_port: u16,
+ game_port: u16, query_port: u16,
+ server_mode: ServerMode, version: &str,
+ ) -> SResult<Server> {
+ unsafe {
+ let version = CString::new(version).unwrap();
+ let raw_ip: u32 = ip.into();
+ let server_mode = match server_mode {
+ ServerMode::NoAuthentication => sys::ServerMode::NoAuthentication,
+ ServerMode::Authentication => sys::ServerMode::Authentication,
+ ServerMode::AuthenticationAndSecure => sys::ServerMode::AuthenticationAndSecure,
+ };
+ if sys::steam_rust_game_server_init(
+ raw_ip, steam_port,
+ game_port, query_port,
+ server_mode,
+ version.as_ptr() as *const _,
+ ) == 0 {
+ return Err(SteamError::InitFailed);
+ }
+ let server_raw = sys::steam_rust_get_server();
+ let server = Arc::new(Inner {
+ _manager: ServerManager { _priv: () },
+ callbacks: Mutex::new(Callbacks {
+ callbacks: Vec::new(),
+ call_results: HashMap::new(),
+ }),
+ });
+ Ok(Server {
+ inner: server,
+ server: server_raw,
+ })
+ }
+ }
+ /// Runs any currently pending callbacks
+ ///
+ /// This runs all currently pending callbacks on the current
+ /// thread.
+ ///
+ /// This should be called frequently (e.g. once per a frame)
+ /// in order to reduce the latency between recieving events.
+ pub fn run_callbacks(&self) {
+ unsafe {
+ sys::SteamGameServer_RunCallbacks();
+ }
+ }
+
+ /// Registers the passed function as a callback for the
+ /// given type.
+ ///
+ /// The callback will be run on the thread that `run_callbacks`
+ /// is called when the event arrives.
+ pub fn register_callback<C, F>(&self, f: F)
+ where C: Callback,
+ F: FnMut(C) + 'static + Send + Sync
+ {
+ unsafe {
+ register_callback(&self.inner, f, true);
+ }
+ }
+
+ /// Returns the steam id of the current server
+ pub fn steam_id(&self) -> SteamId {
+ unsafe {
+ SteamId(sys::SteamAPI_ISteamGameServer_GetSteamID(self.server))
+ }
+ }
+
+ /// Retrieve an authentication session ticket that can be sent
+ /// to an entity that wishes to verify you.
+ ///
+ /// This ticket should not be reused.
+ ///
+ /// When creating ticket for use by the web API you should wait
+ /// for the `AuthSessionTicketResponse` event before trying to
+ /// use the ticket.
+ ///
+ /// When the multiplayer session terminates you must call
+ /// `cancel_authentication_ticket`
+ pub fn authentication_session_ticket(&self) -> (AuthTicket, Vec<u8>) {
+ unsafe {
+ let mut ticket = vec![0; 1024];
+ let mut ticket_len = 0;
+ let auth_ticket = sys::SteamAPI_ISteamGameServer_GetAuthSessionTicket(self.server, ticket.as_mut_ptr() as *mut _, 1024, &mut ticket_len);
+ ticket.truncate(ticket_len as usize);
+ (AuthTicket(auth_ticket), ticket)
+ }
+ }
+
+ /// Cancels an authentication session ticket received from
+ /// `authentication_session_ticket`.
+ ///
+ /// This should be called when you are no longer playing with
+ /// the specified entity.
+ pub fn cancel_authentication_ticket(&self, ticket: AuthTicket) {
+ unsafe {
+ sys::SteamAPI_ISteamGameServer_CancelAuthTicket(self.server, ticket.0);
+ }
+ }
+
+ /// Authenticate the ticket from the steam ID to make sure it is
+ /// valid and not reused.
+ ///
+ /// A `ValidateAuthTicketResponse` callback will be fired if
+ /// the entity goes offline or cancels the ticket.
+ ///
+ /// When the multiplayer session terminates you must call
+ /// `end_authentication_session`
+ pub fn begin_authentication_session(&self, user: SteamId, ticket: &[u8]) -> Result<(), AuthSessionError> {
+ unsafe {
+ let res = sys::SteamAPI_ISteamGameServer_BeginAuthSession(
+ self.server,
+ ticket.as_ptr() as *const _, ticket.len() as _,
+ user.0
+ );
+ Err(match res {
+ sys::BeginAuthSessionResult::Ok => return Ok(()),
+ sys::BeginAuthSessionResult::InvalidTicket => AuthSessionError::InvalidTicket,
+ sys::BeginAuthSessionResult::DuplicateRequest => AuthSessionError::DuplicateRequest,
+ sys::BeginAuthSessionResult::InvalidVersion => AuthSessionError::InvalidVersion,
+ sys::BeginAuthSessionResult::GameMismatch => AuthSessionError::GameMismatch,
+ sys::BeginAuthSessionResult::ExpiredTicket => AuthSessionError::ExpiredTicket,
+ })
+ }
+ }
+
+ /// Ends an authentication session that was started with
+ /// `begin_authentication_session`.
+ ///
+ /// This should be called when you are no longer playing with
+ /// the specified entity.
+ pub fn end_authentication_session(&self, user: SteamId) {
+ unsafe {
+ sys::SteamAPI_ISteamGameServer_EndAuthSession(self.server, user.0);
+ }
+ }
+
+ /// Sets the game product identifier.
+ ///
+ /// Used by the master server for version checking. Required
+ /// field but it will go away eventually.
+ pub fn set_product(&self, product: &str) {
+ unsafe {
+ let product = CString::new(product).unwrap();
+ sys::SteamAPI_ISteamGameServer_SetProduct(self.server, product.as_ptr() as *const _);
+ }
+ }
+
+ /// Sets the game description.
+ ///
+ /// Displayed in the steam server browser (for now). Required
+ /// field but it will go away eventually.
+ pub fn set_game_description(&self, desc: &str) {
+ unsafe {
+ let desc = CString::new(desc).unwrap();
+ sys::SteamAPI_ISteamGameServer_SetGameDescription(self.server, desc.as_ptr() as *const _);
+ }
+ }
+
+ /// Sets whether this server is dedicated or a listen server.
+ pub fn set_dedicated_server(&self, dedicated: bool) {
+ unsafe {
+ sys::SteamAPI_ISteamGameServer_SetDedicatedServer(self.server, dedicated as u8);
+ }
+ }
+
+ /// Login to a generic anonymous account
+ pub fn log_on_anonymous(&self) {
+ unsafe {
+ sys::SteamAPI_ISteamGameServer_LogOnAnonymous(self.server);
+ }
+ }
+
+ /* TODO: Buggy currently?
+ /// Returns an accessor to the steam apps interface
+ pub fn apps(&self) -> Apps<ServerManager> {
+ unsafe {
+ let apps = sys::steam_rust_get_server_apps();
+ debug_assert!(!apps.is_null());
+ Apps {
+ apps: apps,
+ _inner: self.inner.clone(),
+ }
+ }
+ }
+ */
+}
+
+#[test]
+fn test() {
+ let server = Server::init(
+ [127, 0, 0, 1].into(),
+ 23333, 23334, 23335,
+ ServerMode::Authentication, "0.0.1"
+ ).unwrap();
+
+ println!("{:?}", server.steam_id());
+
+ server.set_product("steamworks-rs test");
+ server.set_game_description("basic server test");
+ server.set_dedicated_server(true);
+ server.log_on_anonymous();
+
+ println!("{:?}", server.steam_id());
+
+ server.register_callback(|v: AuthSessionTicketResponse| println!("{:?}", v));
+ server.register_callback(|v: ValidateAuthTicketResponse| println!("{:?}", v));
+
+ let id = server.steam_id();
+ let (auth, ticket) = server.authentication_session_ticket();
+
+ println!("{:?}", server.begin_authentication_session(id, &ticket));
+
+ for _ in 0 .. 20 {
+ server.run_callbacks();
+ ::std::thread::sleep(::std::time::Duration::from_millis(50));
+ }
+
+ println!("END");
+
+ server.cancel_authentication_ticket(auth);
+
+ for _ in 0 .. 20 {
+ server.run_callbacks();
+ ::std::thread::sleep(::std::time::Duration::from_millis(50));
+ }
+
+ server.end_authentication_session(id);
+}
+
+
+/// Manages keeping the steam api active for servers
+pub struct ServerManager {
+ _priv: (),
+}
+
+impl Drop for ServerManager {
+ fn drop(&mut self) {
+ unsafe {
+ sys::SteamGameServer_Shutdown();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/user.rs b/src/user.rs
index 85a9795..3babc18 100644
--- a/src/user.rs
+++ b/src/user.rs
@@ -138,7 +138,7 @@ fn test() {
/// A handle for an authentication ticket that can be used to cancel
/// it.
#[derive(Debug)]
-pub struct AuthTicket(sys::HAuthTicket);
+pub struct AuthTicket(pub(crate) sys::HAuthTicket);
/// Called when generating a authentication session ticket.
///
diff --git a/steamworks-sys/Cargo.toml b/steamworks-sys/Cargo.toml
index db3af3d..d3b22cb 100644
--- a/steamworks-sys/Cargo.toml
+++ b/steamworks-sys/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "steamworks-sys"
-version = "0.1.1"
+version = "0.2.0"
authors = ["Thinkofname"]
build = "build.rs"
description = "Provides raw bindings to the steamworks sdk"
diff --git a/steamworks-sys/src/lib.cpp b/steamworks-sys/src/lib.cpp
index 75c399a..bed54e6 100644
--- a/steamworks-sys/src/lib.cpp
+++ b/steamworks-sys/src/lib.cpp
@@ -1,12 +1,17 @@
#include <steam_api.h>
+#include <steam_gameserver.h>
+#include <stdint.h>
class RustSteamCallback final : CCallbackBase {
public:
RustSteamCallback(int parameter_size, void *userdata,
void (*run_func)(void *, void *), void (*dealloc)(void *),
- int callback_id)
+ int callback_id, int game_server)
: parameter_size(parameter_size), userdata(userdata), run_func(run_func),
dealloc(dealloc) {
+ if (game_server) {
+ m_nCallbackFlags |= k_ECallbackFlagsGameServer;
+ }
SteamAPI_RegisterCallback(this, callback_id);
}
~RustSteamCallback() {
@@ -31,9 +36,11 @@ extern "C" void *register_rust_steam_callback(int parameter_size,
void *userdata,
void (*run_func)(void *, void *),
void (*dealloc)(void *),
- int callback_id) {
+ int callback_id,
+ int game_server
+ ) {
return new RustSteamCallback(parameter_size, userdata, run_func, dealloc,
- callback_id);
+ callback_id, game_server);
}
extern "C" void unregister_rust_steam_callback(void *ty) {
@@ -93,6 +100,13 @@ extern "C" void unregister_rust_steam_call_result(void *ty) {
delete cb;
}
+extern "C" int steam_rust_game_server_init(
+ uint32_t ip, uint16_t steam_port, uint16_t game_port,
+ uint16_t query_port, EServerMode server_mode, const char* version
+) {
+ return SteamGameServer_Init(ip, steam_port, game_port, query_port, server_mode, version);
+}
+
extern "C" ISteamClient *steam_rust_get_client() { return SteamClient(); }
extern "C" ISteamMatchmaking *steam_rust_get_matchmaking() {
return SteamMatchmaking();
@@ -100,4 +114,6 @@ extern "C" ISteamMatchmaking *steam_rust_get_matchmaking() {
extern "C" ISteamUtils *steam_rust_get_utils() { return SteamUtils(); }
extern "C" ISteamApps *steam_rust_get_apps() { return SteamApps(); }
extern "C" ISteamFriends *steam_rust_get_friends() { return SteamFriends(); }
-extern "C" ISteamUser *steam_rust_get_user() { return SteamUser(); } \ No newline at end of file
+extern "C" ISteamUser *steam_rust_get_user() { return SteamUser(); }
+extern "C" ISteamGameServer *steam_rust_get_server() { return SteamGameServer(); }
+extern "C" ISteamApps *steam_rust_get_server_apps() { return SteamGameServerApps(); } \ No newline at end of file
diff --git a/steamworks-sys/src/lib.rs b/steamworks-sys/src/lib.rs
index e84848b..42b5049 100644
--- a/steamworks-sys/src/lib.rs
+++ b/steamworks-sys/src/lib.rs
@@ -20,6 +20,8 @@ pub struct ISteamFriends(c_void);
pub struct ISteamMatchmaking(c_void);
#[repr(C)]
pub struct ISteamUser(c_void);
+#[repr(C)]
+pub struct ISteamGameServer(c_void);
pub type HSteamPipe = i32;
pub type HSteamUser = i32;
@@ -110,6 +112,14 @@ pub enum AuthSessionResponse {
}
#[repr(C)]
+pub enum ServerMode {
+ Invalid = 0,
+ NoAuthentication = 1,
+ Authentication = 2,
+ AuthenticationAndSecure = 3,
+}
+
+#[repr(C)]
#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum SResult {
Ok = 1,
@@ -235,6 +245,7 @@ extern "C" {
run_func: extern "C" fn (*mut c_void, *mut c_void),
dealloc: extern "C" fn (*mut c_void),
callback_id: c_int,
+ game_server: c_int,
) -> *mut c_void;
pub fn unregister_rust_steam_callback(
ty: *mut c_void,
@@ -257,12 +268,19 @@ extern "C" {
pub fn steam_rust_get_apps() -> *mut ISteamApps;
pub fn steam_rust_get_friends() -> *mut ISteamFriends;
pub fn steam_rust_get_user() -> *mut ISteamUser;
+ pub fn steam_rust_get_server() -> *mut ISteamGameServer;
+ pub fn steam_rust_get_server_apps() -> *mut ISteamApps;
+
+ pub fn steam_rust_game_server_init(ip: u32, steam_port: u16, game_port: u16, query_port: u16, server_mode: ServerMode, version: *const c_char) -> c_int;
//
pub fn SteamAPI_Init() -> u8;
pub fn SteamAPI_Shutdown();
pub fn SteamAPI_RunCallbacks();
+ pub fn SteamGameServer_Shutdown();
+ pub fn SteamGameServer_RunCallbacks();
+
pub fn SteamAPI_ISteamClient_CreateSteamPipe(instance: *mut ISteamClient) -> HSteamPipe;
pub fn SteamAPI_ISteamClient_BReleaseSteamPipe(instance: *mut ISteamClient, pipe: HSteamPipe) -> u8;
pub fn SteamAPI_ISteamClient_ConnectToGlobalUser(instance: *mut ISteamClient, pipe: HSteamPipe) -> HSteamUser;
@@ -303,4 +321,14 @@ extern "C" {
pub fn SteamAPI_ISteamUser_BeginAuthSession(instance: *mut ISteamUser, ticket: *const c_void, ticket_size: *mut u32, steam_id: u64) -> BeginAuthSessionResult;
pub fn SteamAPI_ISteamUser_EndAuthSession(instance: *mut ISteamUser, steam_id: u64);
pub fn SteamAPI_ISteamUser_CancelAuthTicket(instance: *mut ISteamUser, auth_ticket: HAuthTicket);
+
+ pub fn SteamAPI_ISteamGameServer_LogOnAnonymous(instance: *mut ISteamGameServer);
+ pub fn SteamAPI_ISteamGameServer_SetProduct(instance: *mut ISteamGameServer, product: *const c_char);
+ pub fn SteamAPI_ISteamGameServer_SetGameDescription(instance: *mut ISteamGameServer, description: *const c_char);
+ pub fn SteamAPI_ISteamGameServer_SetDedicatedServer(instance: *mut ISteamGameServer, dedicated: u8);
+ pub fn SteamAPI_ISteamGameServer_GetSteamID(instance: *mut ISteamGameServer) -> u64;
+ pub fn SteamAPI_ISteamGameServer_GetAuthSessionTicket(instance: *mut ISteamGameServer, ticket: *mut c_void, max_ticket: c_int, ticket_size: *mut u32) -> HAuthTicket;
+ pub fn SteamAPI_ISteamGameServer_BeginAuthSession(instance: *mut ISteamGameServer, ticket: *const c_void, ticket_size: *mut u32, steam_id: u64) -> BeginAuthSessionResult;
+ pub fn SteamAPI_ISteamGameServer_EndAuthSession(instance: *mut ISteamGameServer, steam_id: u64);
+ pub fn SteamAPI_ISteamGameServer_CancelAuthTicket(instance: *mut ISteamGameServer, auth_ticket: HAuthTicket);
}