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)]
105#[non_exhaustive]
106pub enum GnsError {
107 #[error("GameNetworkingSockets_Init failed: {0}")]
108 Init(String),
109 #[error("listen failed: invalid handle")]
110 Listen,
111 #[error("connect failed: invalid handle")]
112 Connect,
113 #[error("create socket pair failed")]
114 SocketPair,
115 #[error("receive failed: invalid connection or poll group handle")]
116 Receive,
117 #[error("accept failed: could not set connection poll group")]
118 Accept,
119 #[error("close failed: invalid connection handle")]
120 Close,
121 #[error("steam api: {0:?}")]
122 Api(EResult),
123 #[error("config: {0}")]
124 Config(&'static str),
125}
126
127pub type GnsResult<T> = Result<T, GnsError>;
128
129#[inline]
132fn ip_from_steam_ip_addr(addr: &SteamNetworkingIPAddr) -> IpAddr {
133 let ipv4 = unsafe { addr.__bindgen_anon_1.m_ipv4 };
134 if ipv4.m_8zeros == 0 && ipv4.m_0000 == 0 && ipv4.m_ffff == 0xffff {
135 IpAddr::from(Ipv4Addr::from(ipv4.m_ip))
136 } else {
137 IpAddr::from(Ipv6Addr::from(unsafe { addr.__bindgen_anon_1.m_ipv6 }))
138 }
139}
140
141#[inline]
143fn check(e: EResult) -> GnsResult<()> {
144 match e {
145 EResult::k_EResultOK => Ok(()),
146 e => Err(GnsError::Api(e)),
147 }
148}
149
150pub struct GnsGlobal {
156 utils: GnsUtils,
157 next_queue_id: AtomicI64,
158 event_queues: RwLock<HashMap<i64, Weak<SegQueue<GnsConnectionEvent>>>>,
166}
167
168static GNS_GLOBAL: OnceLock<GnsGlobal> = OnceLock::new();
169
170impl Drop for GnsGlobal {
171 #[inline]
172 fn drop(&mut self) {
173 unsafe { GameNetworkingSockets_Kill() }
179 }
180}
181
182impl GnsGlobal {
183 pub fn get() -> GnsResult<&'static Self> {
193 if let Some(g) = GNS_GLOBAL.get() {
195 return Ok(g);
196 }
197 static INIT_LOCK: Mutex<()> = Mutex::new(());
199 let _guard = INIT_LOCK.lock().unwrap();
200 if let Some(g) = GNS_GLOBAL.get() {
201 return Ok(g);
202 }
203 unsafe {
204 let mut error: SteamDatagramErrMsg = MaybeUninit::zeroed().assume_init();
205 if !GameNetworkingSockets_Init(core::ptr::null(), &mut error) {
206 return Err(GnsError::Init(
207 CStr::from_ptr(error.as_ptr())
208 .to_str()
209 .unwrap_or("")
210 .to_owned(),
211 ));
212 }
213 }
214 let _ = GNS_GLOBAL.set(GnsGlobal {
215 utils: GnsUtils(()),
216 next_queue_id: AtomicI64::new(0),
217 event_queues: RwLock::new(HashMap::new()),
218 });
219 Ok(GNS_GLOBAL.get().expect("impossible; qed;"))
220 }
221
222 #[inline]
223 pub fn poll_callbacks(&self) {
224 unsafe {
225 SteamAPI_ISteamNetworkingSockets_RunCallbacks(get_interface());
226 }
227 }
228
229 #[inline]
230 pub fn utils(&self) -> &GnsUtils {
231 &self.utils
232 }
233
234 #[inline]
235 pub fn queue_count(&self) -> usize {
236 self.event_queues.read().unwrap().len()
237 }
238
239 #[inline]
240 fn create_queue(&self) -> (i64, Arc<SegQueue<GnsConnectionEvent>>) {
241 let queue = Arc::new(SegQueue::new());
242 let queue_id = self.next_queue_id.fetch_add(1, Ordering::SeqCst);
243 self.event_queues
244 .write()
245 .unwrap()
246 .insert(queue_id, Arc::downgrade(&queue));
247 (queue_id, queue)
248 }
249}
250
251#[repr(transparent)]
253pub(crate) struct GnsListenSocket(HSteamListenSocket);
254
255#[repr(transparent)]
257pub(crate) struct GnsPollGroup(HSteamNetPollGroup);
258
259pub struct IsCreated;
264
265mod private {
266 pub trait Sealed {}
267 impl Sealed for super::IsServer {}
268 impl Sealed for super::IsClient {}
269}
270
271pub trait IsReady: private::Sealed {
276 fn queue(&self) -> &SegQueue<GnsConnectionEvent>;
278 fn receive(&self, slots: &mut [MaybeUninit<*mut ISteamNetworkingMessage>]) -> GnsResult<usize>;
283}
284
285pub struct IsServer {
291 queue: Arc<SegQueue<GnsConnectionEvent>>,
292 queue_id: i64,
293 global: &'static GnsGlobal,
294 listen_socket: GnsListenSocket,
295 poll_group: GnsPollGroup,
296}
297
298impl Drop for IsServer {
299 #[inline]
300 fn drop(&mut self) {
301 unsafe {
302 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
303 get_interface(),
304 self.listen_socket.0,
305 );
306 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup(get_interface(), self.poll_group.0);
307 }
308 self.global
309 .event_queues
310 .write()
311 .unwrap()
312 .remove(&self.queue_id);
313 }
314}
315
316impl IsReady for IsServer {
317 #[inline]
318 fn queue(&self) -> &SegQueue<GnsConnectionEvent> {
319 &self.queue
320 }
321
322 fn receive(&self, slots: &mut [MaybeUninit<*mut ISteamNetworkingMessage>]) -> GnsResult<usize> {
323 let result = unsafe {
324 SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
325 get_interface(),
326 self.poll_group.0,
327 slots.as_mut_ptr() as _,
328 slots.len() as _,
329 ) as _
330 };
331 if result == usize::MAX {
332 Err(GnsError::Receive)
333 } else {
334 Ok(result)
335 }
336 }
337}
338
339pub struct IsClient {
344 queue: Arc<SegQueue<GnsConnectionEvent>>,
345 queue_id: i64,
346 global: &'static GnsGlobal,
347 connection: GnsConnection,
348}
349
350impl Drop for IsClient {
351 fn drop(&mut self) {
352 unsafe {
353 SteamAPI_ISteamNetworkingSockets_CloseConnection(
354 get_interface(),
355 self.connection.0,
356 0,
357 core::ptr::null(),
358 false,
359 );
360 }
361 self.global
362 .event_queues
363 .write()
364 .unwrap()
365 .remove(&self.queue_id);
366 }
367}
368
369impl IsReady for IsClient {
370 #[inline]
371 fn queue(&self) -> &SegQueue<GnsConnectionEvent> {
372 &self.queue
373 }
374
375 fn receive(&self, slots: &mut [MaybeUninit<*mut ISteamNetworkingMessage>]) -> GnsResult<usize> {
376 let result = unsafe {
377 SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnConnection(
378 get_interface(),
379 self.connection.0,
380 slots.as_mut_ptr() as _,
381 slots.len() as _,
382 ) as _
383 };
384 if result == usize::MAX {
385 Err(GnsError::Receive)
386 } else {
387 Ok(result)
388 }
389 }
390}
391
392pub struct ToReceive(());
393
394pub struct ToSend(());
395
396pub type MessageSlot = MaybeUninit<*mut ISteamNetworkingMessage>;
403
404#[inline]
411unsafe fn take_message(slot: &MessageSlot) -> GnsNetworkMessage<ToReceive> {
412 GnsNetworkMessage(unsafe { slot.assume_init() }, PhantomData)
413}
414
415struct SlotCursor {
421 len: usize,
422 pos: usize,
423}
424
425impl SlotCursor {
426 fn next(&mut self, slots: &[MessageSlot]) -> Option<GnsNetworkMessage<ToReceive>> {
427 if self.pos < self.len {
428 let message = unsafe { take_message(&slots[self.pos]) };
431 self.pos += 1;
432 Some(message)
433 } else {
434 None
435 }
436 }
437
438 #[inline]
439 fn remaining(&self) -> usize {
440 self.len - self.pos
441 }
442
443 fn drain_unconsumed(&mut self, slots: &[MessageSlot]) {
446 for slot in &slots[self.pos..self.len] {
447 drop(unsafe { take_message(slot) });
450 }
451 self.pos = self.len;
452 }
453}
454
455pub struct ReceivedMessages<const K: usize> {
465 slots: [MessageSlot; K],
466 cursor: SlotCursor,
467}
468
469impl<const K: usize> Iterator for ReceivedMessages<K> {
470 type Item = GnsNetworkMessage<ToReceive>;
471
472 #[inline]
473 fn next(&mut self) -> Option<Self::Item> {
474 self.cursor.next(&self.slots)
475 }
476
477 #[inline]
478 fn size_hint(&self) -> (usize, Option<usize>) {
479 let remaining = self.cursor.remaining();
480 (remaining, Some(remaining))
481 }
482}
483
484impl<const K: usize> ExactSizeIterator for ReceivedMessages<K> {}
485
486impl<const K: usize> core::iter::FusedIterator for ReceivedMessages<K> {}
487
488impl<const K: usize> Drop for ReceivedMessages<K> {
489 #[inline]
490 fn drop(&mut self) {
491 self.cursor.drain_unconsumed(&self.slots);
492 }
493}
494
495pub struct ReceivedMessagesInto<'a> {
505 slots: &'a mut [MessageSlot],
506 cursor: SlotCursor,
507}
508
509impl Iterator for ReceivedMessagesInto<'_> {
510 type Item = GnsNetworkMessage<ToReceive>;
511
512 #[inline]
513 fn next(&mut self) -> Option<Self::Item> {
514 self.cursor.next(self.slots)
515 }
516
517 #[inline]
518 fn size_hint(&self) -> (usize, Option<usize>) {
519 let remaining = self.cursor.remaining();
520 (remaining, Some(remaining))
521 }
522}
523
524impl ExactSizeIterator for ReceivedMessagesInto<'_> {}
525
526impl core::iter::FusedIterator for ReceivedMessagesInto<'_> {}
527
528impl Drop for ReceivedMessagesInto<'_> {
529 #[inline]
530 fn drop(&mut self) {
531 self.cursor.drain_unconsumed(self.slots);
532 }
533}
534
535bitflags::bitflags! {
536 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
540 pub struct SendFlags: i32 {
541 const UNRELIABLE = sys::k_nSteamNetworkingSend_Unreliable;
542 const NO_NAGLE = sys::k_nSteamNetworkingSend_NoNagle;
543 const NO_DELAY = sys::k_nSteamNetworkingSend_NoDelay;
544 const RELIABLE = sys::k_nSteamNetworkingSend_Reliable;
545 const USE_CURRENT_THREAD = sys::k_nSteamNetworkingSend_UseCurrentThread;
546 const AUTO_RESTART_BROKEN_SESSION = sys::k_nSteamNetworkingSend_AutoRestartBrokenSession;
547 }
548}
549
550#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
555pub struct GnsLane {
556 pub priority: i32,
557 pub weight: u16,
558}
559
560impl GnsLane {
561 #[inline]
562 pub const fn new(priority: i32, weight: u16) -> Self {
563 Self { priority, weight }
564 }
565}
566
567pub type GnsLaneId = u16;
569
570#[must_use = "Failed/Skipped variants own a message that needs inspection or drop"]
578pub enum SendOutcome {
579 Sent(GnsMessageNumber),
580 Failed(EResult, GnsNetworkMessage<ToSend>),
581 Skipped(GnsNetworkMessage<ToSend>),
582}
583
584pub unsafe trait Payload: Send + 'static {
607 fn into_raw(self) -> (*mut u8, usize);
608 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self;
613}
614
615extern "C" fn free_payload<P: Payload>(msg: *mut ISteamNetworkingMessage) {
621 let ptr = unsafe { (*msg).m_pData } as *mut u8;
622 let len = unsafe { (*msg).m_cbSize } as usize;
623 drop(unsafe { P::from_raw(ptr, len) });
626}
627
628unsafe impl Payload for Box<[u8]> {
629 #[inline]
630 fn into_raw(self) -> (*mut u8, usize) {
631 let len = self.len();
632 let raw = Box::into_raw(self) as *mut u8;
633 (raw, len)
634 }
635 #[inline]
636 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
637 let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
638 unsafe { Box::from_raw(slice) }
639 }
640}
641
642unsafe impl Payload for Vec<u8> {
646 #[inline]
647 fn into_raw(self) -> (*mut u8, usize) {
648 <Box<[u8]> as Payload>::into_raw(self.into_boxed_slice())
649 }
650 #[inline]
651 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
652 unsafe { Vec::from_raw_parts(ptr, len, len) }
653 }
654}
655
656unsafe impl Payload for String {
657 #[inline]
658 fn into_raw(self) -> (*mut u8, usize) {
659 <Vec<u8> as Payload>::into_raw(self.into_bytes())
660 }
661 #[inline]
662 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
663 unsafe { String::from_raw_parts(ptr, len, len) }
664 }
665}
666
667unsafe impl Payload for Arc<[u8]> {
668 #[inline]
669 fn into_raw(self) -> (*mut u8, usize) {
670 let len = self.len();
671 let raw = Arc::into_raw(self) as *const u8 as *mut u8;
672 (raw, len)
673 }
674 #[inline]
675 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
676 let slice = core::ptr::slice_from_raw_parts(ptr as *const u8, len);
677 unsafe { Arc::from_raw(slice) }
678 }
679}
680
681unsafe impl Payload for &'static [u8] {
682 #[inline]
683 fn into_raw(self) -> (*mut u8, usize) {
684 (self.as_ptr() as *mut u8, self.len())
685 }
686 #[inline]
687 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
688 unsafe { core::slice::from_raw_parts(ptr as *const u8, len) }
689 }
690}
691
692unsafe impl Payload for &'static str {
693 #[inline]
694 fn into_raw(self) -> (*mut u8, usize) {
695 (self.as_ptr() as *mut u8, self.len())
696 }
697 #[inline]
698 unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
699 let bytes = unsafe { core::slice::from_raw_parts(ptr as *const u8, len) };
700 unsafe { core::str::from_utf8_unchecked(bytes) }
701 }
702}
703
704#[repr(transparent)]
710pub struct GnsNetworkMessage<T>(*mut ISteamNetworkingMessage, PhantomData<T>);
711
712impl<T> Drop for GnsNetworkMessage<T> {
713 #[inline]
714 fn drop(&mut self) {
715 if !self.0.is_null() {
716 unsafe {
717 SteamAPI_SteamNetworkingMessage_t_Release(self.0);
718 }
719 }
720 }
721}
722
723impl<T> GnsNetworkMessage<T> {
724 #[inline]
732 pub unsafe fn into_inner(self) -> *mut ISteamNetworkingMessage {
733 core::mem::ManuallyDrop::new(self).0
737 }
738
739 #[inline]
740 pub fn payload(&self) -> &[u8] {
741 unsafe {
742 core::slice::from_raw_parts((*self.0).m_pData as *const u8, (*self.0).m_cbSize as _)
743 }
744 }
745
746 #[inline]
747 pub fn message_number(&self) -> u64 {
748 unsafe { (*self.0).m_nMessageNumber as _ }
749 }
750
751 #[inline]
758 pub fn time_received(&self) -> SteamNetworkingMicroseconds {
759 unsafe { (*self.0).m_usecTimeReceived }
760 }
761
762 #[inline]
763 pub fn lane(&self) -> GnsLaneId {
764 unsafe { (*self.0).m_idxLane }
765 }
766
767 #[inline]
768 pub fn flags(&self) -> SendFlags {
769 SendFlags::from_bits_retain(unsafe { (*self.0).m_nFlags })
770 }
771
772 #[inline]
773 pub fn user_data(&self) -> u64 {
774 unsafe { (*self.0).m_nUserData as _ }
775 }
776
777 #[inline]
778 pub fn connection(&self) -> GnsConnection {
779 GnsConnection(unsafe { (*self.0).m_conn })
780 }
781
782 #[inline]
783 pub fn connection_user_data(&self) -> u64 {
784 unsafe { (*self.0).m_nConnUserData as _ }
785 }
786}
787
788impl GnsNetworkMessage<ToSend> {
789 #[inline]
790 fn new<P: Payload>(
791 ptr: *mut ISteamNetworkingMessage,
792 conn: GnsConnection,
793 flags: SendFlags,
794 payload: P,
795 ) -> Self {
796 let (data_ptr, len) = payload.into_raw();
797 unsafe {
798 (*ptr).m_pData = data_ptr as *mut c_void;
799 (*ptr).m_cbSize = len as i32;
800 (*ptr).m_pfnFreeData = Some(free_payload::<P>);
801 }
802 GnsNetworkMessage(ptr, PhantomData)
803 .set_flags(flags)
804 .set_connection(conn)
805 }
806
807 #[inline]
808 pub fn set_connection(self, GnsConnection(conn): GnsConnection) -> Self {
809 unsafe { (*self.0).m_conn = conn }
810 self
811 }
812
813 #[inline]
814 pub fn set_lane(self, lane: GnsLaneId) -> Self {
815 unsafe { (*self.0).m_idxLane = lane }
816 self
817 }
818
819 #[inline]
820 pub fn set_flags(self, flags: SendFlags) -> Self {
821 unsafe { (*self.0).m_nFlags = flags.bits() as _ }
822 self
823 }
824
825 #[inline]
826 pub fn set_user_data(self, userdata: u64) -> Self {
827 unsafe { (*self.0).m_nUserData = userdata as _ }
828 self
829 }
830}
831
832#[repr(transparent)]
833#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
834pub struct GnsConnection(HSteamNetConnection);
835
836impl GnsConnection {
837 #[inline]
842 pub const fn from_raw(handle: HSteamNetConnection) -> Self {
843 Self(handle)
844 }
845
846 #[inline]
848 pub fn is_valid(self) -> bool {
849 self.0 != k_HSteamNetConnection_Invalid
850 }
851}
852
853#[derive(Default, Copy, Clone)]
854pub struct GnsConnectionInfo(SteamNetConnectionInfo_t);
855
856impl GnsConnectionInfo {
857 #[inline]
858 pub fn state(&self) -> ESteamNetworkingConnectionState {
859 self.0.m_eState
860 }
861
862 #[inline]
863 pub fn end_reason(&self) -> u32 {
864 self.0.m_eEndReason as u32
865 }
866
867 #[inline]
868 pub fn end_debug(&self) -> &str {
869 unsafe { CStr::from_ptr(self.0.m_szEndDebug.as_ptr()) }
870 .to_str()
871 .unwrap_or("")
872 }
873
874 #[inline]
875 pub fn remote_address(&self) -> IpAddr {
876 ip_from_steam_ip_addr(&self.0.m_addrRemote)
877 }
878
879 #[inline]
880 pub fn remote_port(&self) -> u16 {
881 self.0.m_addrRemote.m_port
882 }
883}
884
885#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
886pub struct GnsConnectionRealTimeLaneStatus(SteamNetConnectionRealTimeLaneStatus_t);
887
888impl GnsConnectionRealTimeLaneStatus {
889 #[inline]
890 pub fn pending_bytes_unreliable(&self) -> u32 {
891 self.0.m_cbPendingUnreliable as _
892 }
893
894 #[inline]
895 pub fn pending_bytes_reliable(&self) -> u32 {
896 self.0.m_cbPendingReliable as _
897 }
898
899 #[inline]
900 pub fn bytes_sent_unacked_reliable(&self) -> u32 {
901 self.0.m_cbSentUnackedReliable as _
902 }
903
904 #[inline]
905 pub fn approximated_queue_time(&self) -> Duration {
906 Duration::from_micros(self.0.m_usecQueueTime as _)
907 }
908}
909
910#[derive(Default, Debug, Copy, Clone, PartialOrd, PartialEq)]
911pub struct GnsConnectionRealTimeStatus(SteamNetConnectionRealTimeStatus_t);
912
913impl GnsConnectionRealTimeStatus {
914 #[inline]
915 pub fn state(&self) -> ESteamNetworkingConnectionState {
916 self.0.m_eState
917 }
918
919 #[inline]
920 pub fn ping(&self) -> u32 {
921 self.0.m_nPing as _
922 }
923
924 #[inline]
925 pub fn quality_local(&self) -> f32 {
926 self.0.m_flConnectionQualityLocal
927 }
928
929 #[inline]
930 pub fn quality_remote(&self) -> f32 {
931 self.0.m_flConnectionQualityRemote
932 }
933
934 #[inline]
935 pub fn out_packets_per_sec(&self) -> f32 {
936 self.0.m_flOutPacketsPerSec
937 }
938
939 #[inline]
940 pub fn out_bytes_per_sec(&self) -> f32 {
941 self.0.m_flOutBytesPerSec
942 }
943
944 #[inline]
945 pub fn in_packets_per_sec(&self) -> f32 {
946 self.0.m_flInPacketsPerSec
947 }
948
949 #[inline]
950 pub fn in_bytes_per_sec(&self) -> f32 {
951 self.0.m_flInBytesPerSec
952 }
953
954 #[inline]
955 pub fn send_rate_bytes_per_sec(&self) -> u32 {
956 self.0.m_nSendRateBytesPerSecond as _
957 }
958
959 #[inline]
960 pub fn pending_bytes_unreliable(&self) -> u32 {
961 self.0.m_cbPendingUnreliable as _
962 }
963
964 #[inline]
965 pub fn pending_bytes_reliable(&self) -> u32 {
966 self.0.m_cbPendingReliable as _
967 }
968
969 #[inline]
970 pub fn bytes_sent_unacked_reliable(&self) -> u32 {
971 self.0.m_cbSentUnackedReliable as _
972 }
973
974 #[inline]
975 pub fn approximated_queue_time(&self) -> Duration {
976 Duration::from_micros(self.0.m_usecQueueTime as _)
977 }
978
979 #[inline]
986 pub fn max_jitter_usec(&self) -> Option<i32> {
987 let val = self.0.m_usecMaxJitter;
988 if val < 0 {
989 None
990 } else {
991 Some(val)
992 }
993 }
994}
995
996#[derive(Default, Copy, Clone)]
997pub struct GnsConnectionEvent(SteamNetConnectionStatusChangedCallback_t);
998
999impl GnsConnectionEvent {
1000 #[inline]
1001 pub fn old_state(&self) -> ESteamNetworkingConnectionState {
1002 self.0.m_eOldState
1003 }
1004
1005 #[inline]
1006 pub fn connection(&self) -> GnsConnection {
1007 GnsConnection(self.0.m_hConn)
1008 }
1009
1010 #[inline]
1011 pub fn info(&self) -> GnsConnectionInfo {
1012 GnsConnectionInfo(self.0.m_info)
1013 }
1014}
1015
1016pub struct GnsSocket<S> {
1025 global: &'static GnsGlobal,
1026 state: S,
1027}
1028
1029impl<S> GnsSocket<S>
1030where
1031 S: IsReady,
1032{
1033 pub fn get_connection_real_time_status(
1038 &self,
1039 GnsConnection(conn): GnsConnection,
1040 nb_of_lanes: u32,
1041 ) -> GnsResult<(
1042 GnsConnectionRealTimeStatus,
1043 Vec<GnsConnectionRealTimeLaneStatus>,
1044 )> {
1045 let mut lanes: Vec<GnsConnectionRealTimeLaneStatus> =
1046 vec![Default::default(); nb_of_lanes as _];
1047 let mut status: GnsConnectionRealTimeStatus = Default::default();
1048 check(unsafe {
1049 SteamAPI_ISteamNetworkingSockets_GetConnectionRealTimeStatus(
1050 get_interface(),
1051 conn,
1052 &mut status as *mut GnsConnectionRealTimeStatus
1053 as *mut SteamNetConnectionRealTimeStatus_t,
1054 nb_of_lanes as _,
1055 lanes.as_mut_ptr() as *mut SteamNetConnectionRealTimeLaneStatus_t,
1056 )
1057 })?;
1058 Ok((status, lanes))
1059 }
1060
1061 pub fn get_connection_info(
1062 &self,
1063 GnsConnection(conn): GnsConnection,
1064 ) -> Option<GnsConnectionInfo> {
1065 let mut info: SteamNetConnectionInfo_t = Default::default();
1066 if unsafe {
1067 SteamAPI_ISteamNetworkingSockets_GetConnectionInfo(get_interface(), conn, &mut info)
1068 } {
1069 Some(GnsConnectionInfo(info))
1070 } else {
1071 None
1072 }
1073 }
1074
1075 pub fn get_detailed_connection_status(
1081 &self,
1082 GnsConnection(conn): GnsConnection,
1083 ) -> Option<String> {
1084 let mut buf = vec![0u8; 2048];
1085 loop {
1086 let result = unsafe {
1087 SteamAPI_ISteamNetworkingSockets_GetDetailedConnectionStatus(
1088 get_interface(),
1089 conn,
1090 buf.as_mut_ptr() as *mut std::ffi::c_char,
1091 buf.len() as _,
1092 )
1093 };
1094 if result < 0 {
1095 return None;
1096 }
1097 if result == 0 {
1098 let text = CStr::from_bytes_until_nul(&buf).ok()?;
1099 return Some(text.to_string_lossy().into_owned());
1100 }
1101 buf.resize(result as usize, 0);
1103 }
1104 }
1105
1106 pub fn get_connection_name(&self, GnsConnection(conn): GnsConnection) -> Option<String> {
1111 let mut buf = [0u8; 256];
1112 if unsafe {
1113 SteamAPI_ISteamNetworkingSockets_GetConnectionName(
1114 get_interface(),
1115 conn,
1116 buf.as_mut_ptr() as *mut std::ffi::c_char,
1117 buf.len() as _,
1118 )
1119 } {
1120 let name = CStr::from_bytes_until_nul(&buf).ok()?;
1121 Some(name.to_string_lossy().into_owned())
1122 } else {
1123 None
1124 }
1125 }
1126
1127 pub fn set_connection_name(
1136 &self,
1137 GnsConnection(conn): GnsConnection,
1138 name: &str,
1139 ) -> GnsResult<()> {
1140 let c = CString::new(name).map_err(|_| GnsError::Config("interior NUL"))?;
1141 unsafe {
1142 SteamAPI_ISteamNetworkingSockets_SetConnectionName(get_interface(), conn, c.as_ptr());
1143 }
1144 Ok(())
1145 }
1146
1147 pub fn flush_messages_on_connection(
1148 &self,
1149 GnsConnection(conn): GnsConnection,
1150 ) -> GnsResult<()> {
1151 check(unsafe {
1152 SteamAPI_ISteamNetworkingSockets_FlushMessagesOnConnection(get_interface(), conn)
1153 })
1154 }
1155
1156 pub fn close_connection(
1165 &self,
1166 GnsConnection(conn): GnsConnection,
1167 reason: u32,
1168 debug: Option<&CStr>,
1169 linger: bool,
1170 ) -> GnsResult<()> {
1171 let debug_ptr = debug.map(|d| d.as_ptr()).unwrap_or(core::ptr::null());
1172 if unsafe {
1173 SteamAPI_ISteamNetworkingSockets_CloseConnection(
1174 get_interface(),
1175 conn,
1176 reason as _,
1177 debug_ptr,
1178 linger,
1179 )
1180 } {
1181 Ok(())
1182 } else {
1183 Err(GnsError::Close)
1184 }
1185 }
1186
1187 pub fn receive_messages<const K: usize>(&self) -> GnsResult<ReceivedMessages<K>> {
1203 let mut slots: [MessageSlot; K] = [const { MessageSlot::uninit() }; K];
1204 let len = self.state.receive(&mut slots)?;
1205 Ok(ReceivedMessages {
1206 slots,
1207 cursor: SlotCursor { len, pos: 0 },
1208 })
1209 }
1210
1211 pub fn receive_messages_into<'a>(
1223 &self,
1224 buffer: &'a mut [MessageSlot],
1225 ) -> GnsResult<ReceivedMessagesInto<'a>> {
1226 let len = self.state.receive(buffer)?;
1227 Ok(ReceivedMessagesInto {
1228 slots: buffer,
1229 cursor: SlotCursor { len, pos: 0 },
1230 })
1231 }
1232
1233 pub fn receive_events(&self) -> impl Iterator<Item = GnsConnectionEvent> + '_ {
1239 core::iter::from_fn(|| self.state.queue().pop())
1240 }
1241
1242 pub fn configure_connection_lanes(
1243 &self,
1244 GnsConnection(connection): GnsConnection,
1245 lanes: &[GnsLane],
1246 ) -> GnsResult<()> {
1247 let (priorities, weights): (Vec<i32>, Vec<u16>) =
1248 lanes.iter().map(|l| (l.priority, l.weight)).unzip();
1249 check(unsafe {
1250 SteamAPI_ISteamNetworkingSockets_ConfigureConnectionLanes(
1251 get_interface(),
1252 connection,
1253 lanes.len() as _,
1254 priorities.as_ptr(),
1255 weights.as_ptr(),
1256 )
1257 })
1258 }
1259
1260 pub fn send_message(&self, message: GnsNetworkMessage<ToSend>) -> GnsResult<GnsMessageNumber> {
1265 match self.send_messages(core::iter::once(message)).pop() {
1266 Some(SendOutcome::Sent(number)) => Ok(number),
1267 Some(SendOutcome::Failed(result, _)) => Err(GnsError::Api(result)),
1268 _ => Err(GnsError::Api(EResult::k_EResultFail)),
1272 }
1273 }
1274
1275 pub fn send_messages(
1280 &self,
1281 messages: impl IntoIterator<Item = GnsNetworkMessage<ToSend>>,
1282 ) -> Vec<SendOutcome> {
1283 let mut raw: Vec<*mut ISteamNetworkingMessage> = messages
1288 .into_iter()
1289 .map(|message| {
1290 let message = core::mem::ManuallyDrop::new(message);
1291 message.0
1292 })
1293 .collect();
1294 let mut result = vec![0i64; raw.len()];
1295 unsafe {
1296 SteamAPI_ISteamNetworkingSockets_SendMessages(
1297 get_interface(),
1298 raw.len() as _,
1299 raw.as_mut_ptr(),
1300 result.as_mut_ptr(),
1301 false,
1302 );
1303 }
1304 result
1305 .into_iter()
1306 .zip(raw)
1307 .map(|(value, ptr)| {
1308 if value > 0 {
1309 SendOutcome::Sent(value as _)
1310 } else if value < 0 {
1311 let result = unsafe { core::mem::transmute::<u32, EResult>((-value) as u32) };
1315 SendOutcome::Failed(result, GnsNetworkMessage(ptr, PhantomData))
1316 } else {
1317 SendOutcome::Skipped(GnsNetworkMessage(ptr, PhantomData))
1318 }
1319 })
1320 .collect()
1321 }
1322}
1323
1324impl GnsSocket<IsCreated> {
1325 unsafe extern "C" fn on_connection_state_changed(
1331 info: &mut SteamNetConnectionStatusChangedCallback_t,
1332 ) {
1333 let gns_global = GnsGlobal::get()
1334 .expect("GnsGlobal should be initialized");
1336
1337 let queue_id = info.m_info.m_nUserData as _;
1338 let needs_purge = {
1341 let queues = gns_global.event_queues.read().unwrap();
1342 match queues.get(&queue_id).and_then(Weak::upgrade) {
1343 Some(queue) => {
1344 queue.push(GnsConnectionEvent(*info));
1345 false
1346 }
1347 None => queues.contains_key(&queue_id),
1348 }
1349 };
1350 if needs_purge {
1355 gns_global.event_queues.write().unwrap().remove(&queue_id);
1356 }
1357 }
1358
1359 #[inline]
1361 pub fn new(global: &'static GnsGlobal) -> Self {
1362 GnsSocket {
1363 global,
1364 state: IsCreated,
1365 }
1366 }
1367
1368 fn setup_common(
1369 address: IpAddr,
1370 port: u16,
1371 queue_id: int64,
1372 ) -> (SteamNetworkingIPAddr, [SteamNetworkingConfigValue_t; 2]) {
1373 let addr = SteamNetworkingIPAddr {
1374 __bindgen_anon_1: match address {
1375 IpAddr::V4(address) => SteamNetworkingIPAddr__bindgen_ty_2 {
1376 m_ipv4: SteamNetworkingIPAddr_IPv4MappedAddress {
1377 m_8zeros: 0,
1378 m_0000: 0,
1379 m_ffff: 0xffff,
1380 m_ip: address.octets(),
1381 },
1382 },
1383 IpAddr::V6(address) => SteamNetworkingIPAddr__bindgen_ty_2 {
1384 m_ipv6: address.octets(),
1385 },
1386 },
1387 m_port: port,
1388 };
1389 let options = [SteamNetworkingConfigValue_t {
1390 m_eDataType: ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Ptr,
1391 m_eValue: ESteamNetworkingConfigValue::k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged,
1392 m_val: SteamNetworkingConfigValue_t__bindgen_ty_1 {
1393 m_ptr: Self::on_connection_state_changed as *const fn(&SteamNetConnectionStatusChangedCallback_t) as *mut c_void
1394 }
1395 }, SteamNetworkingConfigValue_t {
1396 m_eDataType: ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Int64,
1397 m_eValue: ESteamNetworkingConfigValue::k_ESteamNetworkingConfig_ConnectionUserData,
1398 m_val: SteamNetworkingConfigValue_t__bindgen_ty_1 {
1399 m_int64: queue_id
1400 }
1401 }];
1402 (addr, options)
1403 }
1404
1405 pub fn listen(self, address: IpAddr, port: u16) -> GnsResult<GnsSocket<IsServer>> {
1410 let (queue_id, queue) = self.global.create_queue();
1411 let (addr, options) = Self::setup_common(address, port, queue_id);
1412 let listen_socket = unsafe {
1413 SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
1414 get_interface(),
1415 &addr,
1416 options.len() as _,
1417 options.as_ptr(),
1418 )
1419 };
1420 if listen_socket == k_HSteamListenSocket_Invalid {
1421 Err(GnsError::Listen)
1422 } else {
1423 let poll_group =
1424 unsafe { SteamAPI_ISteamNetworkingSockets_CreatePollGroup(get_interface()) };
1425 if poll_group == k_HSteamNetPollGroup_Invalid {
1426 Err(GnsError::Listen)
1427 } else {
1428 Ok(GnsSocket {
1429 global: self.global,
1430 state: IsServer {
1431 queue,
1432 queue_id,
1433 global: self.global,
1434 listen_socket: GnsListenSocket(listen_socket),
1435 poll_group: GnsPollGroup(poll_group),
1436 },
1437 })
1438 }
1439 }
1440 }
1441
1442 pub fn connect(self, address: IpAddr, port: u16) -> GnsResult<GnsSocket<IsClient>> {
1447 let (queue_id, queue) = self.global.create_queue();
1448 let (addr, options) = Self::setup_common(address, port, queue_id);
1449 let connection = unsafe {
1450 SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress(
1451 get_interface(),
1452 &addr,
1453 options.len() as _,
1454 options.as_ptr(),
1455 )
1456 };
1457 if connection == k_HSteamNetConnection_Invalid {
1458 Err(GnsError::Connect)
1459 } else {
1460 Ok(GnsSocket {
1461 global: self.global,
1462 state: IsClient {
1463 queue,
1464 queue_id,
1465 global: self.global,
1466 connection: GnsConnection(connection),
1467 },
1468 })
1469 }
1470 }
1471
1472 pub fn socket_pair(
1486 self,
1487 use_network_loopback: bool,
1488 ) -> GnsResult<(GnsSocket<IsClient>, GnsSocket<IsClient>)> {
1489 let (queue_id_a, queue_a) = self.global.create_queue();
1490 let (queue_id_b, queue_b) = self.global.create_queue();
1491 let mut conn_a = k_HSteamNetConnection_Invalid;
1492 let mut conn_b = k_HSteamNetConnection_Invalid;
1493 let ok = unsafe {
1494 SteamAPI_ISteamNetworkingSockets_CreateSocketPair(
1495 get_interface(),
1496 &mut conn_a,
1497 &mut conn_b,
1498 use_network_loopback,
1499 core::ptr::null(),
1500 core::ptr::null(),
1501 )
1502 };
1503 if !ok {
1504 let mut queues = self.global.event_queues.write().unwrap();
1505 queues.remove(&queue_id_a);
1506 queues.remove(&queue_id_b);
1507 return Err(GnsError::SocketPair);
1508 }
1509 let make_client = |connection, queue, queue_id| GnsSocket {
1512 global: self.global,
1513 state: IsClient {
1514 queue,
1515 queue_id,
1516 global: self.global,
1517 connection: GnsConnection(connection),
1518 },
1519 };
1520 let a = make_client(conn_a, queue_a, queue_id_a);
1521 let b = make_client(conn_b, queue_b, queue_id_b);
1522 for (conn, queue_id) in [(conn_a, queue_id_a), (conn_b, queue_id_b)] {
1525 unsafe {
1526 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
1527 get_interface(),
1528 conn,
1529 queue_id,
1530 );
1531 }
1532 self.global.utils().set_config_value_scoped(
1533 ESteamNetworkingConfigValue::k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged,
1534 ESteamNetworkingConfigScope::k_ESteamNetworkingConfig_Connection,
1535 conn as isize,
1536 GnsConfig::Ptr(Self::on_connection_state_changed as *const fn(&SteamNetConnectionStatusChangedCallback_t) as *mut c_void),
1537 )?;
1538 }
1539 Ok((a, b))
1540 }
1541}
1542
1543impl GnsSocket<IsServer> {
1544 pub fn accept(&self, connection: GnsConnection) -> GnsResult<()> {
1547 check(unsafe {
1548 SteamAPI_ISteamNetworkingSockets_AcceptConnection(get_interface(), connection.0)
1549 })?;
1550 if !unsafe {
1551 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
1552 get_interface(),
1553 connection.0,
1554 self.state.poll_group.0,
1555 )
1556 } {
1557 return Err(GnsError::Accept);
1560 }
1561 Ok(())
1562 }
1563
1564 pub fn get_listen_socket_address(&self) -> Option<(IpAddr, u16)> {
1570 let mut addr: SteamNetworkingIPAddr = unsafe { MaybeUninit::zeroed().assume_init() };
1571 if unsafe {
1572 SteamAPI_ISteamNetworkingSockets_GetListenSocketAddress(
1573 get_interface(),
1574 self.state.listen_socket.0,
1575 &mut addr,
1576 )
1577 } {
1578 Some((ip_from_steam_ip_addr(&addr), addr.m_port))
1579 } else {
1580 None
1581 }
1582 }
1583
1584 pub fn set_listen_socket_config_value(
1588 &self,
1589 typ: ESteamNetworkingConfigValue,
1590 value: GnsConfig<'_>,
1591 ) -> GnsResult<()> {
1592 self.global.utils().set_config_value_scoped(
1593 typ,
1594 ESteamNetworkingConfigScope::k_ESteamNetworkingConfig_ListenSocket,
1595 self.state.listen_socket.0 as isize,
1596 value,
1597 )
1598 }
1599
1600 pub fn get_listen_socket_config_value(
1602 &self,
1603 typ: ESteamNetworkingConfigValue,
1604 ) -> GnsResult<GnsConfigValue> {
1605 self.global.utils().get_config_value_scoped(
1606 typ,
1607 ESteamNetworkingConfigScope::k_ESteamNetworkingConfig_ListenSocket,
1608 self.state.listen_socket.0 as isize,
1609 )
1610 }
1611}
1612
1613impl GnsSocket<IsClient> {
1614 #[inline]
1617 pub fn connection(&self) -> GnsConnection {
1618 self.state.connection
1619 }
1620}
1621
1622#[non_exhaustive]
1629pub enum GnsConfig<'a> {
1630 Float(f32),
1631 Int32(i32),
1632 String(&'a str),
1636 CStr(&'a CStr),
1639 Ptr(*mut c_void),
1640}
1641
1642#[derive(Debug, Clone, PartialEq)]
1652#[non_exhaustive]
1653pub enum GnsConfigValue {
1654 Float(f32),
1655 Int32(i32),
1656 Int64(i64),
1657 String(String),
1658 Ptr(*mut c_void),
1659}
1660
1661pub struct GnsUtils(());
1662
1663type MsgPtr = *const ::std::os::raw::c_char;
1664
1665type DebugCallback = dyn Fn(ESteamNetworkingSocketsDebugOutputType, &str) + Send + Sync + 'static;
1670
1671static DEBUG_CB: OnceLock<Box<DebugCallback>> = OnceLock::new();
1676
1677unsafe extern "C" fn debug_trampoline(ty: ESteamNetworkingSocketsDebugOutputType, msg: MsgPtr) {
1678 if let Some(cb) = DEBUG_CB.get() {
1679 let s = unsafe { CStr::from_ptr(msg) }.to_str().unwrap_or("");
1680 cb(ty, s);
1681 }
1682}
1683
1684impl GnsUtils {
1685 pub fn enable_debug_output(
1694 &self,
1695 ty: ESteamNetworkingSocketsDebugOutputType,
1696 f: impl Fn(ESteamNetworkingSocketsDebugOutputType, &str) + Send + Sync + 'static,
1697 ) {
1698 let _ = DEBUG_CB.set(Box::new(f));
1699 unsafe {
1700 SteamAPI_ISteamNetworkingUtils_SetDebugOutputFunction(
1701 get_utils(),
1702 ty,
1703 Some(debug_trampoline),
1704 );
1705 }
1706 }
1707
1708 #[inline]
1714 pub fn allocate_message<P: Payload>(
1715 &self,
1716 conn: GnsConnection,
1717 flags: SendFlags,
1718 payload: P,
1719 ) -> GnsNetworkMessage<ToSend> {
1720 let message_ptr = unsafe { SteamAPI_ISteamNetworkingUtils_AllocateMessage(get_utils(), 0) };
1721 GnsNetworkMessage::new(message_ptr, conn, flags, payload)
1722 }
1723
1724 pub fn set_global_config_value(
1727 &self,
1728 typ: ESteamNetworkingConfigValue,
1729 value: GnsConfig<'_>,
1730 ) -> GnsResult<()> {
1731 let result = match value {
1732 GnsConfig::Float(x) => unsafe {
1733 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueFloat(get_utils(), typ, x)
1734 },
1735 GnsConfig::Int32(x) => unsafe {
1736 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueInt32(get_utils(), typ, x)
1737 },
1738 GnsConfig::String(x) => {
1739 let c = CString::new(x).map_err(|_| GnsError::Config("interior NUL"))?;
1740 unsafe {
1741 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueString(
1742 get_utils(),
1743 typ,
1744 c.as_ptr(),
1745 )
1746 }
1747 }
1748 GnsConfig::CStr(x) => unsafe {
1749 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueString(
1750 get_utils(),
1751 typ,
1752 x.as_ptr(),
1753 )
1754 },
1755 GnsConfig::Ptr(x) => unsafe {
1756 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValuePtr(get_utils(), typ, x)
1757 },
1758 };
1759 if result {
1760 Ok(())
1761 } else {
1762 Err(GnsError::Config("SetGlobalConfigValue rejected"))
1763 }
1764 }
1765
1766 pub fn set_connection_config_value(
1769 &self,
1770 conn: GnsConnection,
1771 typ: ESteamNetworkingConfigValue,
1772 value: GnsConfig<'_>,
1773 ) -> GnsResult<()> {
1774 let result = match value {
1775 GnsConfig::Float(x) => unsafe {
1776 SteamAPI_ISteamNetworkingUtils_SetConnectionConfigValueFloat(
1777 get_utils(),
1778 conn.0,
1779 typ,
1780 x,
1781 )
1782 },
1783 GnsConfig::Int32(x) => unsafe {
1784 SteamAPI_ISteamNetworkingUtils_SetConnectionConfigValueInt32(
1785 get_utils(),
1786 conn.0,
1787 typ,
1788 x,
1789 )
1790 },
1791 GnsConfig::String(x) => {
1792 let c = CString::new(x).map_err(|_| GnsError::Config("interior NUL"))?;
1793 unsafe {
1794 SteamAPI_ISteamNetworkingUtils_SetConnectionConfigValueString(
1795 get_utils(),
1796 conn.0,
1797 typ,
1798 c.as_ptr(),
1799 )
1800 }
1801 }
1802 GnsConfig::CStr(x) => unsafe {
1803 SteamAPI_ISteamNetworkingUtils_SetConnectionConfigValueString(
1804 get_utils(),
1805 conn.0,
1806 typ,
1807 x.as_ptr(),
1808 )
1809 },
1810 GnsConfig::Ptr(x) => {
1811 return self.set_config_value_scoped(
1812 typ,
1813 ESteamNetworkingConfigScope::k_ESteamNetworkingConfig_Connection,
1814 conn.0 as isize,
1815 GnsConfig::Ptr(x),
1816 )
1817 }
1818 };
1819 if result {
1820 Ok(())
1821 } else {
1822 Err(GnsError::Config("SetConnectionConfigValue rejected"))
1823 }
1824 }
1825
1826 fn set_config_value_scoped(
1829 &self,
1830 typ: ESteamNetworkingConfigValue,
1831 scope: ESteamNetworkingConfigScope,
1832 scope_obj: isize,
1833 value: GnsConfig<'_>,
1834 ) -> GnsResult<()> {
1835 let owned_string;
1837 let (data_type, arg): (ESteamNetworkingConfigDataType, *const c_void) = match &value {
1841 GnsConfig::Float(x) => (
1842 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Float,
1843 x as *const f32 as _,
1844 ),
1845 GnsConfig::Int32(x) => (
1846 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Int32,
1847 x as *const i32 as _,
1848 ),
1849 GnsConfig::String(x) => {
1850 owned_string = CString::new(*x).map_err(|_| GnsError::Config("interior NUL"))?;
1851 (
1852 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_String,
1853 owned_string.as_ptr() as _,
1854 )
1855 }
1856 GnsConfig::CStr(x) => (
1857 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_String,
1858 x.as_ptr() as _,
1859 ),
1860 GnsConfig::Ptr(x) => (
1861 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Ptr,
1862 x as *const *mut c_void as _,
1863 ),
1864 };
1865 if unsafe {
1866 SteamAPI_ISteamNetworkingUtils_SetConfigValue(
1867 get_utils(),
1868 typ,
1869 scope,
1870 scope_obj,
1871 data_type,
1872 arg,
1873 )
1874 } {
1875 Ok(())
1876 } else {
1877 Err(GnsError::Config("SetConfigValue rejected"))
1878 }
1879 }
1880
1881 fn get_config_value_scoped(
1883 &self,
1884 typ: ESteamNetworkingConfigValue,
1885 scope: ESteamNetworkingConfigScope,
1886 scope_obj: isize,
1887 ) -> GnsResult<GnsConfigValue> {
1888 let mut data_type = ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Int32;
1889 let mut buf = vec![0u8; 64];
1890 let mut len = buf.len();
1891 loop {
1892 let result = unsafe {
1893 SteamAPI_ISteamNetworkingUtils_GetConfigValue(
1894 get_utils(),
1895 typ,
1896 scope,
1897 scope_obj,
1898 &mut data_type,
1899 buf.as_mut_ptr() as *mut c_void,
1900 &mut len,
1901 )
1902 };
1903 match result {
1904 ESteamNetworkingGetConfigValueResult::k_ESteamNetworkingGetConfigValue_OK
1905 | ESteamNetworkingGetConfigValueResult::k_ESteamNetworkingGetConfigValue_OKInherited => {
1906 break
1907 }
1908 ESteamNetworkingGetConfigValueResult::k_ESteamNetworkingGetConfigValue_BufferTooSmall => {
1909 buf.resize(len, 0);
1911 }
1912 ESteamNetworkingGetConfigValueResult::k_ESteamNetworkingGetConfigValue_BadValue => {
1913 return Err(GnsError::Config("unknown config value"))
1914 }
1915 ESteamNetworkingGetConfigValueResult::k_ESteamNetworkingGetConfigValue_BadScopeObj => {
1916 return Err(GnsError::Config("bad scope object"))
1917 }
1918 _ => return Err(GnsError::Config("GetConfigValue failed")),
1919 }
1920 }
1921 let value = match data_type {
1923 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Int32 => {
1924 GnsConfigValue::Int32(unsafe { (buf.as_ptr() as *const i32).read_unaligned() })
1925 }
1926 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Int64 => {
1927 GnsConfigValue::Int64(unsafe { (buf.as_ptr() as *const i64).read_unaligned() })
1928 }
1929 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Float => {
1930 GnsConfigValue::Float(unsafe { (buf.as_ptr() as *const f32).read_unaligned() })
1931 }
1932 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_String => {
1933 let s = CStr::from_bytes_until_nul(&buf)
1934 .map_err(|_| GnsError::Config("string value missing NUL"))?;
1935 GnsConfigValue::String(s.to_string_lossy().into_owned())
1936 }
1937 ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Ptr => {
1938 GnsConfigValue::Ptr(unsafe {
1939 (buf.as_ptr() as *const *mut c_void).read_unaligned()
1940 })
1941 }
1942 _ => return Err(GnsError::Config("unknown config data type")),
1943 };
1944 Ok(value)
1945 }
1946
1947 #[inline]
1950 pub fn get_global_config_value(
1951 &self,
1952 typ: ESteamNetworkingConfigValue,
1953 ) -> GnsResult<GnsConfigValue> {
1954 self.get_config_value_scoped(
1955 typ,
1956 ESteamNetworkingConfigScope::k_ESteamNetworkingConfig_Global,
1957 0,
1958 )
1959 }
1960
1961 #[inline]
1967 pub fn get_connection_config_value(
1968 &self,
1969 conn: GnsConnection,
1970 typ: ESteamNetworkingConfigValue,
1971 ) -> GnsResult<GnsConfigValue> {
1972 self.get_config_value_scoped(
1973 typ,
1974 ESteamNetworkingConfigScope::k_ESteamNetworkingConfig_Connection,
1975 conn.0 as isize,
1976 )
1977 }
1978
1979 pub fn get_config_value_info(
1982 &self,
1983 typ: ESteamNetworkingConfigValue,
1984 ) -> Option<(
1985 &'static str,
1986 ESteamNetworkingConfigDataType,
1987 ESteamNetworkingConfigScope,
1988 )> {
1989 let mut data_type = ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Int32;
1990 let mut scope = ESteamNetworkingConfigScope::k_ESteamNetworkingConfig_Global;
1991 let name = unsafe {
1992 SteamAPI_ISteamNetworkingUtils_GetConfigValueInfo(
1993 get_utils(),
1994 typ,
1995 &mut data_type,
1996 &mut scope,
1997 )
1998 };
1999 if name.is_null() {
2000 None
2001 } else {
2002 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap_or("");
2004 Some((name, data_type, scope))
2005 }
2006 }
2007
2008 #[inline]
2013 pub fn local_timestamp(&self) -> SteamNetworkingMicroseconds {
2014 unsafe { SteamAPI_ISteamNetworkingUtils_GetLocalTimestamp(get_utils()) }
2015 }
2016}