1use crossbeam_queue::SegQueue;
71pub use gns_sys as sys;
72use std::sync::atomic::{AtomicI64, Ordering};
73use std::{
74 collections::HashMap,
75 ffi::{c_void, CStr, CString},
76 marker::PhantomData,
77 mem::MaybeUninit,
78 net::{IpAddr, Ipv4Addr, Ipv6Addr},
79 sync::{Arc, Mutex, OnceLock, RwLock, Weak},
80 time::Duration,
81};
82use sys::*;
83
84#[inline]
85fn get_interface() -> *mut ISteamNetworkingSockets {
86 unsafe { SteamAPI_SteamNetworkingSockets_v009() }
87}
88
89#[inline]
90fn get_utils() -> *mut ISteamNetworkingUtils {
91 unsafe { SteamAPI_SteamNetworkingUtils_v003() }
92}
93
94pub type GnsMessageNumber = u64;
96
97#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
102pub enum GnsError {
103 #[error("GameNetworkingSockets_Init failed: {0}")]
104 Init(String),
105 #[error("listen failed: invalid handle")]
106 Listen,
107 #[error("connect failed: invalid handle")]
108 Connect,
109 #[error("receive failed: invalid connection or poll group handle")]
110 Receive,
111 #[error("accept failed: could not set connection poll group")]
112 Accept,
113 #[error("close failed: invalid connection handle")]
114 Close,
115 #[error("steam api: {0:?}")]
116 Api(EResult),
117 #[error("config: {0}")]
118 Config(&'static str),
119}
120
121pub type GnsResult<T> = Result<T, GnsError>;
122
123#[inline]
125fn check(e: EResult) -> GnsResult<()> {
126 match e {
127 EResult::k_EResultOK => Ok(()),
128 e => Err(GnsError::Api(e)),
129 }
130}
131
132pub struct GnsGlobal {
138 utils: GnsUtils,
139 next_queue_id: AtomicI64,
140 event_queues: RwLock<HashMap<i64, Weak<SegQueue<GnsConnectionEvent>>>>,
148}
149
150static GNS_GLOBAL: OnceLock<GnsGlobal> = OnceLock::new();
151
152impl Drop for GnsGlobal {
153 #[inline]
154 fn drop(&mut self) {
155 unsafe { GameNetworkingSockets_Kill() }
161 }
162}
163
164impl GnsGlobal {
165 pub fn get() -> GnsResult<&'static Self> {
175 if let Some(g) = GNS_GLOBAL.get() {
177 return Ok(g);
178 }
179 static INIT_LOCK: Mutex<()> = Mutex::new(());
181 let _guard = INIT_LOCK.lock().unwrap();
182 if let Some(g) = GNS_GLOBAL.get() {
183 return Ok(g);
184 }
185 unsafe {
186 let mut error: SteamDatagramErrMsg = MaybeUninit::zeroed().assume_init();
187 if !GameNetworkingSockets_Init(core::ptr::null(), &mut error) {
188 return Err(GnsError::Init(
189 CStr::from_ptr(error.as_ptr())
190 .to_str()
191 .unwrap_or("")
192 .to_owned(),
193 ));
194 }
195 }
196 let _ = GNS_GLOBAL.set(GnsGlobal {
197 utils: GnsUtils(()),
198 next_queue_id: AtomicI64::new(0),
199 event_queues: RwLock::new(HashMap::new()),
200 });
201 Ok(GNS_GLOBAL.get().expect("impossible; qed;"))
202 }
203
204 #[inline]
205 pub fn poll_callbacks(&self) {
206 unsafe {
207 SteamAPI_ISteamNetworkingSockets_RunCallbacks(get_interface());
208 }
209 }
210
211 #[inline]
212 pub fn utils(&self) -> &GnsUtils {
213 &self.utils
214 }
215
216 #[inline]
217 pub fn queue_count(&self) -> usize {
218 self.event_queues.read().unwrap().len()
219 }
220
221 #[inline]
222 fn create_queue(&self) -> (i64, Arc<SegQueue<GnsConnectionEvent>>) {
223 let queue = Arc::new(SegQueue::new());
224 let queue_id = self.next_queue_id.fetch_add(1, Ordering::SeqCst);
225 self.event_queues
226 .write()
227 .unwrap()
228 .insert(queue_id, Arc::downgrade(&queue));
229 (queue_id, queue)
230 }
231}
232
233#[repr(transparent)]
235pub(crate) struct GnsListenSocket(HSteamListenSocket);
236
237#[repr(transparent)]
239pub(crate) struct GnsPollGroup(HSteamNetPollGroup);
240
241pub struct IsCreated;
246
247mod private {
248 pub trait Sealed {}
249 impl Sealed for super::IsServer {}
250 impl Sealed for super::IsClient {}
251}
252
253pub trait IsReady: private::Sealed {
258 fn queue(&self) -> &SegQueue<GnsConnectionEvent>;
260 fn receive(&self, slots: &mut [MaybeUninit<*mut ISteamNetworkingMessage>]) -> GnsResult<usize>;
265}
266
267pub struct IsServer {
273 queue: Arc<SegQueue<GnsConnectionEvent>>,
274 queue_id: i64,
275 global: &'static GnsGlobal,
276 listen_socket: GnsListenSocket,
277 poll_group: GnsPollGroup,
278}
279
280impl Drop for IsServer {
281 #[inline]
282 fn drop(&mut self) {
283 unsafe {
284 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
285 get_interface(),
286 self.listen_socket.0,
287 );
288 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup(get_interface(), self.poll_group.0);
289 }
290 self.global
291 .event_queues
292 .write()
293 .unwrap()
294 .remove(&self.queue_id);
295 }
296}
297
298impl IsReady for IsServer {
299 #[inline]
300 fn queue(&self) -> &SegQueue<GnsConnectionEvent> {
301 &self.queue
302 }
303
304 fn receive(&self, slots: &mut [MaybeUninit<*mut ISteamNetworkingMessage>]) -> GnsResult<usize> {
305 let result = unsafe {
306 SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
307 get_interface(),
308 self.poll_group.0,
309 slots.as_mut_ptr() as _,
310 slots.len() as _,
311 ) as _
312 };
313 if result == usize::MAX {
314 Err(GnsError::Receive)
315 } else {
316 Ok(result)
317 }
318 }
319}
320
321pub struct IsClient {
326 queue: Arc<SegQueue<GnsConnectionEvent>>,
327 queue_id: i64,
328 global: &'static GnsGlobal,
329 connection: GnsConnection,
330}
331
332impl Drop for IsClient {
333 fn drop(&mut self) {
334 unsafe {
335 SteamAPI_ISteamNetworkingSockets_CloseConnection(
336 get_interface(),
337 self.connection.0,
338 0,
339 core::ptr::null(),
340 false,
341 );
342 }
343 self.global
344 .event_queues
345 .write()
346 .unwrap()
347 .remove(&self.queue_id);
348 }
349}
350
351impl IsReady for IsClient {
352 #[inline]
353 fn queue(&self) -> &SegQueue<GnsConnectionEvent> {
354 &self.queue
355 }
356
357 fn receive(&self, slots: &mut [MaybeUninit<*mut ISteamNetworkingMessage>]) -> GnsResult<usize> {
358 let result = unsafe {
359 SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnConnection(
360 get_interface(),
361 self.connection.0,
362 slots.as_mut_ptr() as _,
363 slots.len() as _,
364 ) as _
365 };
366 if result == usize::MAX {
367 Err(GnsError::Receive)
368 } else {
369 Ok(result)
370 }
371 }
372}
373
374pub struct ToReceive(());
375
376pub struct ToSend(());
377
378pub type MessageSlot = MaybeUninit<*mut ISteamNetworkingMessage>;
385
386#[inline]
393unsafe fn take_message(slot: &MessageSlot) -> GnsNetworkMessage<ToReceive> {
394 GnsNetworkMessage(unsafe { slot.assume_init() }, PhantomData)
395}
396
397struct SlotCursor {
403 len: usize,
404 pos: usize,
405}
406
407impl SlotCursor {
408 fn next(&mut self, slots: &[MessageSlot]) -> Option<GnsNetworkMessage<ToReceive>> {
409 if self.pos < self.len {
410 let message = unsafe { take_message(&slots[self.pos]) };
413 self.pos += 1;
414 Some(message)
415 } else {
416 None
417 }
418 }
419
420 #[inline]
421 fn remaining(&self) -> usize {
422 self.len - self.pos
423 }
424
425 fn drain_unconsumed(&mut self, slots: &[MessageSlot]) {
428 for slot in &slots[self.pos..self.len] {
429 drop(unsafe { take_message(slot) });
432 }
433 self.pos = self.len;
434 }
435}
436
437pub struct ReceivedMessages<const K: usize> {
447 slots: [MessageSlot; K],
448 cursor: SlotCursor,
449}
450
451impl<const K: usize> Iterator for ReceivedMessages<K> {
452 type Item = GnsNetworkMessage<ToReceive>;
453
454 #[inline]
455 fn next(&mut self) -> Option<Self::Item> {
456 self.cursor.next(&self.slots)
457 }
458
459 #[inline]
460 fn size_hint(&self) -> (usize, Option<usize>) {
461 let remaining = self.cursor.remaining();
462 (remaining, Some(remaining))
463 }
464}
465
466impl<const K: usize> ExactSizeIterator for ReceivedMessages<K> {}
467
468impl<const K: usize> core::iter::FusedIterator for ReceivedMessages<K> {}
469
470impl<const K: usize> Drop for ReceivedMessages<K> {
471 #[inline]
472 fn drop(&mut self) {
473 self.cursor.drain_unconsumed(&self.slots);
474 }
475}
476
477pub struct ReceivedMessagesInto<'a> {
487 slots: &'a mut [MessageSlot],
488 cursor: SlotCursor,
489}
490
491impl Iterator for ReceivedMessagesInto<'_> {
492 type Item = GnsNetworkMessage<ToReceive>;
493
494 #[inline]
495 fn next(&mut self) -> Option<Self::Item> {
496 self.cursor.next(self.slots)
497 }
498
499 #[inline]
500 fn size_hint(&self) -> (usize, Option<usize>) {
501 let remaining = self.cursor.remaining();
502 (remaining, Some(remaining))
503 }
504}
505
506impl ExactSizeIterator for ReceivedMessagesInto<'_> {}
507
508impl core::iter::FusedIterator for ReceivedMessagesInto<'_> {}
509
510impl Drop for ReceivedMessagesInto<'_> {
511 #[inline]
512 fn drop(&mut self) {
513 self.cursor.drain_unconsumed(self.slots);
514 }
515}
516
517bitflags::bitflags! {
518 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
522 pub struct SendFlags: i32 {
523 const UNRELIABLE = sys::k_nSteamNetworkingSend_Unreliable;
524 const NO_NAGLE = sys::k_nSteamNetworkingSend_NoNagle;
525 const NO_DELAY = sys::k_nSteamNetworkingSend_NoDelay;
526 const RELIABLE = sys::k_nSteamNetworkingSend_Reliable;
527 const USE_CURRENT_THREAD = sys::k_nSteamNetworkingSend_UseCurrentThread;
528 const AUTO_RESTART_BROKEN_SESSION = sys::k_nSteamNetworkingSend_AutoRestartBrokenSession;
529 }
530}
531
532#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
537pub struct GnsLane {
538 pub priority: i32,
539 pub weight: u16,
540}
541
542impl GnsLane {
543 #[inline]
544 pub const fn new(priority: i32, weight: u16) -> Self {
545 Self { priority, weight }
546 }
547}
548
549pub type GnsLaneId = u16;
551
552#[must_use = "Failed/Skipped variants own a message that needs inspection or drop"]
560pub enum SendOutcome {
561 Sent(GnsMessageNumber),
562 Failed(EResult, GnsNetworkMessage<ToSend>),
563 Skipped(GnsNetworkMessage<ToSend>),
564}
565
566pub unsafe trait Payload: Send + 'static {
589 fn into_raw(self) -> (*mut u8, usize);
590 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self;
595}
596
597extern "C" fn free_payload<P: Payload>(msg: *mut ISteamNetworkingMessage) {
603 let ptr = unsafe { (*msg).m_pData } as *mut u8;
604 let len = unsafe { (*msg).m_cbSize } as usize;
605 drop(unsafe { P::from_raw(ptr, len) });
608}
609
610unsafe impl Payload for Box<[u8]> {
611 #[inline]
612 fn into_raw(self) -> (*mut u8, usize) {
613 let len = self.len();
614 let raw = Box::into_raw(self) as *mut u8;
615 (raw, len)
616 }
617 #[inline]
618 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
619 let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
620 unsafe { Box::from_raw(slice) }
621 }
622}
623
624unsafe impl Payload for Vec<u8> {
628 #[inline]
629 fn into_raw(self) -> (*mut u8, usize) {
630 <Box<[u8]> as Payload>::into_raw(self.into_boxed_slice())
631 }
632 #[inline]
633 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
634 unsafe { Vec::from_raw_parts(ptr, len, len) }
635 }
636}
637
638unsafe impl Payload for String {
639 #[inline]
640 fn into_raw(self) -> (*mut u8, usize) {
641 <Vec<u8> as Payload>::into_raw(self.into_bytes())
642 }
643 #[inline]
644 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
645 unsafe { String::from_raw_parts(ptr, len, len) }
646 }
647}
648
649unsafe impl Payload for Arc<[u8]> {
650 #[inline]
651 fn into_raw(self) -> (*mut u8, usize) {
652 let len = self.len();
653 let raw = Arc::into_raw(self) as *const u8 as *mut u8;
654 (raw, len)
655 }
656 #[inline]
657 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
658 let slice = core::ptr::slice_from_raw_parts(ptr as *const u8, len);
659 unsafe { Arc::from_raw(slice) }
660 }
661}
662
663unsafe impl Payload for &'static [u8] {
664 #[inline]
665 fn into_raw(self) -> (*mut u8, usize) {
666 (self.as_ptr() as *mut u8, self.len())
667 }
668 #[inline]
669 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
670 unsafe { core::slice::from_raw_parts(ptr as *const u8, len) }
671 }
672}
673
674unsafe impl Payload for &'static str {
675 #[inline]
676 fn into_raw(self) -> (*mut u8, usize) {
677 (self.as_ptr() as *mut u8, self.len())
678 }
679 #[inline]
680 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
681 let bytes = unsafe { core::slice::from_raw_parts(ptr as *const u8, len) };
682 unsafe { core::str::from_utf8_unchecked(bytes) }
683 }
684}
685
686#[repr(transparent)]
692pub struct GnsNetworkMessage<T>(*mut ISteamNetworkingMessage, PhantomData<T>);
693
694impl<T> Drop for GnsNetworkMessage<T> {
695 #[inline]
696 fn drop(&mut self) {
697 if !self.0.is_null() {
698 unsafe {
699 SteamAPI_SteamNetworkingMessage_t_Release(self.0);
700 }
701 }
702 }
703}
704
705impl<T> GnsNetworkMessage<T> {
706 #[inline]
714 pub unsafe fn into_inner(self) -> *mut ISteamNetworkingMessage {
715 core::mem::ManuallyDrop::new(self).0
719 }
720
721 #[inline]
722 pub fn payload(&self) -> &[u8] {
723 unsafe {
724 core::slice::from_raw_parts((*self.0).m_pData as *const u8, (*self.0).m_cbSize as _)
725 }
726 }
727
728 #[inline]
729 pub fn message_number(&self) -> u64 {
730 unsafe { (*self.0).m_nMessageNumber as _ }
731 }
732
733 #[inline]
734 pub fn lane(&self) -> GnsLaneId {
735 unsafe { (*self.0).m_idxLane }
736 }
737
738 #[inline]
739 pub fn flags(&self) -> SendFlags {
740 SendFlags::from_bits_retain(unsafe { (*self.0).m_nFlags })
741 }
742
743 #[inline]
744 pub fn user_data(&self) -> u64 {
745 unsafe { (*self.0).m_nUserData as _ }
746 }
747
748 #[inline]
749 pub fn connection(&self) -> GnsConnection {
750 GnsConnection(unsafe { (*self.0).m_conn })
751 }
752
753 #[inline]
754 pub fn connection_user_data(&self) -> u64 {
755 unsafe { (*self.0).m_nConnUserData as _ }
756 }
757}
758
759impl GnsNetworkMessage<ToSend> {
760 #[inline]
761 fn new<P: Payload>(
762 ptr: *mut ISteamNetworkingMessage,
763 conn: GnsConnection,
764 flags: SendFlags,
765 payload: P,
766 ) -> Self {
767 let (data_ptr, len) = payload.into_raw();
768 unsafe {
769 (*ptr).m_pData = data_ptr as *mut c_void;
770 (*ptr).m_cbSize = len as i32;
771 (*ptr).m_pfnFreeData = Some(free_payload::<P>);
772 }
773 GnsNetworkMessage(ptr, PhantomData)
774 .set_flags(flags)
775 .set_connection(conn)
776 }
777
778 #[inline]
779 pub fn set_connection(self, GnsConnection(conn): GnsConnection) -> Self {
780 unsafe { (*self.0).m_conn = conn }
781 self
782 }
783
784 #[inline]
785 pub fn set_lane(self, lane: GnsLaneId) -> Self {
786 unsafe { (*self.0).m_idxLane = lane }
787 self
788 }
789
790 #[inline]
791 pub fn set_flags(self, flags: SendFlags) -> Self {
792 unsafe { (*self.0).m_nFlags = flags.bits() as _ }
793 self
794 }
795
796 #[inline]
797 pub fn set_user_data(self, userdata: u64) -> Self {
798 unsafe { (*self.0).m_nUserData = userdata as _ }
799 self
800 }
801}
802
803#[repr(transparent)]
804#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
805pub struct GnsConnection(HSteamNetConnection);
806
807impl GnsConnection {
808 #[inline]
813 pub const fn from_raw(handle: HSteamNetConnection) -> Self {
814 Self(handle)
815 }
816
817 #[inline]
819 pub fn is_valid(self) -> bool {
820 self.0 != k_HSteamNetConnection_Invalid
821 }
822}
823
824#[derive(Default, Copy, Clone)]
825pub struct GnsConnectionInfo(SteamNetConnectionInfo_t);
826
827impl GnsConnectionInfo {
828 #[inline]
829 pub fn state(&self) -> ESteamNetworkingConnectionState {
830 self.0.m_eState
831 }
832
833 #[inline]
834 pub fn end_reason(&self) -> u32 {
835 self.0.m_eEndReason as u32
836 }
837
838 #[inline]
839 pub fn end_debug(&self) -> &str {
840 unsafe { CStr::from_ptr(self.0.m_szEndDebug.as_ptr()) }
841 .to_str()
842 .unwrap_or("")
843 }
844
845 #[inline]
846 pub fn remote_address(&self) -> IpAddr {
847 let ipv4 = unsafe { self.0.m_addrRemote.__bindgen_anon_1.m_ipv4 };
848 if ipv4.m_8zeros == 0 && ipv4.m_0000 == 0 && ipv4.m_ffff == 0xffff {
849 IpAddr::from(Ipv4Addr::from(ipv4.m_ip))
850 } else {
851 IpAddr::from(Ipv6Addr::from(unsafe {
852 self.0.m_addrRemote.__bindgen_anon_1.m_ipv6
853 }))
854 }
855 }
856
857 #[inline]
858 pub fn remote_port(&self) -> u16 {
859 self.0.m_addrRemote.m_port
860 }
861}
862
863#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
864pub struct GnsConnectionRealTimeLaneStatus(SteamNetConnectionRealTimeLaneStatus_t);
865
866impl GnsConnectionRealTimeLaneStatus {
867 #[inline]
868 pub fn pending_bytes_unreliable(&self) -> u32 {
869 self.0.m_cbPendingUnreliable as _
870 }
871
872 #[inline]
873 pub fn pending_bytes_reliable(&self) -> u32 {
874 self.0.m_cbPendingReliable as _
875 }
876
877 #[inline]
878 pub fn bytes_sent_unacked_reliable(&self) -> u32 {
879 self.0.m_cbSentUnackedReliable as _
880 }
881
882 #[inline]
883 pub fn approximated_queue_time(&self) -> Duration {
884 Duration::from_micros(self.0.m_usecQueueTime as _)
885 }
886}
887
888#[derive(Default, Debug, Copy, Clone, PartialOrd, PartialEq)]
889pub struct GnsConnectionRealTimeStatus(SteamNetConnectionRealTimeStatus_t);
890
891impl GnsConnectionRealTimeStatus {
892 #[inline]
893 pub fn state(&self) -> ESteamNetworkingConnectionState {
894 self.0.m_eState
895 }
896
897 #[inline]
898 pub fn ping(&self) -> u32 {
899 self.0.m_nPing as _
900 }
901
902 #[inline]
903 pub fn quality_local(&self) -> f32 {
904 self.0.m_flConnectionQualityLocal
905 }
906
907 #[inline]
908 pub fn quality_remote(&self) -> f32 {
909 self.0.m_flConnectionQualityRemote
910 }
911
912 #[inline]
913 pub fn out_packets_per_sec(&self) -> f32 {
914 self.0.m_flOutPacketsPerSec
915 }
916
917 #[inline]
918 pub fn out_bytes_per_sec(&self) -> f32 {
919 self.0.m_flOutBytesPerSec
920 }
921
922 #[inline]
923 pub fn in_packets_per_sec(&self) -> f32 {
924 self.0.m_flInPacketsPerSec
925 }
926
927 #[inline]
928 pub fn in_bytes_per_sec(&self) -> f32 {
929 self.0.m_flInBytesPerSec
930 }
931
932 #[inline]
933 pub fn send_rate_bytes_per_sec(&self) -> u32 {
934 self.0.m_nSendRateBytesPerSecond as _
935 }
936
937 #[inline]
938 pub fn pending_bytes_unreliable(&self) -> u32 {
939 self.0.m_cbPendingUnreliable as _
940 }
941
942 #[inline]
943 pub fn pending_bytes_reliable(&self) -> u32 {
944 self.0.m_cbPendingReliable as _
945 }
946
947 #[inline]
948 pub fn bytes_sent_unacked_reliable(&self) -> u32 {
949 self.0.m_cbSentUnackedReliable as _
950 }
951
952 #[inline]
953 pub fn approximated_queue_time(&self) -> Duration {
954 Duration::from_micros(self.0.m_usecQueueTime as _)
955 }
956
957 #[inline]
964 pub fn max_jitter_usec(&self) -> Option<i32> {
965 let val = self.0.m_usecMaxJitter;
966 if val < 0 {
967 None
968 } else {
969 Some(val)
970 }
971 }
972}
973
974#[derive(Default, Copy, Clone)]
975pub struct GnsConnectionEvent(SteamNetConnectionStatusChangedCallback_t);
976
977impl GnsConnectionEvent {
978 #[inline]
979 pub fn old_state(&self) -> ESteamNetworkingConnectionState {
980 self.0.m_eOldState
981 }
982
983 #[inline]
984 pub fn connection(&self) -> GnsConnection {
985 GnsConnection(self.0.m_hConn)
986 }
987
988 #[inline]
989 pub fn info(&self) -> GnsConnectionInfo {
990 GnsConnectionInfo(self.0.m_info)
991 }
992}
993
994pub struct GnsSocket<S> {
1003 global: &'static GnsGlobal,
1004 state: S,
1005}
1006
1007impl<S> GnsSocket<S>
1008where
1009 S: IsReady,
1010{
1011 pub fn get_connection_real_time_status(
1016 &self,
1017 GnsConnection(conn): GnsConnection,
1018 nb_of_lanes: u32,
1019 ) -> GnsResult<(
1020 GnsConnectionRealTimeStatus,
1021 Vec<GnsConnectionRealTimeLaneStatus>,
1022 )> {
1023 let mut lanes: Vec<GnsConnectionRealTimeLaneStatus> =
1024 vec![Default::default(); nb_of_lanes as _];
1025 let mut status: GnsConnectionRealTimeStatus = Default::default();
1026 check(unsafe {
1027 SteamAPI_ISteamNetworkingSockets_GetConnectionRealTimeStatus(
1028 get_interface(),
1029 conn,
1030 &mut status as *mut GnsConnectionRealTimeStatus
1031 as *mut SteamNetConnectionRealTimeStatus_t,
1032 nb_of_lanes as _,
1033 lanes.as_mut_ptr() as *mut SteamNetConnectionRealTimeLaneStatus_t,
1034 )
1035 })?;
1036 Ok((status, lanes))
1037 }
1038
1039 pub fn get_connection_info(
1040 &self,
1041 GnsConnection(conn): GnsConnection,
1042 ) -> Option<GnsConnectionInfo> {
1043 let mut info: SteamNetConnectionInfo_t = Default::default();
1044 if unsafe {
1045 SteamAPI_ISteamNetworkingSockets_GetConnectionInfo(get_interface(), conn, &mut info)
1046 } {
1047 Some(GnsConnectionInfo(info))
1048 } else {
1049 None
1050 }
1051 }
1052
1053 pub fn flush_messages_on_connection(
1054 &self,
1055 GnsConnection(conn): GnsConnection,
1056 ) -> GnsResult<()> {
1057 check(unsafe {
1058 SteamAPI_ISteamNetworkingSockets_FlushMessagesOnConnection(get_interface(), conn)
1059 })
1060 }
1061
1062 pub fn close_connection(
1071 &self,
1072 GnsConnection(conn): GnsConnection,
1073 reason: u32,
1074 debug: Option<&CStr>,
1075 linger: bool,
1076 ) -> GnsResult<()> {
1077 let debug_ptr = debug.map(|d| d.as_ptr()).unwrap_or(core::ptr::null());
1078 if unsafe {
1079 SteamAPI_ISteamNetworkingSockets_CloseConnection(
1080 get_interface(),
1081 conn,
1082 reason as _,
1083 debug_ptr,
1084 linger,
1085 )
1086 } {
1087 Ok(())
1088 } else {
1089 Err(GnsError::Close)
1090 }
1091 }
1092
1093 pub fn receive_messages<const K: usize>(&self) -> GnsResult<ReceivedMessages<K>> {
1109 let mut slots: [MessageSlot; K] = [const { MessageSlot::uninit() }; K];
1110 let len = self.state.receive(&mut slots)?;
1111 Ok(ReceivedMessages {
1112 slots,
1113 cursor: SlotCursor { len, pos: 0 },
1114 })
1115 }
1116
1117 pub fn receive_messages_into<'a>(
1129 &self,
1130 buffer: &'a mut [MessageSlot],
1131 ) -> GnsResult<ReceivedMessagesInto<'a>> {
1132 let len = self.state.receive(buffer)?;
1133 Ok(ReceivedMessagesInto {
1134 slots: buffer,
1135 cursor: SlotCursor { len, pos: 0 },
1136 })
1137 }
1138
1139 pub fn receive_events(&self) -> impl Iterator<Item = GnsConnectionEvent> + '_ {
1145 core::iter::from_fn(|| self.state.queue().pop())
1146 }
1147
1148 pub fn configure_connection_lanes(
1149 &self,
1150 GnsConnection(connection): GnsConnection,
1151 lanes: &[GnsLane],
1152 ) -> GnsResult<()> {
1153 let (priorities, weights): (Vec<i32>, Vec<u16>) =
1154 lanes.iter().map(|l| (l.priority, l.weight)).unzip();
1155 check(unsafe {
1156 SteamAPI_ISteamNetworkingSockets_ConfigureConnectionLanes(
1157 get_interface(),
1158 connection,
1159 lanes.len() as _,
1160 priorities.as_ptr(),
1161 weights.as_ptr(),
1162 )
1163 })
1164 }
1165
1166 pub fn send_message(&self, message: GnsNetworkMessage<ToSend>) -> GnsResult<GnsMessageNumber> {
1171 match self.send_messages(core::iter::once(message)).pop() {
1172 Some(SendOutcome::Sent(number)) => Ok(number),
1173 Some(SendOutcome::Failed(result, _)) => Err(GnsError::Api(result)),
1174 _ => Err(GnsError::Api(EResult::k_EResultFail)),
1178 }
1179 }
1180
1181 pub fn send_messages(
1186 &self,
1187 messages: impl IntoIterator<Item = GnsNetworkMessage<ToSend>>,
1188 ) -> Vec<SendOutcome> {
1189 let mut raw: Vec<*mut ISteamNetworkingMessage> = messages
1194 .into_iter()
1195 .map(|message| {
1196 let message = core::mem::ManuallyDrop::new(message);
1197 message.0
1198 })
1199 .collect();
1200 let mut result = vec![0i64; raw.len()];
1201 unsafe {
1202 SteamAPI_ISteamNetworkingSockets_SendMessages(
1203 get_interface(),
1204 raw.len() as _,
1205 raw.as_mut_ptr(),
1206 result.as_mut_ptr(),
1207 false,
1208 );
1209 }
1210 result
1211 .into_iter()
1212 .zip(raw)
1213 .map(|(value, ptr)| {
1214 if value > 0 {
1215 SendOutcome::Sent(value as _)
1216 } else if value < 0 {
1217 let result = unsafe { core::mem::transmute::<u32, EResult>((-value) as u32) };
1221 SendOutcome::Failed(result, GnsNetworkMessage(ptr, PhantomData))
1222 } else {
1223 SendOutcome::Skipped(GnsNetworkMessage(ptr, PhantomData))
1224 }
1225 })
1226 .collect()
1227 }
1228}
1229
1230impl GnsSocket<IsCreated> {
1231 unsafe extern "C" fn on_connection_state_changed(
1237 info: &mut SteamNetConnectionStatusChangedCallback_t,
1238 ) {
1239 let gns_global = GnsGlobal::get()
1240 .expect("GnsGlobal should be initialized");
1242
1243 let queue_id = info.m_info.m_nUserData as _;
1244 let needs_purge = {
1247 let queues = gns_global.event_queues.read().unwrap();
1248 match queues.get(&queue_id).and_then(Weak::upgrade) {
1249 Some(queue) => {
1250 queue.push(GnsConnectionEvent(*info));
1251 false
1252 }
1253 None => queues.contains_key(&queue_id),
1254 }
1255 };
1256 if needs_purge {
1261 gns_global.event_queues.write().unwrap().remove(&queue_id);
1262 }
1263 }
1264
1265 #[inline]
1267 pub fn new(global: &'static GnsGlobal) -> Self {
1268 GnsSocket {
1269 global,
1270 state: IsCreated,
1271 }
1272 }
1273
1274 fn setup_common(
1275 address: IpAddr,
1276 port: u16,
1277 queue_id: int64,
1278 ) -> (SteamNetworkingIPAddr, [SteamNetworkingConfigValue_t; 2]) {
1279 let addr = SteamNetworkingIPAddr {
1280 __bindgen_anon_1: match address {
1281 IpAddr::V4(address) => SteamNetworkingIPAddr__bindgen_ty_2 {
1282 m_ipv4: SteamNetworkingIPAddr_IPv4MappedAddress {
1283 m_8zeros: 0,
1284 m_0000: 0,
1285 m_ffff: 0xffff,
1286 m_ip: address.octets(),
1287 },
1288 },
1289 IpAddr::V6(address) => SteamNetworkingIPAddr__bindgen_ty_2 {
1290 m_ipv6: address.octets(),
1291 },
1292 },
1293 m_port: port,
1294 };
1295 let options = [SteamNetworkingConfigValue_t {
1296 m_eDataType: ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Ptr,
1297 m_eValue: ESteamNetworkingConfigValue::k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged,
1298 m_val: SteamNetworkingConfigValue_t__bindgen_ty_1 {
1299 m_ptr: Self::on_connection_state_changed as *const fn(&SteamNetConnectionStatusChangedCallback_t) as *mut c_void
1300 }
1301 }, SteamNetworkingConfigValue_t {
1302 m_eDataType: ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Int64,
1303 m_eValue: ESteamNetworkingConfigValue::k_ESteamNetworkingConfig_ConnectionUserData,
1304 m_val: SteamNetworkingConfigValue_t__bindgen_ty_1 {
1305 m_int64: queue_id
1306 }
1307 }];
1308 (addr, options)
1309 }
1310
1311 pub fn listen(self, address: IpAddr, port: u16) -> GnsResult<GnsSocket<IsServer>> {
1316 let (queue_id, queue) = self.global.create_queue();
1317 let (addr, options) = Self::setup_common(address, port, queue_id);
1318 let listen_socket = unsafe {
1319 SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
1320 get_interface(),
1321 &addr,
1322 options.len() as _,
1323 options.as_ptr(),
1324 )
1325 };
1326 if listen_socket == k_HSteamListenSocket_Invalid {
1327 Err(GnsError::Listen)
1328 } else {
1329 let poll_group =
1330 unsafe { SteamAPI_ISteamNetworkingSockets_CreatePollGroup(get_interface()) };
1331 if poll_group == k_HSteamNetPollGroup_Invalid {
1332 Err(GnsError::Listen)
1333 } else {
1334 Ok(GnsSocket {
1335 global: self.global,
1336 state: IsServer {
1337 queue,
1338 queue_id,
1339 global: self.global,
1340 listen_socket: GnsListenSocket(listen_socket),
1341 poll_group: GnsPollGroup(poll_group),
1342 },
1343 })
1344 }
1345 }
1346 }
1347
1348 pub fn connect(self, address: IpAddr, port: u16) -> GnsResult<GnsSocket<IsClient>> {
1353 let (queue_id, queue) = self.global.create_queue();
1354 let (addr, options) = Self::setup_common(address, port, queue_id);
1355 let connection = unsafe {
1356 SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress(
1357 get_interface(),
1358 &addr,
1359 options.len() as _,
1360 options.as_ptr(),
1361 )
1362 };
1363 if connection == k_HSteamNetConnection_Invalid {
1364 Err(GnsError::Connect)
1365 } else {
1366 Ok(GnsSocket {
1367 global: self.global,
1368 state: IsClient {
1369 queue,
1370 queue_id,
1371 global: self.global,
1372 connection: GnsConnection(connection),
1373 },
1374 })
1375 }
1376 }
1377}
1378
1379impl GnsSocket<IsServer> {
1380 pub fn accept(&self, connection: GnsConnection) -> GnsResult<()> {
1383 check(unsafe {
1384 SteamAPI_ISteamNetworkingSockets_AcceptConnection(get_interface(), connection.0)
1385 })?;
1386 if !unsafe {
1387 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
1388 get_interface(),
1389 connection.0,
1390 self.state.poll_group.0,
1391 )
1392 } {
1393 return Err(GnsError::Accept);
1396 }
1397 Ok(())
1398 }
1399}
1400
1401impl GnsSocket<IsClient> {
1402 #[inline]
1405 pub fn connection(&self) -> GnsConnection {
1406 self.state.connection
1407 }
1408}
1409
1410pub enum GnsConfig<'a> {
1413 Float(f32),
1414 Int32(i32),
1415 String(&'a str),
1419 CStr(&'a CStr),
1422 Ptr(*mut c_void),
1423}
1424
1425pub struct GnsUtils(());
1426
1427type MsgPtr = *const ::std::os::raw::c_char;
1428
1429type DebugCallback = dyn Fn(ESteamNetworkingSocketsDebugOutputType, &str) + Send + Sync + 'static;
1434
1435static DEBUG_CB: OnceLock<Box<DebugCallback>> = OnceLock::new();
1440
1441unsafe extern "C" fn debug_trampoline(ty: ESteamNetworkingSocketsDebugOutputType, msg: MsgPtr) {
1442 if let Some(cb) = DEBUG_CB.get() {
1443 let s = unsafe { CStr::from_ptr(msg) }.to_str().unwrap_or("");
1444 cb(ty, s);
1445 }
1446}
1447
1448impl GnsUtils {
1449 pub fn enable_debug_output(
1458 &self,
1459 ty: ESteamNetworkingSocketsDebugOutputType,
1460 f: impl Fn(ESteamNetworkingSocketsDebugOutputType, &str) + Send + Sync + 'static,
1461 ) {
1462 let _ = DEBUG_CB.set(Box::new(f));
1463 unsafe {
1464 SteamAPI_ISteamNetworkingUtils_SetDebugOutputFunction(
1465 get_utils(),
1466 ty,
1467 Some(debug_trampoline),
1468 );
1469 }
1470 }
1471
1472 #[inline]
1478 pub fn allocate_message<P: Payload>(
1479 &self,
1480 conn: GnsConnection,
1481 flags: SendFlags,
1482 payload: P,
1483 ) -> GnsNetworkMessage<ToSend> {
1484 let message_ptr = unsafe { SteamAPI_ISteamNetworkingUtils_AllocateMessage(get_utils(), 0) };
1485 GnsNetworkMessage::new(message_ptr, conn, flags, payload)
1486 }
1487
1488 pub fn set_global_config_value(
1491 &self,
1492 typ: ESteamNetworkingConfigValue,
1493 value: GnsConfig<'_>,
1494 ) -> GnsResult<()> {
1495 let result = match value {
1496 GnsConfig::Float(x) => unsafe {
1497 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueFloat(get_utils(), typ, x)
1498 },
1499 GnsConfig::Int32(x) => unsafe {
1500 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueInt32(get_utils(), typ, x)
1501 },
1502 GnsConfig::String(x) => {
1503 let c = CString::new(x).map_err(|_| GnsError::Config("interior NUL"))?;
1504 unsafe {
1505 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueString(
1506 get_utils(),
1507 typ,
1508 c.as_ptr(),
1509 )
1510 }
1511 }
1512 GnsConfig::CStr(x) => unsafe {
1513 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueString(
1514 get_utils(),
1515 typ,
1516 x.as_ptr(),
1517 )
1518 },
1519 GnsConfig::Ptr(x) => unsafe {
1520 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValuePtr(get_utils(), typ, x)
1521 },
1522 };
1523 if result {
1524 Ok(())
1525 } else {
1526 Err(GnsError::Config("SetGlobalConfigValue rejected"))
1527 }
1528 }
1529
1530 pub fn set_connection_config_value(
1533 &self,
1534 conn: GnsConnection,
1535 typ: ESteamNetworkingConfigValue,
1536 value: GnsConfig<'_>,
1537 ) -> GnsResult<()> {
1538 let result = match value {
1539 GnsConfig::Float(x) => unsafe {
1540 SteamAPI_ISteamNetworkingUtils_SetConnectionConfigValueFloat(
1541 get_utils(),
1542 conn.0,
1543 typ,
1544 x,
1545 )
1546 },
1547 GnsConfig::Int32(x) => unsafe {
1548 SteamAPI_ISteamNetworkingUtils_SetConnectionConfigValueInt32(
1549 get_utils(),
1550 conn.0,
1551 typ,
1552 x,
1553 )
1554 },
1555 GnsConfig::String(x) => {
1556 let c = CString::new(x).map_err(|_| GnsError::Config("interior NUL"))?;
1557 unsafe {
1558 SteamAPI_ISteamNetworkingUtils_SetConnectionConfigValueString(
1559 get_utils(),
1560 conn.0,
1561 typ,
1562 c.as_ptr(),
1563 )
1564 }
1565 }
1566 GnsConfig::CStr(x) => unsafe {
1567 SteamAPI_ISteamNetworkingUtils_SetConnectionConfigValueString(
1568 get_utils(),
1569 conn.0,
1570 typ,
1571 x.as_ptr(),
1572 )
1573 },
1574 GnsConfig::Ptr(_) => return Err(GnsError::Config("Ptr not supported per-connection")),
1575 };
1576 if result {
1577 Ok(())
1578 } else {
1579 Err(GnsError::Config("SetConnectionConfigValue rejected"))
1580 }
1581 }
1582}