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