diff options
| author | Matthew Collins <[email protected]> | 2018-05-05 17:07:07 +0100 |
|---|---|---|
| committer | Matthew Collins <[email protected]> | 2018-05-05 17:07:07 +0100 |
| commit | f3accef78c9de43727d7eae93f190952e486a7c8 (patch) | |
| tree | 677a7d0cffe960fa159871485a759f230fcff44b | |
| parent | Rework how the sys crate is generated (diff) | |
| download | steamworks-rs-f3accef78c9de43727d7eae93f190952e486a7c8.tar.xz steamworks-rs-f3accef78c9de43727d7eae93f190952e486a7c8.zip | |
Only allow run_callbacks to be run on a single thread at any time
| -rw-r--r-- | src/callback.rs | 18 | ||||
| -rw-r--r-- | src/lib.rs | 50 | ||||
| -rw-r--r-- | src/matchmaking.rs | 8 | ||||
| -rw-r--r-- | src/server.rs | 35 | ||||
| -rw-r--r-- | src/user.rs | 6 | ||||
| -rw-r--r-- | steamworks-sys/build.rs | 2 |
6 files changed, 73 insertions, 46 deletions
diff --git a/src/callback.rs b/src/callback.rs index 6808a45..18c1db0 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -27,11 +27,11 @@ fn print_err(err: Box<Any>) { pub(crate) unsafe fn register_callback<C, F, Manager>(inner: &Arc<Inner<Manager>>, f: F, game_server: bool) where C: Callback, - F: FnMut(C) + Send + Sync + 'static + F: FnMut(C) + Send + 'static { unsafe extern "C" fn run<C, F>(_: *mut c_void, userdata: *mut c_void, param: *mut c_void) where C: Callback, - F: FnMut(C) + Send + Sync + 'static + F: FnMut(C) + Send + 'static { let func: &mut F = &mut *(userdata as *mut F); let param = C::from_raw(param); @@ -40,14 +40,14 @@ pub(crate) unsafe fn register_callback<C, F, Manager>(inner: &Arc<Inner<Manager> unsafe extern "C" fn run_extra<C, F>(cb: *mut c_void, userdata: *mut c_void, param: *mut c_void, _: u8, _: sys::SteamAPICall) where C: Callback, - F: FnMut(C) + Send + Sync + 'static + F: FnMut(C) + Send + 'static { run::<C, F>(cb, userdata, param); } unsafe extern "C" fn dealloc<C, F>(cb: *mut c_void, userdata: *mut c_void) where C: Callback, - F: FnMut(C) + Send + Sync + 'static + F: FnMut(C) + Send + 'static { sys::SteamAPI_UnregisterCallback(cb); @@ -73,7 +73,7 @@ pub(crate) unsafe fn register_callback<C, F, Manager>(inner: &Arc<Inner<Manager> } 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 + where F: for <'a> FnMut(&'a C, bool) + 'static + Send { struct CallData<F, Manager> { func: F, @@ -82,7 +82,7 @@ pub(crate) unsafe fn register_call_result<C, F, Manager>(inner: &Arc<Inner<Manag } unsafe extern "C" fn run<C, F, Manager>(cb: *mut c_void, userdata: *mut c_void, param: *mut c_void) - where F: for<'a> FnMut(&'a C, bool) + Send + Sync + 'static + where F: for<'a> FnMut(&'a C, bool) + Send + 'static { let data: &mut CallData<F, Manager> = &mut *(userdata as *mut CallData<F, Manager>); #[cfg(debug_assertions)] @@ -105,7 +105,7 @@ pub(crate) unsafe fn register_call_result<C, F, Manager>(inner: &Arc<Inner<Manag } unsafe extern "C" fn run_extra<C, F, Manager>(cb: *mut c_void, userdata: *mut c_void, param: *mut c_void, io_error: u8, api_call: sys::SteamAPICall) - where F: for<'a> FnMut(&'a C, bool) + Send + Sync + 'static + where F: for<'a> FnMut(&'a C, bool) + Send + 'static { let data: &mut CallData<F, Manager> = &mut *(userdata as *mut CallData<F, Manager>); @@ -129,8 +129,8 @@ pub(crate) unsafe fn register_call_result<C, F, Manager>(inner: &Arc<Inner<Manag } } - unsafe extern "C" fn dealloc<C, F, Manager>(cb: *mut c_void, userdata: *mut c_void) - where F: for <'a> FnMut(&'a C, bool) + Send + Sync + 'static + unsafe extern "C" fn dealloc<C, F, Manager>(_cb: *mut c_void, userdata: *mut c_void) + where F: for <'a> FnMut(&'a C, bool) + Send + 'static { let data: Box<CallData<F, Manager>> = Box::from_raw(userdata as _); @@ -29,6 +29,7 @@ use std::ffi::{CString, CStr}; use std::fmt::{ Debug, Formatter, self }; +use std::marker::PhantomData; use std::collections::HashMap; pub type SResult<T> = Result<T, SteamError>; @@ -57,6 +58,13 @@ impl <Manager> Clone for Client<Manager> { } } +/// Allows access parts of the steam api that can only be called +/// on a single thread at any given time. +pub struct SingleClient<Manager = ClientManager> { + _inner: Arc<Inner<Manager>>, + _not_sync: PhantomData<*mut ()>, +} + struct Inner<Manager> { _manager: Manager, callbacks: Mutex<Callbacks>, @@ -71,6 +79,7 @@ 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> {} +unsafe impl <Manager: Send + Sync> Send for SingleClient<Manager> {} /// Returns true if the app wasn't launched through steam and /// begins relaunching it, the app should exit as soon as possible. @@ -83,6 +92,9 @@ pub fn restart_app_if_necessary(app_id: AppId) -> bool { } } +fn static_assert_send<T: Send>() {} +fn static_assert_sync<T>() where T: Sync {} + impl Client<ClientManager> { /// Attempts to initialize the steamworks api and returns /// a client to access the rest of the api. @@ -101,7 +113,10 @@ impl Client<ClientManager> { /// * 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() -> SResult<Client<ClientManager>> { + pub fn init() -> SResult<(Client<ClientManager>, SingleClient<ClientManager>)> { + static_assert_send::<Client<ClientManager>>(); + static_assert_sync::<Client<ClientManager>>(); + static_assert_send::<SingleClient<ClientManager>>(); unsafe { if sys::SteamAPI_Init() == 0 { return Err(SteamError::InitFailed); @@ -114,15 +129,17 @@ impl Client<ClientManager> { call_results: HashMap::new(), }), }); - Ok(Client { - inner: client, + Ok((Client { + inner: client.clone(), client: raw_client, - }) + }, SingleClient { + _inner: client, + _not_sync: PhantomData, + })) } } } - -impl <Manager> Client<Manager> { +impl <M> SingleClient<M> where M: Manager{ /// Runs any currently pending callbacks /// /// This runs all currently pending callbacks on the current @@ -132,10 +149,12 @@ impl <Manager> Client<Manager> { /// in order to reduce the latency between recieving events. pub fn run_callbacks(&self) { unsafe { - sys::SteamAPI_RunCallbacks(); + M::run_callbacks(); } } +} +impl <Manager> Client<Manager> { /// Registers the passed function as a callback for the /// given type. /// @@ -143,7 +162,7 @@ impl <Manager> Client<Manager> { /// is called when the event arrives. pub fn register_callback<C, F>(&self, f: F) where C: Callback, - F: FnMut(C) + 'static + Send + Sync + F: FnMut(C) + 'static + Send { unsafe { register_callback(&self.inner, f, false); @@ -229,11 +248,22 @@ impl <Manager> Drop for Inner<Manager> { } } +/// Used to seperate client and game server modes +pub unsafe trait Manager { + unsafe fn run_callbacks(); +} + /// Manages keeping the steam api active for clients pub struct ClientManager { _priv: (), } +unsafe impl Manager for ClientManager { + unsafe fn run_callbacks() { + sys::SteamAPI_RunCallbacks(); + } +} + impl Drop for ClientManager { fn drop(&mut self) { unsafe { @@ -269,7 +299,7 @@ mod tests { use super::*; #[test] fn basic_test() { - let client = Client::init().unwrap(); + let (client, single) = Client::init().unwrap(); client.register_callback(|p: PersonaStateChange| { println!("Got callback: {:?}", p); @@ -301,7 +331,7 @@ mod tests { friends.request_user_information(SteamId(76561198174976054), true); for _ in 0 .. 50 { - client.run_callbacks(); + single.run_callbacks(); ::std::thread::sleep(::std::time::Duration::from_millis(100)); } } diff --git a/src/matchmaking.rs b/src/matchmaking.rs index 177cb8e..f219381 100644 --- a/src/matchmaking.rs +++ b/src/matchmaking.rs @@ -54,7 +54,7 @@ impl <Manager> Matchmaking<Manager> { /// * `LobbyEnter` /// * `LobbyCreated` pub fn create_lobby<F>(&self, ty: LobbyType, max_members: u32, mut cb: F) - where F: FnMut(Result<LobbyId, SteamError>) + 'static + Send + Sync + where F: FnMut(Result<LobbyId, SteamError>) + 'static + Send { assert!(max_members <= 250); // Steam API limits unsafe { @@ -81,7 +81,7 @@ impl <Manager> Matchmaking<Manager> { /// Tries to join the lobby with the given ID pub fn join_lobby<F>(&self, lobby: LobbyId, mut cb: F) - where F: FnMut(Result<LobbyId, ()>) + 'static + Send + Sync + where F: FnMut(Result<LobbyId, ()>) + 'static + Send { unsafe { let api_call = sys::SteamAPI_ISteamMatchmaking_JoinLobby(self.mm, lobby.0); @@ -109,7 +109,7 @@ impl <Manager> Matchmaking<Manager> { #[test] fn test_lobby() { - let client = Client::init().unwrap(); + let (client, single) = Client::init().unwrap(); let mm = client.matchmaking(); mm.request_lobby_list(|v| { @@ -120,7 +120,7 @@ fn test_lobby() { }); for _ in 0 .. 100 { - client.run_callbacks(); + single.run_callbacks(); ::std::thread::sleep(::std::time::Duration::from_millis(100)); } }
\ No newline at end of file diff --git a/src/server.rs b/src/server.rs index fc1f6e8..ee67742 100644 --- a/src/server.rs +++ b/src/server.rs @@ -57,7 +57,7 @@ impl Server { ip: Ipv4Addr, steam_port: u16, game_port: u16, query_port: u16, server_mode: ServerMode, version: &str, - ) -> SResult<Server> { + ) -> SResult<(Server, SingleClient<ServerManager>)> { unsafe { let version = CString::new(version).unwrap(); let raw_ip: u32 = ip.into(); @@ -82,22 +82,13 @@ impl Server { call_results: HashMap::new(), }), }); - Ok(Server { - inner: server, + Ok((Server { + inner: server.clone(), 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(); + }, SingleClient { + _inner: server, + _not_sync: PhantomData, + })) } } @@ -245,7 +236,7 @@ impl Server { #[test] fn test() { - let server = Server::init( + let (server, single) = Server::init( [127, 0, 0, 1].into(), 23333, 23334, 23335, ServerMode::Authentication, "0.0.1" @@ -269,7 +260,7 @@ fn test() { println!("{:?}", server.begin_authentication_session(id, &ticket)); for _ in 0 .. 20 { - server.run_callbacks(); + single.run_callbacks(); ::std::thread::sleep(::std::time::Duration::from_millis(50)); } @@ -278,7 +269,7 @@ fn test() { server.cancel_authentication_ticket(auth); for _ in 0 .. 20 { - server.run_callbacks(); + single.run_callbacks(); ::std::thread::sleep(::std::time::Duration::from_millis(50)); } @@ -291,6 +282,12 @@ pub struct ServerManager { _priv: (), } +unsafe impl Manager for ServerManager { + unsafe fn run_callbacks() { + sys::SteamGameServer_RunCallbacks(); + } +} + impl Drop for ServerManager { fn drop(&mut self) { unsafe { diff --git a/src/user.rs b/src/user.rs index b21ac5f..8298656 100644 --- a/src/user.rs +++ b/src/user.rs @@ -108,7 +108,7 @@ pub enum AuthSessionError { #[test] fn test() { - let client = Client::init().unwrap(); + let (client, single) = Client::init().unwrap(); let user = client.user(); client.register_callback(|v: AuthSessionTicketResponse| println!("{:?}", v)); @@ -120,7 +120,7 @@ fn test() { println!("{:?}", user.begin_authentication_session(id, &ticket)); for _ in 0 .. 20 { - client.run_callbacks(); + single.run_callbacks(); ::std::thread::sleep(::std::time::Duration::from_millis(50)); } @@ -129,7 +129,7 @@ fn test() { user.cancel_authentication_ticket(auth); for _ in 0 .. 20 { - client.run_callbacks(); + single.run_callbacks(); ::std::thread::sleep(::std::time::Duration::from_millis(50)); } diff --git a/steamworks-sys/build.rs b/steamworks-sys/build.rs index 2aa6c42..dde20e3 100644 --- a/steamworks-sys/build.rs +++ b/steamworks-sys/build.rs @@ -129,7 +129,7 @@ extern "C" { (fty, fty_rust) } - for SteamStruct{struct_: ref ty, ref fields} in &steam_api.structs { + for &SteamStruct{struct_: ref ty, ref fields} in &steam_api.structs { if ty.contains("::") || !ty.ends_with("_t") || fields.iter().any(|v| v.fieldtype.contains('[')) || ty.chars().next().map_or(true, |v| v.is_lowercase()) || ty.starts_with("GSStats") |