1use crossbeam_queue::SegQueue;
62use either::Either;
63pub use gns_sys as sys;
64use std::sync::atomic::{AtomicI64, Ordering};
65use std::{
66 collections::HashMap,
67 ffi::{c_void, CStr, CString},
68 marker::PhantomData,
69 mem::MaybeUninit,
70 net::{IpAddr, Ipv4Addr, Ipv6Addr},
71 sync::{Arc, Mutex, Weak},
72 time::Duration,
73};
74use sys::*;
75
76fn get_interface() -> *mut ISteamNetworkingSockets {
77 unsafe { SteamAPI_SteamNetworkingSockets_v009() }
78}
79
80fn get_utils() -> *mut ISteamNetworkingUtils {
81 unsafe { SteamAPI_SteamNetworkingUtils_v003() }
82}
83
84pub type GnsMessageNumber = u64;
86
87pub type GnsResult<T> = Result<T, EResult>;
90
91#[repr(transparent)]
94#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
95pub struct GnsError(EResult);
96
97impl Into<EResult> for GnsError {
98 fn into(self) -> EResult {
99 self.0
100 }
101}
102
103impl GnsError {
104 pub fn into_result(self) -> GnsResult<()> {
105 self.into()
106 }
107}
108
109impl From<GnsError> for GnsResult<()> {
110 fn from(GnsError(result): GnsError) -> Self {
111 match result {
112 EResult::k_EResultOK => Ok(()),
113 e => Err(e),
114 }
115 }
116}
117
118pub struct GnsGlobal {
124 utils: GnsUtils,
125 next_queue_id: AtomicI64,
126 event_queues: Mutex<HashMap<i64, Weak<SegQueue<GnsConnectionEvent>>>>,
127}
128
129static GNS_GLOBAL: Mutex<Option<Arc<GnsGlobal>>> = Mutex::new(None);
130
131impl GnsGlobal {
132 pub fn get() -> Result<Arc<Self>, String> {
145 let mut lock = GNS_GLOBAL.lock().unwrap();
146 if let Some(gns_global) = lock.clone() {
147 Ok(gns_global)
148 } else {
149 unsafe {
150 let mut error: SteamDatagramErrMsg = MaybeUninit::zeroed().assume_init();
151 if !GameNetworkingSockets_Init(core::ptr::null(), &mut error) {
152 Err(format!(
153 "{}",
154 CStr::from_ptr(error.as_ptr()).to_str().unwrap_or("")
155 ))
156 } else {
157 let gns_global = Arc::new(GnsGlobal {
158 utils: GnsUtils(()),
159 next_queue_id: AtomicI64::new(0),
160 event_queues: Mutex::new(HashMap::new()),
161 });
162 *lock = Some(gns_global.clone());
163 Ok(gns_global)
164 }
165 }
166 }
167 }
168
169 #[inline]
170 pub fn poll_callbacks(&self) {
171 unsafe {
172 SteamAPI_ISteamNetworkingSockets_RunCallbacks(get_interface());
173 }
174 }
175
176 pub fn utils(&self) -> &GnsUtils {
177 &self.utils
178 }
179
180 fn create_queue(&self) -> (i64, Arc<SegQueue<GnsConnectionEvent>>) {
181 let queue = Arc::new(SegQueue::new());
182 let queue_id = self.next_queue_id.fetch_add(1, Ordering::SeqCst);
183 self.event_queues
184 .lock()
185 .unwrap()
186 .insert(queue_id, Arc::downgrade(&queue));
187 (queue_id, queue)
188 }
189}
190
191#[repr(transparent)]
193pub struct GnsListenSocket(HSteamListenSocket);
194
195#[repr(transparent)]
197pub struct GnsPollGroup(HSteamNetPollGroup);
198
199pub struct IsCreated;
203
204pub trait IsReady {
207 fn queue(&self) -> &SegQueue<GnsConnectionEvent>;
209 fn receive<const K: usize>(&self, messages: &mut [GnsNetworkMessage<ToReceive>; K]) -> usize;
212}
213
214pub struct IsServer {
217 queue: Arc<SegQueue<GnsConnectionEvent>>,
222 listen_socket: GnsListenSocket,
224 poll_group: GnsPollGroup,
226}
227
228impl Drop for IsServer {
229 fn drop(&mut self) {
230 unsafe {
231 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
232 get_interface(),
233 self.listen_socket.0,
234 );
235 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup(get_interface(), self.poll_group.0);
236 }
237 }
238}
239
240impl IsReady for IsServer {
241 #[inline]
242 fn queue(&self) -> &SegQueue<GnsConnectionEvent> {
243 &self.queue
244 }
245
246 #[inline]
247 fn receive<const K: usize>(&self, messages: &mut [GnsNetworkMessage<ToReceive>; K]) -> usize {
248 unsafe {
249 SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
250 get_interface(),
251 self.poll_group.0,
252 messages.as_mut_ptr() as _,
253 K as _,
254 ) as _
255 }
256 }
257}
258
259pub struct IsClient {
262 queue: Arc<SegQueue<GnsConnectionEvent>>,
264 connection: GnsConnection,
266}
267
268impl Drop for IsClient {
269 fn drop(&mut self) {
270 unsafe {
271 SteamAPI_ISteamNetworkingSockets_CloseConnection(
272 get_interface(),
273 self.connection.0,
274 0,
275 core::ptr::null(),
276 false,
277 );
278 }
279 }
280}
281
282impl IsReady for IsClient {
283 #[inline]
284 fn queue(&self) -> &SegQueue<GnsConnectionEvent> {
285 &self.queue
286 }
287
288 #[inline]
289 fn receive<const K: usize>(&self, messages: &mut [GnsNetworkMessage<ToReceive>; K]) -> usize {
290 unsafe {
291 SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnConnection(
292 get_interface(),
293 self.connection.0,
294 messages.as_mut_ptr() as _,
295 K as _,
296 ) as _
297 }
298 }
299}
300
301pub trait MayDrop {
302 const MUST_DROP: bool;
303}
304
305pub struct ToSend(());
306
307impl MayDrop for ToSend {
308 const MUST_DROP: bool = false;
309}
310
311pub struct ToReceive(());
312
313impl MayDrop for ToReceive {
314 const MUST_DROP: bool = true;
315}
316
317pub type Priority = u32;
319pub type Weight = u16;
321pub type GnsLane = (Priority, Weight);
323pub type GnsLaneId = u16;
325
326#[repr(transparent)]
333pub struct GnsNetworkMessage<T: MayDrop>(*mut ISteamNetworkingMessage, PhantomData<T>);
334
335impl<T> Drop for GnsNetworkMessage<T>
336where
337 T: MayDrop,
338{
339 fn drop(&mut self) {
340 if T::MUST_DROP && !self.0.is_null() {
341 unsafe {
342 SteamAPI_SteamNetworkingMessage_t_Release(self.0);
343 }
344 }
345 }
346}
347
348impl<T> GnsNetworkMessage<T>
349where
350 T: MayDrop,
351{
352 #[inline]
354 pub unsafe fn into_inner(self) -> *mut ISteamNetworkingMessage {
355 self.0
356 }
357
358 #[inline]
359 pub fn payload(&self) -> &[u8] {
360 unsafe {
361 core::slice::from_raw_parts((*self.0).m_pData as *const u8, (*self.0).m_cbSize as _)
362 }
363 }
364
365 #[inline]
366 pub fn message_number(&self) -> u64 {
367 unsafe { (*self.0).m_nMessageNumber as _ }
368 }
369
370 #[inline]
371 pub fn lane(&self) -> GnsLaneId {
372 unsafe { (*self.0).m_idxLane }
373 }
374
375 #[inline]
376 pub fn flags(&self) -> i32 {
377 unsafe { (*self.0).m_nFlags as _ }
378 }
379
380 #[inline]
381 pub fn user_data(&self) -> u64 {
382 unsafe { (*self.0).m_nUserData as _ }
383 }
384
385 #[inline]
386 pub fn connection(&self) -> GnsConnection {
387 GnsConnection(unsafe { (*self.0).m_conn })
388 }
389
390 pub fn connection_user_data(&self) -> u64 {
391 unsafe { (*self.0).m_nConnUserData as _ }
392 }
393}
394
395impl GnsNetworkMessage<ToSend> {
396 #[inline]
397 fn new(
398 ptr: *mut ISteamNetworkingMessage,
399 conn: GnsConnection,
400 flags: i32,
401 payload: &[u8],
402 ) -> Self {
403 GnsNetworkMessage(ptr, PhantomData)
404 .set_flags(flags)
405 .set_payload(payload)
406 .set_connection(conn)
407 }
408
409 #[inline]
410 pub fn set_connection(self, GnsConnection(conn): GnsConnection) -> Self {
411 unsafe { (*self.0).m_conn = conn }
412 self
413 }
414
415 #[inline]
416 pub fn set_payload(self, payload: &[u8]) -> Self {
417 unsafe {
418 core::ptr::copy_nonoverlapping(
419 payload.as_ptr(),
420 (*self.0).m_pData as *mut u8,
421 payload.len(),
422 );
423 }
424 self
425 }
426
427 #[inline]
428 pub fn set_lane(self, lane: u16) -> Self {
429 unsafe { (*self.0).m_idxLane = lane }
430 self
431 }
432
433 #[inline]
434 pub fn set_flags(self, flags: i32) -> Self {
435 unsafe { (*self.0).m_nFlags = flags as _ }
436 self
437 }
438
439 #[inline]
440 pub fn set_user_data(self, userdata: u64) -> Self {
441 unsafe { (*self.0).m_nUserData = userdata as _ }
442 self
443 }
444}
445
446#[repr(transparent)]
447#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
448pub struct GnsConnection(HSteamNetConnection);
449
450#[derive(Default, Copy, Clone)]
451pub struct GnsConnectionInfo(SteamNetConnectionInfo_t);
452
453impl GnsConnectionInfo {
454 #[inline]
455 pub fn state(&self) -> ESteamNetworkingConnectionState {
456 self.0.m_eState
457 }
458
459 #[inline]
460 pub fn end_reason(&self) -> u32 {
461 self.0.m_eEndReason as u32
462 }
463
464 #[inline]
465 pub fn end_debug(&self) -> &str {
466 unsafe { CStr::from_ptr(self.0.m_szEndDebug.as_ptr()) }
467 .to_str()
468 .unwrap_or("")
469 }
470
471 #[inline]
472 pub fn remote_address(&self) -> IpAddr {
473 let ipv4 = unsafe { self.0.m_addrRemote.__bindgen_anon_1.m_ipv4 };
474 if ipv4.m_8zeros == 0 && ipv4.m_0000 == 0 && ipv4.m_ffff == 0xffff {
475 IpAddr::from(Ipv4Addr::from(ipv4.m_ip))
476 } else {
477 IpAddr::from(Ipv6Addr::from(unsafe {
478 self.0.m_addrRemote.__bindgen_anon_1.m_ipv6
479 }))
480 }
481 }
482
483 #[inline]
484 pub fn remote_port(&self) -> u16 {
485 self.0.m_addrRemote.m_port
486 }
487}
488
489#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
490pub struct GnsConnectionRealTimeLaneStatus(SteamNetConnectionRealTimeLaneStatus_t);
491
492impl GnsConnectionRealTimeLaneStatus {
493 #[inline]
494 pub fn pending_bytes_unreliable(&self) -> u32 {
495 self.0.m_cbPendingUnreliable as _
496 }
497
498 #[inline]
499 pub fn pending_bytes_reliable(&self) -> u32 {
500 self.0.m_cbPendingReliable as _
501 }
502
503 #[inline]
504 pub fn bytes_sent_unacked_reliable(&self) -> u32 {
505 self.0.m_cbSentUnackedReliable as _
506 }
507
508 #[inline]
509 pub fn approximated_queue_time(&self) -> Duration {
510 Duration::from_micros(self.0.m_usecQueueTime as _)
511 }
512}
513
514#[derive(Default, Debug, Copy, Clone, PartialOrd, PartialEq)]
515pub struct GnsConnectionRealTimeStatus(SteamNetConnectionRealTimeStatus_t);
516
517impl GnsConnectionRealTimeStatus {
518 #[inline]
519 pub fn state(&self) -> ESteamNetworkingConnectionState {
520 self.0.m_eState
521 }
522
523 #[inline]
524 pub fn ping(&self) -> u32 {
525 self.0.m_nPing as _
526 }
527
528 #[inline]
529 pub fn quality_local(&self) -> f32 {
530 self.0.m_flConnectionQualityLocal
531 }
532
533 #[inline]
534 pub fn quality_remote(&self) -> f32 {
535 self.0.m_flConnectionQualityRemote
536 }
537
538 #[inline]
539 pub fn out_packets_per_sec(&self) -> f32 {
540 self.0.m_flOutPacketsPerSec
541 }
542
543 #[inline]
544 pub fn out_bytes_per_sec(&self) -> f32 {
545 self.0.m_flOutBytesPerSec
546 }
547
548 #[inline]
549 pub fn in_packets_per_sec(&self) -> f32 {
550 self.0.m_flInPacketsPerSec
551 }
552
553 #[inline]
554 pub fn in_bytes_per_sec(&self) -> f32 {
555 self.0.m_flInBytesPerSec
556 }
557
558 #[inline]
559 pub fn send_rate_bytes_per_sec(&self) -> u32 {
560 self.0.m_nSendRateBytesPerSecond as _
561 }
562
563 #[inline]
564 pub fn pending_bytes_unreliable(&self) -> u32 {
565 self.0.m_cbPendingUnreliable as _
566 }
567
568 #[inline]
569 pub fn pending_bytes_reliable(&self) -> u32 {
570 self.0.m_cbPendingReliable as _
571 }
572
573 #[inline]
574 pub fn bytes_sent_unacked_reliable(&self) -> u32 {
575 self.0.m_cbSentUnackedReliable as _
576 }
577
578 #[inline]
579 pub fn approximated_queue_time(&self) -> Duration {
580 Duration::from_micros(self.0.m_usecQueueTime as _)
581 }
582}
583
584#[derive(Default, Copy, Clone)]
585pub struct GnsConnectionEvent(SteamNetConnectionStatusChangedCallback_t);
586
587impl GnsConnectionEvent {
588 #[inline]
589 pub fn old_state(&self) -> ESteamNetworkingConnectionState {
590 self.0.m_eOldState
591 }
592
593 #[inline]
594 pub fn connection(&self) -> GnsConnection {
595 GnsConnection(self.0.m_hConn)
596 }
597
598 #[inline]
599 pub fn info(&self) -> GnsConnectionInfo {
600 GnsConnectionInfo(self.0.m_info)
601 }
602}
603
604pub struct GnsSocket<S> {
608 global: Arc<GnsGlobal>,
609 state: S,
610}
611
612impl<S> GnsSocket<S>
613where
614 S: IsReady,
615{
616 #[inline]
619 pub fn get_connection_real_time_status(
620 &self,
621 GnsConnection(conn): GnsConnection,
622 nb_of_lanes: u32,
623 ) -> GnsResult<(
624 GnsConnectionRealTimeStatus,
625 Vec<GnsConnectionRealTimeLaneStatus>,
626 )> {
627 let mut lanes: Vec<GnsConnectionRealTimeLaneStatus> =
628 vec![Default::default(); nb_of_lanes as _];
629 let mut status: GnsConnectionRealTimeStatus = Default::default();
630 GnsError(unsafe {
631 SteamAPI_ISteamNetworkingSockets_GetConnectionRealTimeStatus(
632 get_interface(),
633 conn,
634 &mut status as *mut GnsConnectionRealTimeStatus
635 as *mut SteamNetConnectionRealTimeStatus_t,
636 nb_of_lanes as _,
637 lanes.as_mut_ptr() as *mut GnsConnectionRealTimeLaneStatus
638 as *mut SteamNetConnectionRealTimeLaneStatus_t,
639 )
640 })
641 .into_result()?;
642 Ok((status, lanes))
643 }
644
645 #[inline]
646 pub fn get_connection_info(
647 &self,
648 GnsConnection(conn): GnsConnection,
649 ) -> Option<GnsConnectionInfo> {
650 let mut info: SteamNetConnectionInfo_t = Default::default();
651 if unsafe {
652 SteamAPI_ISteamNetworkingSockets_GetConnectionInfo(get_interface(), conn, &mut info)
653 } {
654 Some(GnsConnectionInfo(info))
655 } else {
656 None
657 }
658 }
659
660 #[inline]
661 pub fn flush_messages_on_connection(
662 &self,
663 GnsConnection(conn): GnsConnection,
664 ) -> GnsResult<()> {
665 GnsError(unsafe {
666 SteamAPI_ISteamNetworkingSockets_FlushMessagesOnConnection(get_interface(), conn)
667 })
668 .into_result()
669 }
670
671 #[inline]
672 pub fn close_connection(
673 &self,
674 GnsConnection(conn): GnsConnection,
675 reason: u32,
676 debug: &str,
677 linger: bool,
678 ) -> bool {
679 let debug_c = CString::new(debug).expect("str; qed;");
680 unsafe {
681 SteamAPI_ISteamNetworkingSockets_CloseConnection(
682 get_interface(),
683 conn,
684 reason as _,
685 debug_c.as_ptr(),
686 linger,
687 )
688 }
689 }
690
691 #[inline]
692 pub fn poll_messages<const K: usize>(
693 &self,
694 mut message_callback: impl FnMut(&GnsNetworkMessage<ToReceive>),
695 ) -> Option<usize> {
696 let mut messages: [GnsNetworkMessage<ToReceive>; K] =
698 unsafe { MaybeUninit::zeroed().assume_init() };
699 let nb_of_messages = self.state.receive(&mut messages);
700 if nb_of_messages == usize::MAX {
701 None
702 } else {
703 for message in messages.into_iter().take(nb_of_messages) {
704 message_callback(&message);
705 }
706 Some(nb_of_messages)
707 }
708 }
709
710 #[inline]
711 pub fn poll_event<const K: usize>(
712 &self,
713 mut event_callback: impl FnMut(GnsConnectionEvent),
714 ) -> usize {
715 let mut processed = 0;
716 'a: while let Some(event) = self.state.queue().pop() {
717 event_callback(event);
718 processed += 1;
719 if processed == K {
720 break 'a;
721 }
722 }
723 processed
724 }
725
726 #[inline]
727 pub fn configure_connection_lanes(
728 &self,
729 GnsConnection(connection): GnsConnection,
730 lanes: &[GnsLane],
731 ) -> GnsResult<()> {
732 let (priorities, weights): (Vec<_>, Vec<_>) = lanes.iter().copied().unzip();
733 GnsError(unsafe {
734 SteamAPI_ISteamNetworkingSockets_ConfigureConnectionLanes(
735 get_interface(),
736 connection,
737 lanes.len() as _,
738 priorities.as_ptr() as *const u32 as *const i32,
739 weights.as_ptr(),
740 )
741 })
742 .into_result()
743 }
744
745 #[inline]
746 pub fn send_messages(
747 &self,
748 messages: Vec<GnsNetworkMessage<ToSend>>,
749 ) -> Vec<Either<GnsMessageNumber, EResult>> {
750 let mut result = vec![0i64; messages.len()];
751 unsafe {
752 SteamAPI_ISteamNetworkingSockets_SendMessages(
753 get_interface(),
754 messages.len() as _,
755 messages.as_ptr() as *const _,
756 result.as_mut_ptr(),
757 );
758 }
759 result
760 .into_iter()
761 .map(|value| {
762 if value < 0 {
763 Either::Right(unsafe { core::mem::transmute((-value) as u32) })
764 } else {
765 Either::Left(value as _)
766 }
767 })
768 .collect()
769 }
770}
771
772impl GnsSocket<IsCreated> {
773 unsafe extern "C" fn on_connection_state_changed(
776 info: &mut SteamNetConnectionStatusChangedCallback_t,
777 ) {
778 let gns_global = GnsGlobal::get()
779 .expect("GnsGlobal should be initialized");
781
782 let queue_id = info.m_info.m_nUserData as _;
783 let mut queues = gns_global.event_queues.lock().unwrap();
784 if let Some(queue) = queues.get(&queue_id) {
785 if let Some(queue) = queue.upgrade() {
786 queue.push(GnsConnectionEvent(*info));
787 } else {
788 queues.remove(&queue_id);
790 }
791 }
792 }
793
794 #[inline]
796 pub fn new(global: Arc<GnsGlobal>) -> Self {
797 GnsSocket {
798 global,
799 state: IsCreated,
800 }
801 }
802
803 #[inline]
804 fn setup_common(
805 address: IpAddr,
806 port: u16,
807 queue_id: int64,
808 ) -> (SteamNetworkingIPAddr, [SteamNetworkingConfigValue_t; 2]) {
809 let addr = SteamNetworkingIPAddr {
810 __bindgen_anon_1: match address {
811 IpAddr::V4(address) => SteamNetworkingIPAddr__bindgen_ty_2 {
812 m_ipv4: SteamNetworkingIPAddr_IPv4MappedAddress {
813 m_8zeros: 0,
814 m_0000: 0,
815 m_ffff: 0xffff,
816 m_ip: address.octets(),
817 },
818 },
819 IpAddr::V6(address) => SteamNetworkingIPAddr__bindgen_ty_2 {
820 m_ipv6: address.octets(),
821 },
822 },
823 m_port: port,
824 };
825 let options = [SteamNetworkingConfigValue_t {
826 m_eDataType: ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Ptr,
827 m_eValue: ESteamNetworkingConfigValue::k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged,
828 m_val: SteamNetworkingConfigValue_t__bindgen_ty_1 {
829 m_ptr: Self::on_connection_state_changed as *const fn(&SteamNetConnectionStatusChangedCallback_t) as *mut c_void
830 }
831 }, SteamNetworkingConfigValue_t {
832 m_eDataType: ESteamNetworkingConfigDataType::k_ESteamNetworkingConfig_Int64,
833 m_eValue: ESteamNetworkingConfigValue::k_ESteamNetworkingConfig_ConnectionUserData,
834 m_val: SteamNetworkingConfigValue_t__bindgen_ty_1 {
835 m_int64: queue_id
836 }
837 }];
838 (addr, options)
839 }
840
841 #[inline]
843 pub fn listen(self, address: IpAddr, port: u16) -> Result<GnsSocket<IsServer>, ()> {
844 let (queue_id, queue) = self.global.create_queue();
845 let (addr, options) = Self::setup_common(address, port, queue_id);
846 let listen_socket = unsafe {
847 SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
848 get_interface(),
849 &addr,
850 options.len() as _,
851 options.as_ptr(),
852 )
853 };
854 if listen_socket == k_HSteamListenSocket_Invalid {
855 Err(())
856 } else {
857 let poll_group =
858 unsafe { SteamAPI_ISteamNetworkingSockets_CreatePollGroup(get_interface()) };
859 if poll_group == k_HSteamNetPollGroup_Invalid {
860 Err(())
861 } else {
862 Ok(GnsSocket {
863 global: self.global,
864 state: IsServer {
865 queue,
866 listen_socket: GnsListenSocket(listen_socket),
867 poll_group: GnsPollGroup(poll_group),
868 },
869 })
870 }
871 }
872 }
873
874 #[inline]
876 pub fn connect(self, address: IpAddr, port: u16) -> Result<GnsSocket<IsClient>, ()> {
877 let (queue_id, queue) = self.global.create_queue();
878 let (addr, options) = Self::setup_common(address, port, queue_id);
879 let connection = unsafe {
880 SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress(
881 get_interface(),
882 &addr,
883 options.len() as _,
884 options.as_ptr(),
885 )
886 };
887 if connection == k_HSteamNetConnection_Invalid {
888 Err(())
889 } else {
890 Ok(GnsSocket {
891 global: self.global,
892 state: IsClient {
893 queue,
894 connection: GnsConnection(connection),
895 },
896 })
897 }
898 }
899}
900
901impl GnsSocket<IsServer> {
902 #[inline]
904 pub fn accept(&self, connection: GnsConnection) -> GnsResult<()> {
905 GnsError(unsafe {
906 SteamAPI_ISteamNetworkingSockets_AcceptConnection(get_interface(), connection.0)
907 })
908 .into_result()?;
909 if !unsafe {
910 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
911 get_interface(),
912 connection.0,
913 self.state.poll_group.0,
914 )
915 } {
916 panic!("It's impossible not to be able to set the connection poll group as both the poll group and the connection must be valid at this point.");
917 }
918 Ok(())
919 }
920}
921
922impl GnsSocket<IsClient> {
923 #[inline]
925 pub fn connection(&self) -> GnsConnection {
926 self.state.connection
927 }
928}
929
930pub enum GnsConfig<'a> {
932 Float(f32),
933 Int32(u32),
934 String(&'a str),
935 Ptr(*mut c_void),
936}
937
938pub struct GnsUtils(());
939
940type MsgPtr = *const ::std::os::raw::c_char;
941
942impl GnsUtils {
943 #[inline]
944 pub fn enable_debug_output(
945 &self,
946 ty: ESteamNetworkingSocketsDebugOutputType,
947 f: fn(ty: ESteamNetworkingSocketsDebugOutputType, msg: String),
948 ) {
949 static mut F: Option<fn(ty: ESteamNetworkingSocketsDebugOutputType, msg: String)> = None;
950 unsafe {
951 F = Some(f);
952 }
953 unsafe extern "C" fn debug(ty: ESteamNetworkingSocketsDebugOutputType, msg: MsgPtr) {
954 F.unwrap()(ty, CStr::from_ptr(msg).to_string_lossy().to_string());
955 }
956 unsafe {
957 SteamAPI_ISteamNetworkingUtils_SetDebugOutputFunction(get_utils(), ty, Some(debug));
958 }
959 }
960
961 #[inline]
964 pub fn allocate_message(
965 &self,
966 conn: GnsConnection,
967 flags: i32,
968 payload: &[u8],
969 ) -> GnsNetworkMessage<ToSend> {
970 let message_ptr = unsafe {
971 SteamAPI_ISteamNetworkingUtils_AllocateMessage(get_utils(), payload.len() as _)
972 };
973 GnsNetworkMessage::new(message_ptr, conn, flags, payload)
974 }
975
976 #[inline]
978 pub fn set_global_config_value<'a>(
979 &self,
980 typ: ESteamNetworkingConfigValue,
981 value: GnsConfig<'a>,
982 ) -> Result<(), ()> {
983 let result = match value {
984 GnsConfig::Float(x) => unsafe {
985 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueFloat(get_utils(), typ, x)
986 },
987 GnsConfig::Int32(x) => unsafe {
988 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueInt32(get_utils(), typ, x as i32)
989 },
990 GnsConfig::String(x) => unsafe {
991 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValueString(
992 get_utils(),
993 typ,
994 CString::new(x).expect("str; qed;").as_c_str().as_ptr(),
995 )
996 },
997 GnsConfig::Ptr(x) => unsafe {
998 SteamAPI_ISteamNetworkingUtils_SetGlobalConfigValuePtr(get_utils(), typ, x)
999 },
1000 };
1001 if result {
1002 Ok(())
1003 } else {
1004 Err(())
1005 }
1006 }
1007}