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