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