1use super::*;
2use crate::networking_types::NetworkingIdentity;
3#[cfg(test)]
4use serial_test::serial;
5use std::net::{Ipv4Addr, SocketAddrV4};
6
7#[derive(Clone)]
12pub struct Server {
13    inner: Arc<Inner>,
14    server: *mut sys::ISteamGameServer,
15}
16
17unsafe impl Send for Server {}
18unsafe impl Sync for Server {}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum ServerMode {
23    NoAuthentication,
27    Authentication,
32    AuthenticationAndSecure,
37}
38
39pub const QUERY_PORT_SHARED: u16 = sys::STEAMGAMESERVER_QUERY_PORT_SHARED;
45
46impl Server {
47    fn steam_game_server_init_ex(
48        un_ip: std::ffi::c_uint,
49        us_game_port: std::ffi::c_ushort,
50        us_query_port: std::ffi::c_ushort,
51        e_server_mode: EServerMode,
52        pch_version_string: *const c_char,
53        p_out_err_msg: *mut SteamErrMsg,
54    ) -> ESteamAPIInitResult {
55        let versions: Vec<&[u8]> = vec![
56            sys::STEAMUTILS_INTERFACE_VERSION,
57            sys::STEAMNETWORKINGUTILS_INTERFACE_VERSION,
58            sys::STEAMGAMESERVER_INTERFACE_VERSION,
59            sys::STEAMGAMESERVERSTATS_INTERFACE_VERSION,
60            sys::STEAMHTTP_INTERFACE_VERSION,
61            sys::STEAMINVENTORY_INTERFACE_VERSION,
62            sys::STEAMNETWORKING_INTERFACE_VERSION,
63            sys::STEAMNETWORKINGMESSAGES_INTERFACE_VERSION,
64            sys::STEAMNETWORKINGSOCKETS_INTERFACE_VERSION,
65            sys::STEAMUGC_INTERFACE_VERSION,
66            b"\0",
67        ];
68
69        let merged_versions: Vec<u8> = versions.into_iter().flatten().cloned().collect();
70        let merged_versions_ptr = merged_versions.as_ptr().cast::<::std::os::raw::c_char>();
71
72        return unsafe {
73            sys::SteamInternal_GameServer_Init_V2(
74                un_ip,
75                us_game_port,
76                us_query_port,
77                e_server_mode,
78                pch_version_string,
79                merged_versions_ptr,
80                p_out_err_msg,
81            )
82        };
83    }
84
85    pub fn init(
105        ip: Ipv4Addr,
106        game_port: u16,
107        query_port: u16,
108        server_mode: ServerMode,
109        version: &str,
110    ) -> SIResult<(Server, Client)> {
111        unsafe {
112            let version = CString::new(version).unwrap();
113
114            let raw_ip: u32 = ip.into();
117            let server_mode = match server_mode {
118                ServerMode::NoAuthentication => sys::EServerMode::eServerModeNoAuthentication,
119                ServerMode::Authentication => sys::EServerMode::eServerModeAuthentication,
120                ServerMode::AuthenticationAndSecure => {
121                    sys::EServerMode::eServerModeAuthenticationAndSecure
122                }
123            };
124
125            let mut err_msg: sys::SteamErrMsg = [0; 1024];
126            let result = Self::steam_game_server_init_ex(
127                raw_ip,
128                game_port,
129                query_port,
130                server_mode,
131                version.as_ptr(),
132                &mut err_msg,
133            );
134
135            if result != sys::ESteamAPIInitResult::k_ESteamAPIInitResult_OK {
136                return Err(SteamAPIInitError::from_result_and_message(result, err_msg));
137            }
138
139            sys::SteamAPI_ManualDispatch_Init();
140            let server_raw = sys::SteamAPI_SteamGameServer_v015();
141            let server = Arc::new(Inner {
142                manager: Manager::Server,
143                callbacks: Callbacks {
144                    callbacks: Mutex::new(HashMap::new()),
145                    call_results: Mutex::new(HashMap::new()),
146                },
147                networking_sockets_data: Mutex::new(NetworkingSocketsData {
148                    sockets: Default::default(),
149                    independent_connections: Default::default(),
150                    connection_callback: Default::default(),
151                }),
152            });
153            Ok((
154                Server {
155                    inner: server.clone(),
156                    server: server_raw,
157                },
158                Client { inner: server },
159            ))
160        }
161    }
162
163    pub fn run_callbacks(&self) {
171        self.inner.run_callbacks()
172    }
173
174    pub fn process_callbacks(&self, mut callback_handler: impl FnMut(CallbackResult)) {
185        self.inner.process_callbacks(&mut callback_handler)
186    }
187
188    pub fn register_callback<C, F>(&self, f: F) -> CallbackHandle
201    where
202        C: Callback,
203        F: FnMut(C) + 'static + Send,
204    {
205        unsafe { register_callback(&self.inner, f) }
206    }
207
208    pub fn steam_id(&self) -> SteamId {
210        unsafe { SteamId(sys::SteamAPI_ISteamGameServer_GetSteamID(self.server)) }
211    }
212
213    pub fn authentication_session_ticket_with_steam_id(
225        &self,
226        steam_id: SteamId,
227    ) -> (AuthTicket, Vec<u8>) {
228        self.authentication_session_ticket(NetworkingIdentity::new_steam_id(steam_id))
229    }
230    pub fn authentication_session_ticket(
231        &self,
232        network_identity: NetworkingIdentity,
233    ) -> (AuthTicket, Vec<u8>) {
234        unsafe {
235            let mut ticket = vec![0; 1024];
236            let mut ticket_len = 0;
237            let auth_ticket = sys::SteamAPI_ISteamGameServer_GetAuthSessionTicket(
238                self.server,
239                ticket.as_mut_ptr().cast(),
240                1024,
241                &mut ticket_len,
242                network_identity.as_ptr(),
243            );
244            ticket.truncate(ticket_len as usize);
245            (AuthTicket(auth_ticket), ticket)
246        }
247    }
248
249    pub fn cancel_authentication_ticket(&self, ticket: AuthTicket) {
255        unsafe {
256            sys::SteamAPI_ISteamGameServer_CancelAuthTicket(self.server, ticket.0);
257        }
258    }
259
260    pub fn begin_authentication_session(
269        &self,
270        user: SteamId,
271        ticket: &[u8],
272    ) -> Result<(), AuthSessionError> {
273        unsafe {
274            let res = sys::SteamAPI_ISteamGameServer_BeginAuthSession(
275                self.server,
276                ticket.as_ptr().cast(),
277                ticket.len() as _,
278                user.0,
279            );
280            Err(match res {
281                sys::EBeginAuthSessionResult::k_EBeginAuthSessionResultOK => return Ok(()),
282                sys::EBeginAuthSessionResult::k_EBeginAuthSessionResultInvalidTicket => {
283                    AuthSessionError::InvalidTicket
284                }
285                sys::EBeginAuthSessionResult::k_EBeginAuthSessionResultDuplicateRequest => {
286                    AuthSessionError::DuplicateRequest
287                }
288                sys::EBeginAuthSessionResult::k_EBeginAuthSessionResultInvalidVersion => {
289                    AuthSessionError::InvalidVersion
290                }
291                sys::EBeginAuthSessionResult::k_EBeginAuthSessionResultGameMismatch => {
292                    AuthSessionError::GameMismatch
293                }
294                sys::EBeginAuthSessionResult::k_EBeginAuthSessionResultExpiredTicket => {
295                    AuthSessionError::ExpiredTicket
296                }
297                _ => unreachable!(),
298            })
299        }
300    }
301
302    pub fn end_authentication_session(&self, user: SteamId) {
308        unsafe {
309            sys::SteamAPI_ISteamGameServer_EndAuthSession(self.server, user.0);
310        }
311    }
312
313    pub fn handle_incoming_packet(&self, data: &[u8], addr: SocketAddrV4) -> bool {
322        unsafe {
323            let result = sys::SteamAPI_ISteamGameServer_HandleIncomingPacket(
324                self.server,
325                data.as_ptr() as _,
326                data.len() as _,
327                addr.ip().to_bits(),
328                addr.port(),
329            );
330            return result;
331        }
332    }
333
334    pub fn get_next_outgoing_packet(&self, buffer: &mut [u8], cb: impl Fn(SocketAddrV4, &[u8])) {
338        assert!(
339            buffer.len() >= 16 * 1024,
340            "Buffer size must be at least 16 KiB"
341        );
342
343        loop {
344            let mut addr = 0u32;
345            let mut port = 0u16;
346
347            let len = unsafe {
348                sys::SteamAPI_ISteamGameServer_GetNextOutgoingPacket(
349                    self.server,
350                    buffer.as_mut_ptr() as *mut _,
351                    buffer.len() as _,
352                    &mut addr,
353                    &mut port,
354                )
355            };
356
357            if len == 0 {
358                break; }
360
361            let addr = SocketAddrV4::new(Ipv4Addr::from_bits(addr), port);
362            cb(addr, &buffer[..len as usize]);
363        }
364    }
365
366    pub fn set_product(&self, product: &str) {
372        let product = CString::new(product).unwrap();
373        unsafe {
374            sys::SteamAPI_ISteamGameServer_SetProduct(self.server, product.as_ptr());
375        }
376    }
377
378    pub fn set_game_description(&self, desc: &str) {
383        let desc = CString::new(desc).unwrap();
384        unsafe {
385            sys::SteamAPI_ISteamGameServer_SetGameDescription(self.server, desc.as_ptr());
386        }
387    }
388
389    pub fn set_game_data(&self, data: &str) {
397        let desc = CString::new(data).unwrap();
398        unsafe {
399            sys::SteamAPI_ISteamGameServer_SetGameData(self.server, desc.as_ptr());
400        }
401    }
402
403    pub fn set_dedicated_server(&self, dedicated: bool) {
405        unsafe {
406            sys::SteamAPI_ISteamGameServer_SetDedicatedServer(self.server, dedicated);
407        }
408    }
409
410    pub fn log_on_anonymous(&self) {
412        unsafe {
413            sys::SteamAPI_ISteamGameServer_LogOnAnonymous(self.server);
414        }
415    }
416
417    pub fn log_on(&self, token: &str) {
419        let token = CString::new(token).unwrap();
420        unsafe {
421            sys::SteamAPI_ISteamGameServer_LogOn(self.server, token.as_ptr());
422        }
423    }
424
425    pub fn enable_heartbeats(&self, active: bool) {
428        unsafe {
429            sys::SteamAPI_ISteamGameServer_SetAdvertiseServerActive(self.server, active);
430        }
431    }
432
433    #[inline(always)]
442    pub fn set_advertise_server_active(&self, active: bool) {
443        self.enable_heartbeats(active);
444    }
445
446    pub fn set_mod_dir(&self, mod_dir: &str) {
449        let mod_dir = CString::new(mod_dir).unwrap();
450        unsafe {
451            sys::SteamAPI_ISteamGameServer_SetModDir(self.server, mod_dir.as_ptr());
452        }
453    }
454
455    pub fn set_map_name(&self, map_name: &str) {
457        let map_name = CString::new(map_name).unwrap();
458        unsafe {
459            sys::SteamAPI_ISteamGameServer_SetMapName(self.server, map_name.as_ptr());
460        }
461    }
462
463    pub fn set_server_name(&self, server_name: &str) {
465        let server_name = CString::new(server_name).unwrap();
466        unsafe {
467            sys::SteamAPI_ISteamGameServer_SetServerName(self.server, server_name.as_ptr());
468        }
469    }
470
471    pub fn set_max_players(&self, count: i32) {
475        unsafe {
476            sys::SteamAPI_ISteamGameServer_SetMaxPlayerCount(self.server, count);
477        }
478    }
479
480    pub fn set_game_tags(&self, tags: &str) {
491        assert!(tags.len() != 0, "tags must not be an empty string (\"\").");
492        assert!(tags.len() < 128, "tags can not be longer than 127.");
493
494        let tags = CString::new(tags).unwrap();
495        unsafe {
496            sys::SteamAPI_ISteamGameServer_SetGameTags(self.server, tags.as_ptr());
497        }
498    }
499
500    pub fn set_key_value(&self, key: &str, value: &str) {
502        let key = CString::new(key).unwrap();
503        let value = CString::new(value).unwrap();
504
505        unsafe {
506            sys::SteamAPI_ISteamGameServer_SetKeyValue(self.server, key.as_ptr(), value.as_ptr());
507        }
508    }
509
510    pub fn clear_all_key_values(&self) {
512        unsafe {
513            sys::SteamAPI_ISteamGameServer_ClearAllKeyValues(self.server);
514        }
515    }
516
517    pub fn set_password_protected(&self, b_password_protected: bool) {
519        unsafe {
520            sys::SteamAPI_ISteamGameServer_SetPasswordProtected(self.server, b_password_protected);
521        }
522    }
523
524    pub fn set_bot_player_count(&self, c_bot_players: i32) {
526        unsafe {
527            sys::SteamAPI_ISteamGameServer_SetBotPlayerCount(self.server, c_bot_players);
528        }
529    }
530
531    pub fn ugc(&self) -> UGC {
535        unsafe {
536            let ugc = sys::SteamAPI_SteamGameServerUGC_v021();
537            debug_assert!(!ugc.is_null());
538            UGC {
539                ugc,
540                inner: self.inner.clone(),
541            }
542        }
543    }
544
545    pub fn utils(&self) -> Utils {
547        unsafe {
548            let utils = sys::SteamAPI_SteamGameServerUtils_v010();
549            debug_assert!(!utils.is_null());
550            Utils {
551                utils: utils,
552                _inner: self.inner.clone(),
553            }
554        }
555    }
556
557    pub fn networking(&self) -> Networking {
559        unsafe {
560            let net = sys::SteamAPI_SteamGameServerNetworking_v006();
561            debug_assert!(!net.is_null());
562            Networking {
563                net: net,
564                _inner: self.inner.clone(),
565            }
566        }
567    }
568
569    pub fn networking_messages(&self) -> networking_messages::NetworkingMessages {
570        unsafe {
571            let net = sys::SteamAPI_SteamGameServerNetworkingMessages_SteamAPI_v002();
572            debug_assert!(!net.is_null());
573            networking_messages::NetworkingMessages {
574                net,
575                inner: self.inner.clone(),
576            }
577        }
578    }
579
580    pub fn networking_sockets(&self) -> networking_sockets::NetworkingSockets {
581        unsafe {
582            let sockets = sys::SteamAPI_SteamGameServerNetworkingSockets_SteamAPI_v012();
583            debug_assert!(!sockets.is_null());
584            networking_sockets::NetworkingSockets {
585                sockets,
586                inner: self.inner.clone(),
587            }
588        }
589    }
590
591    }
605
606#[test]
607#[serial]
608fn test() {
609    let (server, single) = Server::init(
610        [127, 0, 0, 1].into(),
611        23334,
612        23335,
613        ServerMode::Authentication,
614        "0.0.1",
615    )
616    .unwrap();
617
618    println!("{:?}", server.steam_id());
619
620    server.set_product("steamworks-rs test");
621    server.set_game_description("basic server test");
622    server.set_dedicated_server(true);
623    server.log_on_anonymous();
624
625    println!("{:?}", server.steam_id());
626
627    let _cb = server.register_callback(|v: AuthSessionTicketResponse| {
628        println!("Got auth ticket response: {:?}", v.result)
629    });
630    let _cb = server.register_callback(|v: ValidateAuthTicketResponse| {
631        println!("Got validate auth ticket response: {:?}", v)
632    });
633
634    let id = server.steam_id();
635    let (auth, ticket) = server.authentication_session_ticket_with_steam_id(id);
636
637    println!("{:?}", server.begin_authentication_session(id, &ticket));
638
639    for _ in 0..20 {
640        single.run_callbacks();
641        ::std::thread::sleep(::std::time::Duration::from_millis(50));
642    }
643
644    println!("END");
645
646    server.cancel_authentication_ticket(auth);
647
648    for _ in 0..20 {
649        single.run_callbacks();
650        ::std::thread::sleep(::std::time::Duration::from_millis(50));
651    }
652
653    server.end_authentication_session(id);
654}
655
656#[derive(Clone, Debug)]
658#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
659pub struct GSClientApprove {
660    pub user: SteamId,
662    pub owner: SteamId,
664}
665
666impl_callback!(cb: GSClientApprove_t => GSClientApprove {
667    Self {
668        user: SteamId(cb.m_SteamID.m_steamid.m_unAll64Bits),
669        owner: SteamId(cb.m_OwnerSteamID.m_steamid.m_unAll64Bits),
670    }
671});
672
673#[repr(i32)]
675#[derive(Clone, Copy, Debug, PartialEq, Eq)]
676#[non_exhaustive]
677#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
678pub enum DenyReason {
679    Invalid,
680    InvalidVersion,
681    Generic,
682    NotLoggedOn,
683    NoLicense,
684    Cheater,
685    LoggedInElseWhere,
686    UnknownText,
687    IncompatibleAnticheat,
688    MemoryCorruption,
689    IncompatibleSoftware,
690    SteamConnectionLost,
691    SteamConnectionError,
692    SteamResponseTimedOut,
693    SteamValidationStalled,
694    SteamOwnerLeftGuestUser,
695}
696
697impl From<sys::EDenyReason> for DenyReason {
698    fn from(r: sys::EDenyReason) -> Self {
699        match r {
700            sys::EDenyReason::k_EDenyInvalid => DenyReason::Invalid,
701            sys::EDenyReason::k_EDenyInvalidVersion => DenyReason::InvalidVersion,
702            sys::EDenyReason::k_EDenyGeneric => DenyReason::Generic,
703            sys::EDenyReason::k_EDenyNotLoggedOn => DenyReason::NotLoggedOn,
704            sys::EDenyReason::k_EDenyNoLicense => DenyReason::NoLicense,
705            sys::EDenyReason::k_EDenyCheater => DenyReason::Cheater,
706            sys::EDenyReason::k_EDenyLoggedInElseWhere => DenyReason::LoggedInElseWhere,
707            sys::EDenyReason::k_EDenyUnknownText => DenyReason::UnknownText,
708            sys::EDenyReason::k_EDenyIncompatibleAnticheat => DenyReason::IncompatibleAnticheat,
709            sys::EDenyReason::k_EDenyMemoryCorruption => DenyReason::MemoryCorruption,
710            sys::EDenyReason::k_EDenyIncompatibleSoftware => DenyReason::IncompatibleSoftware,
711            sys::EDenyReason::k_EDenySteamConnectionLost => DenyReason::SteamConnectionLost,
712            sys::EDenyReason::k_EDenySteamConnectionError => DenyReason::SteamConnectionError,
713            sys::EDenyReason::k_EDenySteamResponseTimedOut => DenyReason::SteamResponseTimedOut,
714            sys::EDenyReason::k_EDenySteamValidationStalled => DenyReason::SteamValidationStalled,
715            sys::EDenyReason::k_EDenySteamOwnerLeftGuestUser => DenyReason::SteamOwnerLeftGuestUser,
716            _ => DenyReason::Invalid,
717        }
718    }
719}
720
721#[derive(Clone, Debug)]
723#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
724pub struct GSClientDeny {
725    pub user: SteamId,
727    pub deny_reason: DenyReason,
728    pub optional_text: String,
729}
730
731impl_callback!(cb: GSClientDeny_t => GSClientDeny {
732    let deny_text = unsafe {
733        let cstr = CStr::from_ptr(cb.m_rgchOptionalText.as_ptr() as *const c_char);
734        cstr.to_string_lossy().to_owned().into_owned()
735    };
736
737    GSClientDeny {
738        user: SteamId(cb.m_SteamID.m_steamid.m_unAll64Bits),
739        deny_reason: DenyReason::from(cb.m_eDenyReason),
740        optional_text: deny_text,
741    }
742});
743
744#[derive(Clone, Debug)]
746#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
747pub struct GSClientKick {
748    pub user: SteamId,
750    pub deny_reason: DenyReason,
751}
752
753impl_callback!(cb: GSClientKick_t => GSClientKick {
754    Self {
755        user: SteamId(cb.m_SteamID.m_steamid.m_unAll64Bits),
756        deny_reason: DenyReason::from(cb.m_eDenyReason),
757    }
758});
759
760#[derive(Clone, Debug)]
762#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
763pub struct GSClientGroupStatus {
764    pub user: SteamId,
766    pub group: SteamId,
767    pub member: bool,
768    pub officer: bool,
769}
770
771impl_callback!(cb: GSClientGroupStatus_t => GSClientGroupStatus {
772    Self {
773        user: SteamId(cb.m_SteamIDUser.m_steamid.m_unAll64Bits),
774        group: SteamId(cb.m_SteamIDGroup.m_steamid.m_unAll64Bits),
775        member: cb.m_bMember,
776        officer: cb.m_bOfficer,
777    }
778});