Skip to main content

pawkit_net/
host.rs

1use std::{
2    ops::Deref,
3    pin::Pin,
4    sync::{
5        Arc,
6        atomic::{AtomicBool, Ordering},
7    },
8};
9
10use bytes::Bytes;
11use futures_util::{StreamExt, stream::FuturesUnordered};
12use just_webrtc::{
13    DataChannelExt, PeerConnectionBuilder, PeerConnectionExt, types::PeerConnectionState,
14};
15use pawkit_holy_array::HolyArray;
16use pawkit_net_signaling::{
17    ChannelConfiguration,
18    client::{ClientConnectionCandidate, HostPeerSignalingClient},
19    model::HostId,
20};
21use tokio::sync::{
22    RwLock,
23    mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel},
24};
25
26use crate::{Connection, PacketFuture, RUNTIME, receive_packet};
27
28pub struct NetHostPeer {
29    connected_clients: RwLock<HolyArray<Arc<Connection>>>,
30    ev_dispatcher: UnboundedSender<NetHostPeerEvent>,
31    running: AtomicBool,
32    game_id: u32,
33    host_id: RwLock<HostId>,
34    request_proxy: bool,
35    channel_configurations: Box<[ChannelConfiguration]>,
36}
37
38#[derive(Debug)]
39pub enum NetHostPeerEvent {
40    PeerConnected {
41        peer_id: usize,
42    },
43    PeerDisconnected {
44        peer_id: usize,
45    },
46    PacketReceived {
47        peer_id: usize,
48        channel: usize,
49        data: Vec<u8>,
50    },
51    HostIdUpdated,
52}
53
54impl NetHostPeer {
55    pub fn create(
56        server_url: &str,
57        game_id: u32,
58        request_proxy: bool,
59        channel_configurations: &[ChannelConfiguration],
60    ) -> (Arc<Self>, UnboundedReceiver<NetHostPeerEvent>) {
61        let (ev_dispatcher, ev_queue) = unbounded_channel::<NetHostPeerEvent>();
62
63        let value = Arc::new(Self {
64            connected_clients: RwLock::new(HolyArray::new()),
65            ev_dispatcher,
66            running: AtomicBool::new(true),
67            game_id,
68            host_id: RwLock::new(HostId {
69                server_url: server_url.into(),
70                lobby_id: 0,
71                shard_id: 0,
72            }),
73            request_proxy,
74            channel_configurations: channel_configurations.into(),
75        });
76
77        value.clone().spawn_worker();
78
79        return (value, ev_queue);
80    }
81
82    pub fn get_host_id(&self) -> HostId {
83        return self.host_id.blocking_read().clone();
84    }
85
86    pub fn send_packet(&self, client_id: usize, channel: usize, data: &[u8]) {
87        let clients = self.connected_clients.blocking_read();
88
89        let Some(client) = clients.get(client_id) else {
90            return;
91        };
92
93        let _ = RUNTIME.block_on(client.channels[channel].send(&Bytes::copy_from_slice(data)));
94    }
95
96    async fn handle_candidate(
97        &self,
98        signaling: &mut HostPeerSignalingClient,
99        candidate: ClientConnectionCandidate,
100        channels: usize,
101    ) -> Option<usize> {
102        let Ok(connection) = PeerConnectionBuilder::new()
103            .with_remote_offer(Some(candidate.offer.clone()))
104            .unwrap()
105            .build()
106            .await
107        else {
108            signaling.reject_candidate(candidate.client_id).await;
109            return None;
110        };
111
112        let _ = connection
113            .add_ice_candidates(candidate.candidates.clone())
114            .await;
115
116        let Some(offer) = connection.get_local_description().await else {
117            signaling.reject_candidate(candidate.client_id).await;
118            return None;
119        };
120
121        let Ok(candidates) = connection.collect_ice_candidates().await else {
122            signaling.reject_candidate(candidate.client_id).await;
123            return None;
124        };
125
126        signaling
127            .accept_candidate(candidate.client_id, offer, candidates)
128            .await;
129
130        let mut connected_clients = self.connected_clients.write().await;
131
132        let PeerConnectionState::Connected = connection.state_change().await else {
133            return None;
134        };
135
136        let connection = Connection::from(connection, channels).await.ok()?;
137
138        let peer_id = connected_clients.acquire(Arc::new(connection));
139
140        let _ = self
141            .ev_dispatcher
142            .send(NetHostPeerEvent::PeerConnected { peer_id });
143
144        return Some(peer_id);
145    }
146
147    async fn refresh_signaling(&self, signaling: &mut HostPeerSignalingClient) {
148        if !signaling.is_open() {
149            {
150                let Some(new_signaling) = HostPeerSignalingClient::new(
151                    &self.host_id.read().await.server_url,
152                    self.game_id,
153                    self.request_proxy,
154                )
155                .await
156                else {
157                    return;
158                };
159                *signaling = new_signaling;
160            }
161            {
162                *self.host_id.write().await = signaling.host_id.clone();
163            }
164            let _ = self.ev_dispatcher.send(NetHostPeerEvent::HostIdUpdated);
165        }
166    }
167
168    async fn packet_task(
169        peer: Arc<Connection>,
170        peer_id: usize,
171        channel: usize,
172    ) -> (Option<(usize, Vec<u8>)>, usize) {
173        tokio::select! {
174            Some(packet) = receive_packet(&peer.channels[channel]) => {
175                return (Some((channel, packet)), peer_id)
176            }
177
178            PeerConnectionState::Disconnected = peer.raw_connection.state_change() => {
179                return (None, peer_id)
180            }
181        }
182    }
183
184    async fn add_packet_task(
185        &self,
186        tasks: &FuturesUnordered<Pin<Box<PacketFuture>>>,
187        peer_id: usize,
188        channel: usize,
189    ) {
190        let clients = self.connected_clients.read().await;
191        let Some(peer) = clients.get(peer_id) else {
192            tasks.push(Box::pin(async move { (None, peer_id) }));
193            return;
194        };
195
196        tasks.push(Box::pin(Self::packet_task(peer.clone(), peer_id, channel)));
197    }
198
199    async fn worker_loop(&self) {
200        let mut signaling = {
201            loop {
202                let Some(host) = HostPeerSignalingClient::new(
203                    &self.host_id.read().await.server_url,
204                    self.game_id,
205                    self.request_proxy,
206                )
207                .await
208                else {
209                    continue;
210                };
211
212                break host;
213            }
214        };
215        {
216            *self.host_id.write().await = signaling.host_id.clone();
217        }
218        let _ = self.ev_dispatcher.send(NetHostPeerEvent::HostIdUpdated);
219
220        let mut tasks = FuturesUnordered::<Pin<Box<PacketFuture>>>::new();
221
222        while self.running.load(Ordering::Relaxed) {
223            self.refresh_signaling(&mut signaling).await;
224
225            tokio::select! {
226                Some(candidate) = signaling.next_candidate() => {
227                    let Some(peer_id) = self.handle_candidate(&mut signaling, candidate, self.channel_configurations.len()).await else {
228                        continue;
229                    };
230
231                    for channel in 0..self.channel_configurations.len() {
232                        self.add_packet_task(&tasks, peer_id, channel).await;
233                    }
234                }
235
236                Some((packet, peer_id)) = tasks.next() => {
237                    let Some((channel, data)) = packet else {
238                        let mut connected_clients = self.connected_clients.write().await;
239                        connected_clients.release(peer_id);
240
241                        let _ = self
242                            .ev_dispatcher
243                            .send(NetHostPeerEvent::PeerDisconnected { peer_id });
244
245                        continue;
246                    };
247
248                    let _ = self
249                        .ev_dispatcher
250                        .send(NetHostPeerEvent::PacketReceived { peer_id, channel, data });
251
252                    self.add_packet_task(&tasks, peer_id, channel).await;
253                }
254
255                else => {
256                    continue;
257                }
258            }
259        }
260    }
261
262    pub fn shutdown(&self) {
263        self.running.store(false, Ordering::Relaxed);
264    }
265
266    fn spawn_worker(self: Arc<Self>) {
267        tokio::spawn(async move {
268            self.worker_loop().await;
269        });
270    }
271}
272
273pub struct SimpleNetHostPeer {
274    raw_peer: Arc<NetHostPeer>,
275    ev_queue: UnboundedReceiver<NetHostPeerEvent>,
276}
277
278impl SimpleNetHostPeer {
279    pub fn create(
280        server_url: &str,
281        game_id: u32,
282        request_proxy: bool,
283        channel_configurations: &[ChannelConfiguration],
284    ) -> Self {
285        let (raw_peer, ev_queue) =
286            NetHostPeer::create(server_url, game_id, request_proxy, channel_configurations);
287
288        return Self { raw_peer, ev_queue };
289    }
290
291    pub fn next_event(&mut self) -> Option<NetHostPeerEvent> {
292        return self.ev_queue.try_recv().ok();
293    }
294}
295
296impl Drop for SimpleNetHostPeer {
297    fn drop(&mut self) {
298        self.shutdown();
299    }
300}
301
302impl Deref for SimpleNetHostPeer {
303    type Target = Arc<NetHostPeer>;
304
305    fn deref(&self) -> &Self::Target {
306        return &self.raw_peer;
307    }
308}