zero_trust_rps/common/client/
do_moves.rs

1use 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}