citadel_sdk/prefabs/client/
mod.rs1use crate::prefabs::client::single_connection::SingleClientServerConnectionKernel;
46use crate::prelude::*;
47use std::marker::PhantomData;
48use std::net::{SocketAddr, ToSocketAddrs};
49use uuid::Uuid;
50
51pub mod broadcast;
53pub(crate) mod broadcast_join;
55pub mod peer_connection;
57#[cfg(target_family = "wasm")]
59pub(crate) mod serverless;
60pub mod single_connection;
62
63#[async_trait]
64pub trait PrefabFunctions<'a, Arg: Send + 'a, R: Ratchet>: Sized + 'a {
65 type UserLevelInputFunction: Send + 'a;
66 type SharedBundle: Send + 'a;
68
69 fn get_shared_bundle(&self) -> Self::SharedBundle;
70
71 async fn on_c2s_channel_received(
72 connect_success: CitadelClientServerConnection<R>,
73 arg: Arg,
74 fx: Self::UserLevelInputFunction,
75 shared: Self::SharedBundle,
76 ) -> Result<(), NetworkError>;
77
78 fn construct(kernel: Box<dyn NetKernel<R> + 'a>) -> Self;
79
80 fn new(
82 server_connection_settings: ServerConnectionSettings<R>,
83 arg: Arg,
84 on_channel_received: Self::UserLevelInputFunction,
85 ) -> Self {
86 let (tx, rx) = citadel_io::tokio::sync::oneshot::channel();
87 let server_conn_kernel = SingleClientServerConnectionKernel::<_, _, R>::new(
88 server_connection_settings,
89 |connect_success| {
90 on_channel_received_fn::<_, Self, R>(connect_success, rx, arg, on_channel_received)
91 },
92 );
93
94 let this = Self::construct(Box::new(server_conn_kernel));
95 assert!(tx.send(this.get_shared_bundle()).is_ok());
96 this
97 }
98}
99
100async fn on_channel_received_fn<'a, Arg: Send + 'a, T: PrefabFunctions<'a, Arg, R>, R: Ratchet>(
101 connect_success: CitadelClientServerConnection<R>,
102 rx_bundle: citadel_io::tokio::sync::oneshot::Receiver<T::SharedBundle>,
103 arg: Arg,
104 on_channel_received: T::UserLevelInputFunction,
105) -> Result<(), NetworkError> {
106 let shared = rx_bundle
107 .await
108 .map_err(|err| NetworkError::generic(err.to_string()))?;
109 T::on_c2s_channel_received(connect_success, arg, on_channel_received, shared).await
110}
111
112pub struct ServerConnectionSettingsBuilder<R: Ratchet, T: ToSocketAddrs> {
114 password: Option<SecBuffer>,
115 username: Option<String>,
116 name: Option<String>,
117 psk: Option<PreSharedKey>,
118 address: Option<T>,
119 udp_mode: Option<UdpMode>,
120 session_security_settings: Option<SessionSecuritySettings>,
121 transient_uuid: Option<Uuid>,
122 is_connect: bool,
123 _ratchet: PhantomData<R>,
124}
125
126pub type DefaultServerConnectionSettingsBuilder<T> =
127 ServerConnectionSettingsBuilder<StackedRatchet, T>;
128
129impl<R: Ratchet, T: ToSocketAddrs> ServerConnectionSettingsBuilder<R, T> {
130 fn base(addr: T) -> Self {
131 Self {
132 password: None,
133 username: None,
134 udp_mode: None,
135 session_security_settings: None,
136 name: None,
137 psk: None,
138 transient_uuid: None,
139 address: Some(addr),
140 is_connect: false,
141 _ratchet: PhantomData,
142 }
143 }
144
145 pub fn transient(addr: T) -> Self {
149 Self::transient_with_id(addr, Uuid::new_v4())
150 }
151
152 pub fn transient_with_id(addr: T, id: impl Into<Uuid>) -> Self {
154 Self {
155 transient_uuid: Some(id.into()),
156 ..Self::base(addr)
157 }
158 }
159
160 pub fn credentialed_registration<U: Into<String>, N: Into<String>, P: Into<SecBuffer>>(
163 addr: T,
164 username: U,
165 alias: N,
166 password: P,
167 ) -> Self {
168 Self {
169 password: Some(password.into()),
170 username: Some(username.into()),
171 name: Some(alias.into()),
172 ..Self::base(addr)
173 }
174 }
175
176 pub fn credentialed_login<U: Into<String>, P: Into<SecBuffer>>(
178 addr: T,
179 username: U,
180 password: P,
181 ) -> Self {
182 Self {
183 password: Some(password.into()),
184 username: Some(username.into()),
185 is_connect: true,
186 ..Self::base(addr)
187 }
188 }
189
190 pub fn with_session_password<V: Into<PreSharedKey>>(mut self, psk: V) -> Self {
192 self.psk = Some(psk.into());
193 self
194 }
195
196 pub fn with_udp_mode(mut self, mode: UdpMode) -> Self {
198 self.udp_mode = Some(mode);
199 self
200 }
201
202 pub fn disable_udp(self) -> Self {
204 self.with_udp_mode(UdpMode::Disabled)
205 }
206
207 pub fn enable_udp(self) -> Self {
208 self.with_udp_mode(UdpMode::Enabled)
209 }
210
211 pub fn with_session_security_settings<V: Into<SessionSecuritySettings>>(
213 mut self,
214 settings: V,
215 ) -> Self {
216 self.session_security_settings = Some(settings.into());
217 self
218 }
219
220 pub fn build(self) -> Result<ServerConnectionSettings<R>, NetworkError> {
222 let server_addr = if let Some(addr) = self.address {
223 let addr = addr
224 .to_socket_addrs()
225 .map_err(|err| NetworkError::generic(err.to_string()))?
226 .next()
227 .ok_or(citadel_io::error!(citadel_io::ErrorCode::BuilderNoAddress))?;
228 Some(addr)
229 } else {
230 None
231 };
232
233 if let Some(uuid) = self.transient_uuid {
234 Ok(ServerConnectionSettings::<R>::Transient {
235 server_addr: server_addr
236 .ok_or(citadel_io::error!(citadel_io::ErrorCode::BuilderNoAddress))?,
237 uuid,
238 udp_mode: self.udp_mode.unwrap_or_default(),
239 session_security_settings: self.session_security_settings.unwrap_or_default(),
240 pre_shared_key: self.psk,
241 _ratchet: PhantomData,
242 })
243 } else if self.is_connect {
244 Ok(ServerConnectionSettings::<R>::CredentialedConnect {
245 username: self
246 .username
247 .ok_or(citadel_io::error!(citadel_io::ErrorCode::BuilderNoUsername))?,
248 password: self
249 .password
250 .ok_or(citadel_io::error!(citadel_io::ErrorCode::BuilderNoPassword))?,
251 udp_mode: self.udp_mode.unwrap_or_default(),
252 session_security_settings: self.session_security_settings.unwrap_or_default(),
253 pre_shared_key: self.psk,
254 _ratchet: PhantomData,
255 })
256 } else {
257 Ok(ServerConnectionSettings::<R>::CredentialedRegister {
258 address: server_addr
259 .ok_or(citadel_io::error!(citadel_io::ErrorCode::BuilderNoAddress))?,
260 username: self
261 .username
262 .ok_or(citadel_io::error!(citadel_io::ErrorCode::BuilderNoUsername))?,
263 alias: self
264 .name
265 .ok_or(citadel_io::error!(citadel_io::ErrorCode::BuilderNoAlias))?,
266 password: self
267 .password
268 .ok_or(citadel_io::error!(citadel_io::ErrorCode::BuilderNoPassword))?,
269 pre_shared_key: self.psk,
270 udp_mode: self.udp_mode.unwrap_or_default(),
271 session_security_settings: self.session_security_settings.unwrap_or_default(),
272 _ratchet: PhantomData,
273 })
274 }
275 }
276}
277
278pub enum ServerConnectionSettings<R: Ratchet> {
280 Transient {
281 server_addr: SocketAddr,
282 uuid: Uuid,
283 udp_mode: UdpMode,
284 session_security_settings: SessionSecuritySettings,
285 pre_shared_key: Option<PreSharedKey>,
286 _ratchet: PhantomData<R>,
287 },
288 CredentialedConnect {
289 username: String,
290 password: SecBuffer,
291 udp_mode: UdpMode,
292 session_security_settings: SessionSecuritySettings,
293 pre_shared_key: Option<PreSharedKey>,
294 _ratchet: PhantomData<R>,
295 },
296 CredentialedRegister {
297 address: SocketAddr,
298 username: String,
299 alias: String,
300 password: SecBuffer,
301 pre_shared_key: Option<PreSharedKey>,
302 udp_mode: UdpMode,
303 session_security_settings: SessionSecuritySettings,
304 _ratchet: PhantomData<R>,
305 },
306}
307
308impl<R: Ratchet> ServerConnectionSettings<R> {
309 pub(crate) fn udp_mode(&self) -> UdpMode {
310 match self {
311 Self::Transient { udp_mode, .. } => *udp_mode,
312 Self::CredentialedRegister { udp_mode, .. } => *udp_mode,
313 Self::CredentialedConnect { udp_mode, .. } => *udp_mode,
314 }
315 }
316
317 pub(crate) fn session_security_settings(&self) -> SessionSecuritySettings {
318 match self {
319 Self::Transient {
320 session_security_settings,
321 ..
322 } => *session_security_settings,
323 Self::CredentialedRegister {
324 session_security_settings,
325 ..
326 } => *session_security_settings,
327 Self::CredentialedConnect {
328 session_security_settings,
329 ..
330 } => *session_security_settings,
331 }
332 }
333
334 pub(crate) fn pre_shared_key(&self) -> Option<&PreSharedKey> {
335 match self {
336 Self::Transient { pre_shared_key, .. } => pre_shared_key.as_ref(),
337 Self::CredentialedRegister { pre_shared_key, .. } => pre_shared_key.as_ref(),
338 Self::CredentialedConnect { pre_shared_key, .. } => pre_shared_key.as_ref(),
339 }
340 }
341}