use crate::networking_types::{
NetConnectionInfo, NetConnectionRealTimeInfo, NetworkingConnectionState, NetworkingIdentity,
NetworkingMessage, SendFlags,
};
use crate::{register_callback, Callback, Inner, SteamError};
use std::ffi::c_void;
use std::sync::{Arc, Weak};
use steamworks_sys as sys;
pub struct NetworkingMessages {
pub(crate) net: *mut sys::ISteamNetworkingMessages,
pub(crate) inner: Arc<Inner>,
}
unsafe impl Sync for NetworkingMessages {}
unsafe impl Send for NetworkingMessages {}
impl NetworkingMessages {
pub fn send_message_to_user(
&self,
user: NetworkingIdentity,
send_type: SendFlags,
data: &[u8],
channel: u32,
) -> Result<(), SteamError> {
let result = unsafe {
sys::SteamAPI_ISteamNetworkingMessages_SendMessageToUser(
self.net,
user.as_ptr(),
data.as_ptr().cast(),
data.len() as u32,
send_type.bits(),
channel as i32,
)
};
if result == sys::EResult::k_EResultOK {
return Ok(());
}
Err(result.into())
}
pub fn receive_messages_on_channel(
&self,
channel: u32,
batch_size: usize,
) -> Vec<NetworkingMessage> {
let mut buffer = Vec::with_capacity(batch_size);
unsafe {
let message_count = sys::SteamAPI_ISteamNetworkingMessages_ReceiveMessagesOnChannel(
self.net,
channel as i32,
buffer.as_mut_ptr(),
batch_size as _,
);
buffer.set_len(message_count as usize);
}
buffer
.into_iter()
.map(|x| NetworkingMessage {
message: x,
_inner: self.inner.clone(),
})
.collect()
}
pub fn session_request_callback(
&self,
mut callback: impl FnMut(SessionRequest) + Send + 'static,
) {
let builder = SessionRequestBuilder {
message: self.net,
inner: Arc::downgrade(&self.inner),
};
let call_handle = unsafe {
register_callback(
&self.inner,
move |request: NetworkingMessagesSessionRequest| {
if let Some(request) = builder.build_request(request.remote) {
callback(request);
}
},
)
};
std::mem::forget(call_handle);
}
pub fn session_failed_callback(
&self,
mut callback: impl FnMut(NetConnectionInfo) + Send + 'static,
) {
let call_handle = unsafe {
register_callback(
&self.inner,
move |failed: NetworkingMessagesSessionFailed| {
callback(failed.info);
},
)
};
std::mem::forget(call_handle);
}
pub fn get_session_connection_info(
&self,
identity_remote: &NetworkingIdentity,
) -> (
NetworkingConnectionState,
Option<NetConnectionInfo>,
Option<NetConnectionRealTimeInfo>,
) {
let mut connection_info: sys::SteamNetConnectionInfo_t = unsafe { std::mem::zeroed() };
let mut quick_status: sys::SteamNetConnectionRealTimeStatus_t =
unsafe { std::mem::zeroed() };
let state = unsafe {
sys::SteamAPI_ISteamNetworkingMessages_GetSessionConnectionInfo(
self.net,
identity_remote.as_ptr(),
&mut connection_info,
&mut quick_status,
)
};
let state = match NetworkingConnectionState::try_from(state) {
Ok(state) => state,
Err(_) => NetworkingConnectionState::None,
};
let connection_info = if state != NetworkingConnectionState::None {
Some(NetConnectionInfo {
inner: connection_info,
})
} else {
None
};
let quick_status = if state != NetworkingConnectionState::None {
Some(NetConnectionRealTimeInfo {
inner: quick_status,
})
} else {
None
};
(state, connection_info, quick_status)
}
}
struct SessionRequestBuilder {
message: *mut sys::ISteamNetworkingMessages,
inner: Weak<Inner>,
}
unsafe impl Send for SessionRequestBuilder {}
unsafe impl Sync for SessionRequestBuilder {}
impl SessionRequestBuilder {
pub fn build_request(&self, remote: NetworkingIdentity) -> Option<SessionRequest> {
self.inner.upgrade().map(|inner| SessionRequest {
remote,
messages: self.message,
accepted: false,
_inner: inner,
})
}
}
#[derive(Debug)]
pub struct NetworkingMessagesSessionRequest {
remote: NetworkingIdentity,
}
impl_callback!(cb: SteamNetworkingMessagesSessionRequest_t => NetworkingMessagesSessionRequest {
let remote = cb.m_identityRemote.into();
Self { remote }
});
#[derive(Debug)]
pub struct NetworkingMessagesSessionFailed {
pub info: NetConnectionInfo,
}
impl_callback!(cb: SteamNetworkingMessagesSessionFailed_t => NetworkingMessagesSessionFailed {
let remote = cb.m_info.into();
Self { info: remote }
});
pub struct SessionRequest {
remote: NetworkingIdentity,
messages: *mut sys::ISteamNetworkingMessages,
accepted: bool,
_inner: Arc<Inner>,
}
unsafe impl Sync for SessionRequest {}
unsafe impl Send for SessionRequest {}
impl SessionRequest {
pub fn remote(&self) -> &NetworkingIdentity {
&self.remote
}
pub fn accept(mut self) -> bool {
self.accepted = true;
unsafe {
return sys::SteamAPI_ISteamNetworkingMessages_AcceptSessionWithUser(
self.messages,
self.remote.as_ptr(),
);
}
}
pub fn reject(mut self) {
self.reject_inner();
}
fn reject_inner(&mut self) {
unsafe {
sys::SteamAPI_ISteamNetworkingMessages_CloseSessionWithUser(
self.messages,
self.remote.as_ptr(),
);
}
}
}
impl Drop for SessionRequest {
fn drop(&mut self) {
if !self.accepted {
self.reject_inner();
}
}
}