Skip to main content

citadel_sdk/prefabs/
mod.rs

1//! Pre-built Network Components
2//!
3//! This module provides a collection of pre-built network components for both client
4//! and server implementations in the Citadel Protocol. These components offer ready-to-use
5//! functionality for common networking patterns and use cases.
6//!
7//! # Features
8//! - Client-side networking components
9//! - Server-side networking components
10//! - Remote connection management
11//! - File transfer handling
12//! - Signal and event processing
13//! - Connection security management
14//! - Peer discovery and listing
15//!
16//! # Example
17//! ```rust
18//! use citadel_sdk::prelude::*;
19//! use citadel_sdk::prefabs::client::single_connection::SingleClientServerConnectionKernel;
20//!
21//! # fn main() -> Result<(), NetworkError> {
22//!#  async fn connect_to_server() -> Result<(), NetworkError> {
23//!     let settings = DefaultServerConnectionSettingsBuilder::transient("127.0.0.1:25021")
24//!         .with_udp_mode(UdpMode::Enabled)
25//!         .build()?;
26//!
27//!     let kernel = SingleClientServerConnectionKernel::new(
28//!         settings,
29//!         |conn| async move {
30//!             println!("Connected to server!");
31//!             Ok(())
32//!         },
33//!     );
34//!
35//!     let client = DefaultNodeBuilder::default()
36//!         .with_stun_servers(["stun1.example.com:3478", "stun2.example.com:3478", "stun3.example.com:3478"])
37//!         .build(kernel)
38//!         .map_err(|err| NetworkError::generic(err.to_string()))?;
39//!
40//!     let _result = client.await?;
41//!     Ok(())
42//! # }
43//! # Ok::<(), NetworkError>(())
44//! }
45//!
46//! ```
47//!
48//! # Important Notes
49//! - File transfer handlers can only be obtained once
50//! - Signal receivers are single-use
51//! - Remote shutdown is graceful and asynchronous
52//! - Connection types determine available operations
53//!
54//! # Related Components
55//! - [`client`]: Client-side networking implementations
56//! - [`server`]: Server-side networking implementations
57//!
58//! [`client`]: crate::prefabs::client
59//! [`server`]: crate::prefabs::server
60
61use crate::impl_remote;
62use crate::prefabs::client::peer_connection::FileTransferHandleRx;
63use citadel_io::tokio::sync::mpsc::UnboundedReceiver;
64use citadel_io::Mutex;
65use citadel_proto::prelude::*;
66use std::net::{SocketAddr, ToSocketAddrs};
67use std::ops::{Deref, DerefMut};
68use std::sync::Arc;
69
70/// Kernels for clients
71pub mod client;
72/// Kernels for servers
73pub mod server;
74/// Shared utilities between client and server kernels
75pub mod shared;
76use crate::prelude::user_ids::TargetLockedRemote;
77use crate::remote_ext::results::LocalGroupPeer;
78use crate::remote_ext::ProtocolRemoteExt;
79
80/// A limited version of the [`NodeRemote`] designed to only allow shutdown calls to the protocol
81/// as well as several other functions
82#[derive(Clone)]
83pub struct ClientServerRemote<R: Ratchet> {
84    pub(crate) inner: NodeRemote<R>,
85    pub(crate) unprocessed_signals_rx: Arc<Mutex<Option<UnboundedReceiver<NodeResult<R>>>>>,
86    pub(crate) file_transfer_handle_rx: Arc<Mutex<Option<FileTransferHandleRx>>>,
87    conn_type: VirtualTargetType,
88    session_security_settings: SessionSecuritySettings,
89}
90
91impl<R: Ratchet> Deref for ClientServerRemote<R> {
92    type Target = NodeRemote<R>;
93    fn deref(&self) -> &Self::Target {
94        &self.inner
95    }
96}
97
98impl<R: Ratchet> DerefMut for ClientServerRemote<R> {
99    fn deref_mut(&mut self) -> &mut Self::Target {
100        &mut self.inner
101    }
102}
103
104impl_remote!(ClientServerRemote);
105
106impl<R: Ratchet> ClientServerRemote<R> {
107    /// constructs a new [`ClientServerRemote`] from a [`ClientServerRemote`] and a [`VirtualTargetType`]
108    pub fn new(
109        conn_type: VirtualTargetType,
110        remote: NodeRemote<R>,
111        session_security_settings: SessionSecuritySettings,
112        unprocessed_signals_rx: Option<UnboundedReceiver<NodeResult<R>>>,
113        file_transfer_handle_rx: Option<FileTransferHandleRx>,
114    ) -> Self {
115        // TODO: Add handles, only the server calls this
116        Self {
117            inner: remote,
118            unprocessed_signals_rx: Arc::new(Mutex::new(unprocessed_signals_rx)),
119            file_transfer_handle_rx: Arc::new(Mutex::new(file_transfer_handle_rx)),
120            conn_type,
121            session_security_settings,
122        }
123    }
124    /// Can only be called once per remote. Allows receiving events
125    pub fn get_unprocessed_signals_receiver(
126        &self,
127    ) -> Option<citadel_io::tokio::sync::mpsc::UnboundedReceiver<NodeResult<R>>> {
128        self.unprocessed_signals_rx.lock().take()
129    }
130
131    /// Obtains a receiver which yields incoming file/object transfer handles
132    pub fn get_incoming_file_transfer_handle(&self) -> Result<FileTransferHandleRx, NetworkError> {
133        self.file_transfer_handle_rx
134            .lock()
135            .take()
136            .ok_or(citadel_io::error!(
137                citadel_io::ErrorCode::RemoteFunctionAlreadyCalled
138            ))
139    }
140}
141
142impl<R: Ratchet> TargetLockedRemote<R> for ClientServerRemote<R> {
143    fn user(&self) -> &VirtualTargetType {
144        &self.conn_type
145    }
146    fn remote(&self) -> &NodeRemote<R> {
147        &self.inner
148    }
149    fn target_username(&self) -> Option<&str> {
150        None
151    }
152    fn user_mut(&mut self) -> &mut VirtualTargetType {
153        &mut self.conn_type
154    }
155
156    fn session_security_settings(&self) -> Option<&SessionSecuritySettings> {
157        Some(&self.session_security_settings)
158    }
159}
160
161impl<R: Ratchet> ClientServerRemote<R> {
162    /// Gracefully closes the protocol and kernel executor
163    pub async fn shutdown_kernel(&self) -> Result<(), NetworkError> {
164        self.inner.shutdown().await
165    }
166
167    pub async fn get_peers(
168        &self,
169        limit: Option<usize>,
170    ) -> Result<Vec<LocalGroupPeer>, NetworkError> {
171        let session_cid = self.conn_type.get_session_cid();
172        let peer_info = self.inner.get_local_group_peers(session_cid, limit).await?;
173        Ok(peer_info
174            .iter()
175            .map(|info| LocalGroupPeer {
176                cid: info.cid,
177                is_online: info.is_online,
178            })
179            .collect())
180    }
181}
182
183pub fn get_socket_addr<T: ToSocketAddrs>(addr: T) -> Result<SocketAddr, NetworkError> {
184    addr.to_socket_addrs()
185        .map_err(|err| NetworkError::socket(err.to_string()))?
186        .next()
187        .ok_or_else(|| citadel_io::error!(citadel_io::ErrorCode::BuilderInvalidSocketAddress))
188}