Live location tracking and playback for the game "manhunt"
1use std::{collections::HashMap, sync::Arc};
2
3use tokio::{
4 sync::{Mutex, mpsc},
5 task::yield_now,
6};
7use uuid::Uuid;
8
9use crate::{
10 MsgPair, StateUpdateSender, Transport, TransportMessage,
11 location::{Location, LocationService},
12 prelude::*,
13};
14
15type GameEventRx = mpsc::Receiver<MsgPair>;
16type GameEventTx = mpsc::Sender<MsgPair>;
17
18pub struct MockTransport {
19 id: Uuid,
20 rx: Mutex<GameEventRx>,
21 txs: HashMap<Uuid, GameEventTx>,
22}
23
24impl MockTransport {
25 pub fn create_mesh(players: u32) -> (Vec<Uuid>, Vec<Self>) {
26 let uuids = (0..players)
27 .map(|_| uuid::Uuid::new_v4())
28 .collect::<Vec<_>>();
29 let channels = (0..players)
30 .map(|_| tokio::sync::mpsc::channel(20))
31 .collect::<Vec<_>>();
32 let txs = channels
33 .iter()
34 .enumerate()
35 .map(|(i, (tx, _))| (uuids[i], tx.clone()))
36 .collect::<HashMap<_, _>>();
37
38 let transports = channels
39 .into_iter()
40 .enumerate()
41 .map(|(i, (_tx, rx))| Self::new(uuids[i], rx, txs.clone()))
42 .collect::<Vec<_>>();
43
44 (uuids, transports)
45 }
46
47 pub async fn wait_for_queue_empty(&self) {
48 // println!("Waiting for {} queue to empty", self.id);
49 loop {
50 let all_empty = self
51 .txs
52 .values()
53 .all(|tx| tx.is_closed() || tx.capacity() == tx.max_capacity());
54
55 if all_empty {
56 break;
57 } else {
58 yield_now().await;
59 }
60 }
61 }
62
63 pub async fn fake_join(&self) {
64 self.send_message(TransportMessage::PeerConnect(self.id))
65 .await;
66 }
67
68 pub fn is_disconnected(&self) -> bool {
69 self.txs[&self.id].is_closed()
70 }
71
72 fn new(id: Uuid, rx: GameEventRx, txs: HashMap<Uuid, GameEventTx>) -> Self {
73 Self {
74 id,
75 rx: Mutex::new(rx),
76 txs,
77 }
78 }
79}
80
81impl Transport for MockTransport {
82 async fn initialize(_code: &str, _host: bool) -> Result<Arc<Self>> {
83 let (_, rx) = mpsc::channel(5);
84 Ok(Arc::new(Self {
85 id: Uuid::default(),
86 rx: Mutex::new(rx),
87 txs: HashMap::default(),
88 }))
89 }
90
91 async fn disconnect(&self) {
92 self.send_message(TransportMessage::PeerDisconnect(self.id))
93 .await;
94 let mut rx = self.rx.lock().await;
95 rx.close();
96 }
97
98 async fn receive_messages(&self) -> impl Iterator<Item = MsgPair> {
99 let mut rx = self.rx.lock().await;
100 let mut buf = Vec::with_capacity(20);
101 rx.recv_many(&mut buf, 20).await;
102 buf.into_iter()
103 }
104
105 async fn send_message(&self, msg: TransportMessage) {
106 for (_id, tx) in self.txs.iter().filter(|(id, _)| **id != self.id) {
107 tx.send((Some(self.id), msg.clone())).await.ok();
108 }
109 }
110
111 async fn send_message_single(&self, peer: Uuid, msg: TransportMessage) {
112 if let Some(tx) = self.txs.get(&peer) {
113 tx.send((Some(self.id), msg)).await.ok();
114 }
115 }
116
117 async fn send_self(&self, msg: TransportMessage) {
118 self.txs[&self.id].send((Some(self.id), msg)).await.ok();
119 }
120
121 fn self_id(&self) -> Uuid {
122 self.id
123 }
124}
125
126pub struct MockLocation;
127
128impl LocationService for MockLocation {
129 fn get_loc(&self) -> Option<Location> {
130 Some(crate::location::Location {
131 lat: 0.0,
132 long: 0.0,
133 heading: None,
134 })
135 }
136}
137
138pub struct DummySender;
139
140impl StateUpdateSender for DummySender {
141 fn send_update(&self) {}
142}