zero_trust_rps/common/client/
do_moves.rs1use crate::common::{
2 blake3::Blake3Hash as _,
3 message::{ClientMessage, Hash, HashWithData, Round},
4 rps::{
5 move_validation::validate_move, simple_move::SimpleUserMove, state::UserMoveMethods as _,
6 },
7 utils::hash_users,
8};
9
10use super::{
11 channel::{AsyncChannelSender, SendError},
12 connection::read::SimplifiedServerMessage,
13 state::ClientState,
14};
15
16#[derive(thiserror::Error, Debug)]
17pub enum MoveError {
18 #[error("{}", .0)]
19 SendError(#[from] SendError),
20 #[error("{}", .0)]
21 Other(String),
22}
23
24impl From<String> for MoveError {
25 fn from(value: String) -> Self {
26 MoveError::Other(value)
27 }
28}
29
30pub async fn do_move(
31 state: &mut ClientState,
32 umove: SimpleUserMove,
33 sender: impl AsyncChannelSender<ClientMessage>,
34 answer: impl AsyncChannelSender<SimplifiedServerMessage>,
35) -> Result<(), MoveError> {
36 if let Err(error) = validate_move(
37 state.user,
38 &umove,
39 state.room.iter().flat_map(|room| room.users.iter()),
40 state.room.as_ref().and_then(|room| room.round.as_ref()),
41 ) {
42 let message = format!(
43 "Could not do move: {error}. Tried to play {umove:?}, state is {:?} and should be {:?}",
44 state.state,
45 umove.allowed_state(),
46 );
47 log::warn!("{message}");
48 answer.send(SimplifiedServerMessage::Error(message)).await?;
49
50 return Ok(());
51 }
52 match umove {
53 SimpleUserMove::JoinRoom(id) => {
54 sender
55 .send(ClientMessage::Join {
56 user: state.user,
57 room_id: id,
58 })
59 .await?
60 }
61 SimpleUserMove::Play(play) => {
62 state.last_play = Some(play);
63
64 let room = state.room.as_ref().expect("state is InRoom");
65
66 let round = room.round.clone().unwrap_or_else(|| {
67 let users: Box<[_]> = room.users.iter().map(|u| u.id).collect();
68 Round {
69 round_id: hash_users(users.iter().copied()),
70 users,
71 }
72 });
73
74 let hash = state
75 .last_play
76 .as_ref()
77 .expect("set above")
78 .hash_keyed(&state.secret, &round);
79
80 sender
81 .send(ClientMessage::Play {
82 value: Hash::B3sum(hash),
83 round,
84 })
85 .await?;
86 }
87 SimpleUserMove::ConfirmPlay => {
88 let last_play = state
89 .last_play
90 .as_ref()
91 .ok_or_else(|| "Can't get last play, should be there!".to_string())?;
92 let hash = last_play.hash_keyed(
93 &state.secret,
94 state
95 .room
96 .as_ref()
97 .expect("room")
98 .round
99 .as_ref()
100 .expect("round"),
101 );
102
103 sender
104 .send(ClientMessage::ConfirmPlay(HashWithData::B3sum {
105 hash,
106 key: state.secret,
107 data: *last_play,
108 }))
109 .await?;
110 }
111 SimpleUserMove::BackToRoom => {
112 sender
113 .send(ClientMessage::RoundFinished {
114 round_id: state
115 .room
116 .as_ref()
117 .expect("room")
118 .round
119 .as_ref()
120 .expect("round")
121 .round_id,
122 })
123 .await?
124 }
125 };
126
127 Ok(())
128}