zero_trust_rps/common/client/
do_moves.rsuse crate::common::{
blake3::Blake3Hash as _,
message::{ClientMessage, Hash, HashWithData, Round},
rps::{
move_validation::validate_move, simple_move::SimpleUserMove, state::UserMoveMethods as _,
},
utils::hash_users,
};
use super::{
channel::{AsyncChannelSender, SendError},
connection::read::SimplifiedServerMessage,
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 let Err(error) = validate_move(
state.user,
&umove,
state.room.iter().flat_map(|room| room.users.iter()),
state.room.as_ref().and_then(|room| room.round.as_ref()),
) {
let message = format!(
"Could not do move: {error}. Tried to play {umove:?}, state is {:?} and should be {:?}",
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(())
}