Skip to main content

aetheris_server/
multi_transport.rs

1//! Aggregates multiple `GameTransport` implementations into one.
2
3use aetheris_protocol::events::NetworkEvent;
4use aetheris_protocol::traits::{ClientId, GameTransport, TransportError};
5use async_trait::async_trait;
6
7/// Combines multiple `GameTransport` implementations.
8///
9/// This allows the server to simultaneously support native clients (via Renet)
10/// and web clients (via WebTransport).
11pub struct MultiTransport {
12    transports: Vec<Box<dyn GameTransport>>,
13}
14
15impl MultiTransport {
16    /// Creates a new empty `MultiTransport`.
17    #[must_use]
18    pub fn new() -> Self {
19        Self {
20            transports: Vec::new(),
21        }
22    }
23
24    /// Adds a transport to the aggregator.
25    pub fn add_transport(&mut self, transport: Box<dyn GameTransport>) {
26        self.transports.push(transport);
27    }
28}
29
30impl Default for MultiTransport {
31    fn default() -> Self {
32        Self::new()
33    }
34}
35
36#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
37#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
38impl GameTransport for MultiTransport {
39    async fn send_unreliable(
40        &self,
41        client_id: ClientId,
42        data: &[u8],
43    ) -> Result<(), TransportError> {
44        for transport in &self.transports {
45            // We try to send to all, but only one will likely succeed if IDs are properly partitioned
46            // or if the transport returns ClientNotConnected for unknown IDs.
47            match transport.send_unreliable(client_id, data).await {
48                Ok(()) => return Ok(()),
49                Err(TransportError::ClientNotConnected(_)) => {}
50                Err(e) => {
51                    tracing::error!("MultiTransport: individual transport send error: {:?}", e);
52                    return Err(e);
53                }
54            }
55        }
56        Err(TransportError::ClientNotConnected(client_id))
57    }
58
59    async fn send_reliable(&self, client_id: ClientId, data: &[u8]) -> Result<(), TransportError> {
60        for transport in &self.transports {
61            match transport.send_reliable(client_id, data).await {
62                Ok(()) => return Ok(()),
63                Err(TransportError::ClientNotConnected(_)) => {}
64                Err(e) => {
65                    tracing::error!("MultiTransport: individual transport send error: {:?}", e);
66                    return Err(e);
67                }
68            }
69        }
70        Err(TransportError::ClientNotConnected(client_id))
71    }
72
73    async fn broadcast_unreliable(&self, data: &[u8]) -> Result<(), TransportError> {
74        let mut first_error = None;
75        for transport in &self.transports {
76            if let Err(e) = transport.broadcast_unreliable(data).await
77                && first_error.is_none()
78            {
79                first_error = Some(e);
80            }
81        }
82
83        if let Some(e) = first_error {
84            Err(e)
85        } else {
86            Ok(())
87        }
88    }
89
90    async fn poll_events(&mut self) -> Result<Vec<NetworkEvent>, TransportError> {
91        let mut all_events = Vec::new();
92        for transport in &mut self.transports {
93            all_events.extend(transport.poll_events().await?);
94        }
95        Ok(all_events)
96    }
97
98    async fn connected_client_count(&self) -> usize {
99        let mut total = 0;
100        for transport in &self.transports {
101            total += transport.connected_client_count().await;
102        }
103        total
104    }
105}