zero_trust_rps/common/client/
do_moves.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::common::{
    blake3::Blake3Hash as _,
    message::{ClientMessage, Hash, HashWithData, Round},
    utils::hash_users,
};

use super::{
    channel::{AsyncChannelSender, SendError},
    connection::read::SimplifiedServerMessage,
    simple_move::SimpleUserMove,
    state::ClientState,
};

#[derive(thiserror::Error, Debug)]
pub enum MoveError {
    #[error("{}", .0)]
    SendError(#[from] SendError),
    #[error("{}", .0)]
    Other(String),
}

impl From<String> for MoveError {
    fn from(value: String) -> Self {
        MoveError::Other(value)
    }
}

pub async fn do_move(
    state: &mut ClientState,
    umove: SimpleUserMove,
    sender: impl AsyncChannelSender<ClientMessage>,
    answer: impl AsyncChannelSender<SimplifiedServerMessage>,
) -> Result<(), MoveError> {
    if umove.allowed_state() != state.state {
        let message = format!(
            "Tried to play {umove:?} even though, state is {:?} and not {:?}",
            state.state,
            umove.allowed_state(),
        );
        log::warn!("{message}");
        answer.send(SimplifiedServerMessage::Error(message)).await?;

        return Ok(());
    }
    match umove {
        SimpleUserMove::JoinRoom(id) => {
            sender
                .send(ClientMessage::Join {
                    user: state.user,
                    room_id: id,
                })
                .await?
        }
        SimpleUserMove::Play(play) => {
            state.last_play = Some(play);

            let room = state.room.as_ref().expect("state is InRoom");

            let round = room.round.clone().unwrap_or_else(|| {
                let users: Box<[_]> = room.users.iter().map(|u| u.id).collect();
                Round {
                    round_id: hash_users(users.iter().copied()),
                    users,
                }
            });

            let hash = state
                .last_play
                .as_ref()
                .expect("set above")
                .hash_keyed(&state.secret, &round);

            sender
                .send(ClientMessage::Play {
                    value: Hash::B3sum(hash),
                    round,
                })
                .await?;
        }
        SimpleUserMove::ConfirmPlay => {
            let last_play = state
                .last_play
                .as_ref()
                .ok_or_else(|| "Can't get last play, should be there!".to_string())?;
            let hash = last_play.hash_keyed(
                &state.secret,
                state
                    .room
                    .as_ref()
                    .expect("room")
                    .round
                    .as_ref()
                    .expect("round"),
            );

            sender
                .send(ClientMessage::ConfirmPlay(HashWithData::B3sum {
                    hash,
                    key: state.secret,
                    data: *last_play,
                }))
                .await?;
        }
        SimpleUserMove::BackToRoom => {
            sender
                .send(ClientMessage::RoundFinished {
                    round_id: state
                        .room
                        .as_ref()
                        .expect("room")
                        .round
                        .as_ref()
                        .expect("round")
                        .round_id,
                })
                .await?
        }
    };

    Ok(())
}