Skip to main content

pawkit_net/
client.rs

1use std::{
2    ops::Deref,
3    sync::{
4        Arc,
5        atomic::{AtomicBool, Ordering},
6    },
7};
8
9use bytes::Bytes;
10use just_webrtc::{
11    DataChannelExt, PeerConnectionBuilder, PeerConnectionExt,
12    types::{DataChannelOptions, PeerConnectionState},
13};
14use pawkit_net_signaling::{
15    ChannelConfiguration, Reliability, client::ClientPeerSignalingClient, model::HostId,
16};
17use tokio::sync::{
18    RwLock,
19    mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel},
20};
21
22use crate::{Connection, RUNTIME, receive_packets};
23
24pub struct NetClientPeer {
25    connection: RwLock<Option<Connection>>,
26    ev_dispatcher: UnboundedSender<NetClientPeerEvent>,
27    running: AtomicBool,
28    host_id: HostId,
29    game_id: u32,
30    channel_configurations: Box<[ChannelConfiguration]>,
31}
32
33#[derive(Debug)]
34pub enum NetClientPeerEvent {
35    Connected,
36    Disconnected,
37    ConnectionFailed,
38    PacketReceived { channel: usize, data: Vec<u8> },
39}
40
41impl NetClientPeer {
42    pub fn create(
43        game_id: u32,
44        host_id: HostId,
45        channel_configurations: &[ChannelConfiguration],
46    ) -> (Arc<Self>, UnboundedReceiver<NetClientPeerEvent>) {
47        let (ev_dispatcher, ev_queue) = unbounded_channel::<NetClientPeerEvent>();
48
49        let peer = Arc::new(Self {
50            connection: RwLock::new(None),
51            ev_dispatcher,
52            running: AtomicBool::new(true),
53            host_id,
54            game_id,
55            channel_configurations: channel_configurations.into(),
56        });
57
58        peer.clone().spawn_worker();
59
60        return (peer, ev_queue);
61    }
62
63    pub fn send_packet(&self, channel: usize, data: &[u8]) {
64        let conn = self.connection.blocking_read();
65
66        if let Some(conn) = &*conn {
67            let _ = RUNTIME.block_on(conn.channels[channel].send(&Bytes::copy_from_slice(data)));
68        }
69    }
70
71    fn channel_config_to_option(config: &ChannelConfiguration) -> DataChannelOptions {
72        let max_retransmits = if let Reliability::Retry(n) = config.reliability {
73            Some(n.get())
74        } else if let Reliability::Unreliable = config.reliability {
75            Some(0)
76        } else {
77            None
78        };
79
80        let max_packet_life_time = if let Reliability::ExpireAfter(deadline) = config.reliability {
81            Some(deadline.get())
82        } else {
83            None
84        };
85
86        return DataChannelOptions {
87            ordered: Some(config.ordered),
88            max_retransmits,
89            max_packet_life_time,
90
91            ..Default::default()
92        };
93    }
94
95    async fn connect_to_host(&self) -> Option<Connection> {
96        let mut signaling =
97            ClientPeerSignalingClient::new(&self.host_id.server_url, self.game_id).await?;
98
99        let channel_options = self
100            .channel_configurations
101            .iter()
102            .map(|it| ("pawkit_".to_string(), Self::channel_config_to_option(it)))
103            .collect::<Vec<_>>();
104
105        let channels = channel_options.len();
106
107        let connection = PeerConnectionBuilder::new()
108            .with_channel_options(channel_options)
109            .unwrap()
110            .build()
111            .await
112            .ok()?;
113
114        let offer = connection.get_local_description().await?;
115        let candidates = connection.collect_ice_candidates().await.ok()?;
116
117        let candidate = signaling
118            .offer_connection(self.host_id.clone(), offer, candidates)
119            .await?;
120
121        connection
122            .set_remote_description(candidate.offer)
123            .await
124            .ok()?;
125        let _ = connection.add_ice_candidates(candidate.candidates).await;
126
127        if let PeerConnectionState::Connected = connection.state_change().await {
128            return Connection::from(connection, channels).await.ok();
129        }
130
131        return None;
132    }
133
134    async fn worker_loop(self: Arc<Self>) {
135        let Some(conn) = self.connect_to_host().await else {
136            let _ = self
137                .ev_dispatcher
138                .send(NetClientPeerEvent::ConnectionFailed);
139            return;
140        };
141
142        {
143            let mut lock = self.connection.write().await;
144            *lock = Some(conn);
145        }
146
147        let _ = self.ev_dispatcher.send(NetClientPeerEvent::Connected);
148
149        while self.running.load(Ordering::Relaxed) {
150            let connection = self.connection.read().await;
151            let Some(connection) = &*connection else {
152                break;
153            };
154
155            tokio::select! {
156                Some((channel, data)) = receive_packets(&connection.channels) => {
157                    let _ = self
158                        .ev_dispatcher
159                        .send(NetClientPeerEvent::PacketReceived { channel, data });
160                }
161
162                PeerConnectionState::Disconnected = connection.raw_connection.state_change() => {
163                    break;
164                }
165
166                else => break
167            }
168        }
169
170        let _ = self.ev_dispatcher.send(NetClientPeerEvent::Disconnected);
171
172        self.running.store(false, Ordering::Relaxed);
173    }
174
175    pub fn disconnect(&self) {
176        self.running.store(false, Ordering::Relaxed);
177    }
178
179    fn spawn_worker(self: Arc<Self>) {
180        tokio::spawn(async move {
181            self.worker_loop().await;
182        });
183    }
184}
185
186pub struct SimpleNetClientPeer {
187    raw_peer: Arc<NetClientPeer>,
188    ev_queue: UnboundedReceiver<NetClientPeerEvent>,
189}
190
191impl SimpleNetClientPeer {
192    pub fn create(
193        game_id: u32,
194        host_id: HostId,
195        channel_configurations: &[ChannelConfiguration],
196    ) -> Self {
197        let (raw_peer, ev_queue) = NetClientPeer::create(game_id, host_id, channel_configurations);
198        Self { raw_peer, ev_queue }
199    }
200
201    pub fn next_event(&mut self) -> Option<NetClientPeerEvent> {
202        return self.ev_queue.try_recv().ok();
203    }
204}
205
206impl Drop for SimpleNetClientPeer {
207    fn drop(&mut self) {
208        self.disconnect();
209    }
210}
211
212impl Deref for SimpleNetClientPeer {
213    type Target = Arc<NetClientPeer>;
214
215    fn deref(&self) -> &Self::Target {
216        &self.raw_peer
217    }
218}