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().build(kernel)?;
36//!
37//!     let _result = client.await?;
38//!     Ok(())
39//! # }
40//! # Ok::<(), NetworkError>(())
41//! }
42//!
43//! ```
44//!
45//! # Important Notes
46//! - File transfer handlers can only be obtained once
47//! - Signal receivers are single-use
48//! - Remote shutdown is graceful and asynchronous
49//! - Connection types determine available operations
50//!
51//! # Related Components
52//! - [`client`]: Client-side networking implementations
53//! - [`server`]: Server-side networking implementations
54//!
55//! [`client`]: crate::prefabs::client
56//! [`server`]: crate::prefabs::server
57
58use crate::impl_remote;
59use crate::prefabs::client::peer_connection::FileTransferHandleRx;
60use citadel_io::tokio::sync::mpsc::UnboundedReceiver;
61use citadel_io::Mutex;
62use citadel_proto::prelude::*;
63use std::net::{SocketAddr, ToSocketAddrs};
64use std::ops::{Deref, DerefMut};
65use std::sync::Arc;
66
67/// Kernels for clients
68pub mod client;
69/// Kernels for servers
70pub mod server;
71/// Shared utilities between client and server kernels
72pub mod shared;
73use crate::prelude::user_ids::TargetLockedRemote;
74use crate::remote_ext::results::LocalGroupPeer;
75use crate::remote_ext::ProtocolRemoteExt;
76
77/// A limited version of the [`NodeRemote`] designed to only allow shutdown calls to the protocol
78/// as well as several other functions
79#[derive(Clone)]
80pub struct ClientServerRemote<R: Ratchet> {
81    pub(crate) inner: NodeRemote<R>,
82    pub(crate) unprocessed_signals_rx: Arc<Mutex<Option<UnboundedReceiver<NodeResult<R>>>>>,
83    pub(crate) file_transfer_handle_rx: Arc<Mutex<Option<FileTransferHandleRx>>>,
84    conn_type: VirtualTargetType,
85    session_security_settings: SessionSecuritySettings,
86}
87
88impl<R: Ratchet> Deref for ClientServerRemote<R> {
89    type Target = NodeRemote<R>;
90    fn deref(&self) -> &Self::Target {
91        &self.inner
92    }
93}
94
95impl<R: Ratchet> DerefMut for ClientServerRemote<R> {
96    fn deref_mut(&mut self) -> &mut Self::Target {
97        &mut self.inner
98    }
99}
100
101impl_remote!(ClientServerRemote);
102
103impl<R: Ratchet> ClientServerRemote<R> {
104    /// constructs a new [`ClientServerRemote`] from a [`ClientServerRemote`] and a [`VirtualTargetType`]
105    pub fn new(
106        conn_type: VirtualTargetType,
107        remote: NodeRemote<R>,
108        session_security_settings: SessionSecuritySettings,
109        unprocessed_signals_rx: Option<UnboundedReceiver<NodeResult<R>>>,
110        file_transfer_handle_rx: Option<FileTransferHandleRx>,
111    ) -> Self {
112        // TODO: Add handles, only the server calls this
113        Self {
114            inner: remote,
115            unprocessed_signals_rx: Arc::new(Mutex::new(unprocessed_signals_rx)),
116            file_transfer_handle_rx: Arc::new(Mutex::new(file_transfer_handle_rx)),
117            conn_type,
118            session_security_settings,
119        }
120    }
121    /// Can only be called once per remote. Allows receiving events
122    pub fn get_unprocessed_signals_receiver(
123        &self,
124    ) -> Option<citadel_io::tokio::sync::mpsc::UnboundedReceiver<NodeResult<R>>> {
125        self.unprocessed_signals_rx.lock().take()
126    }
127
128    /// Obtains a receiver which yields incoming file/object transfer handles
129    pub fn get_incoming_file_transfer_handle(&self) -> Result<FileTransferHandleRx, NetworkError> {
130        self.file_transfer_handle_rx
131            .lock()
132            .take()
133            .ok_or(NetworkError::InternalError(
134                "This function has already been called",
135            ))
136    }
137}
138
139impl<R: Ratchet> TargetLockedRemote<R> for ClientServerRemote<R> {
140    fn user(&self) -> &VirtualTargetType {
141        &self.conn_type
142    }
143    fn remote(&self) -> &NodeRemote<R> {
144        &self.inner
145    }
146    fn target_username(&self) -> Option<&str> {
147        None
148    }
149    fn user_mut(&mut self) -> &mut VirtualTargetType {
150        &mut self.conn_type
151    }
152
153    fn session_security_settings(&self) -> Option<&SessionSecuritySettings> {
154        Some(&self.session_security_settings)
155    }
156}
157
158impl<R: Ratchet> ClientServerRemote<R> {
159    /// Gracefully closes the protocol and kernel executor
160    pub async fn shutdown_kernel(&self) -> Result<(), NetworkError> {
161        self.inner.shutdown().await
162    }
163
164    pub async fn get_peers(
165        &self,
166        limit: Option<usize>,
167    ) -> Result<Vec<LocalGroupPeer>, NetworkError> {
168        let session_cid = self.conn_type.get_session_cid();
169        let peer_info = self.inner.get_local_group_peers(session_cid, limit).await?;
170        Ok(peer_info
171            .iter()
172            .map(|info| LocalGroupPeer {
173                cid: info.cid,
174                is_online: info.is_online,
175            })
176            .collect())
177    }
178}
179
180pub fn get_socket_addr<T: ToSocketAddrs>(addr: T) -> Result<SocketAddr, NetworkError> {
181    addr.to_socket_addrs()
182        .map_err(|err| NetworkError::SocketError(err.to_string()))?
183        .next()
184        .ok_or_else(|| NetworkError::msg("Invalid socket address specified"))
185}