-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Code review #1
base: main
Are you sure you want to change the base?
Code review #1
Conversation
}; | ||
} | ||
|
||
fn in_range(&self, val: &f64, left_border: &f64, right_border: &f64) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is no need to take primitive copy types by reference, just take them by value
} | ||
|
||
fn is_alive(&self) -> bool { | ||
if self.health <= 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be simplify
fn is_alive(&self) -> bool { self.health > 0 }
} | ||
|
||
fn in_range(&self, val: &f64, left_border: &f64, right_border: &f64) -> bool { | ||
if val >= left_border && val <= right_border { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why you don't use short condition?
fn in_range(&self, val: &f64, left_border: &f64, right_border: &f64) -> bool {
val >= left_border && val <= right_border
}
if let Some(socket_recipient) = self.sessions.get(id_to) { | ||
let _ = socket_recipient.do_send(WsMessage(message.to_owned())); | ||
} else { | ||
println!("attempting to send message but couldn't find user id."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use logger instead println!
.service(start_connection_route) | ||
.app_data(Data::new(game_server.clone())) | ||
}) | ||
.bind("127.0.0.1:8080")? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would better to use configuration for host and port
fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) { | ||
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { | ||
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { | ||
println!("Disconnecting failed heartbeat"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use logger
} | ||
Ok(ws::Message::Binary(bin)) => ctx.binary(bin), | ||
Ok(ws::Message::Close(reason)) => { | ||
println!("Connection closed: {:?}", reason); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use logger
let game_id_str = "8ec45aa2-fa28-42e8-a80c-fd6ea3098424"; | ||
let server_addr = format!("ws://127.0.0.1:8080/{}", game_id_str); | ||
|
||
println!("Connecting to {}", server_addr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use logger. Check all your print and replace it to logger
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code is well-structured and intuitive. The game takes into account many nuances: Even the ping of the players was handled. However, it could benefit from enhanced error handling (I mean .unwrap()) and logging.
if self.sessions.remove(&msg.id).is_some() { | ||
self.games_users_ids | ||
.get(&msg.game_id) | ||
.unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a possibility that the unwrapped value would be None?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added some minor suggestions, mainly related to error handling, also some style related.
error handling in rust is a big topic, but let's start simple.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use String
in error, use enum
type directly, then implements std::fmt::Display
(and std::error::Error
) instead. for example:
pub enum AppError {
GameStateError(GameState),
NumberOfPlayersError(usize),
PlayerNotFoundError,
OpponentNotFoundError,
}
impl Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
//...
}
}
}
you can use libraries like thiserror
or snafu
to help you reduce boilerplates.
return Err(AppError { | ||
message: "Player not found".to_string(), | ||
error_type: AppErrorType::PlayerNotFoundError, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
example:
return Err(AppError::PlayNotFoundError);
return Err(AppError { | ||
message: format!("Game state is {:?}", self.state), | ||
error_type: AppErrorType::GameStateError, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return Err(AppError::GameStateError(self.state));
|
||
pub fn add_player(&mut self, player_id: Uuid) -> Result<(), AppError> { | ||
if self.players.len() >= 2 { | ||
return Err(AppError { message: "The number of players cannot exceed 2".to_string(), error_type: AppErrorType::NumberOfPlayersError }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return Err(AppError::NumberOfPlayersError(self.players.len()));
Err(AppError { | ||
message: "Opponent player not found".to_string(), | ||
error_type: AppErrorType::PlayerNotFoundError, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Err(AppError::OpponentNotFoundError)
Err(err) => { | ||
let _ = msg.addr.do_send(WsMessage(err.message)); | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
types that implements Display
automatically implements ToString
let _ = msg.addr.do_send(WsMessage(err.to_string()));
let response; | ||
|
||
match game.handle_msg(&msg) { | ||
Ok(_) => response = serde_json::to_string(&game).unwrap(), | ||
Err(err) => response = err.message, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
match
is an expression and can be used in assignment:
let response = match game.handle_msg(&msg) {
Ok(_) => serde_json::to_string(&game).unwrap(),
Err(err) => err.to_string(),
};
one possible alternatives to match
is Result::map_or_else()
, but it's just a matter of personal taste:
let response = game.handle_msg.map_or_else(
|err| err.to_string(),
|_| serde_json::to_string(&game).unwrap(),
);
chars.next(); | ||
chars.next_back(); | ||
|
||
let msg_str= chars.as_str(); | ||
|
||
match msg_str { | ||
"Left" => Ok(Msg::Left), | ||
"Right" => Ok(Msg::Right), | ||
"Hit" => Ok(Msg::Hit), | ||
"Wait" => Ok(Msg::Wait), | ||
_ => { | ||
Err(()) | ||
}, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not bad or wrong, just a question, is unicode handling necessary in this use case?
revsered unicode iteration for a utf8 string (next_back()
in your code) is uncommon. it's totally ok if the use case requires it.
I suggest FromStr
only be used for simple situations like round trip conversion of ToString
, and if you need to parse arbitrary strings, FromStr
probably is not a good choice.
async move { | ||
while let Some(Ok(msg)) = read.next().await { | ||
match msg { | ||
Message::Text(text) => { | ||
if !*has_id.lock().unwrap() { | ||
let message: Vec<&str> = text.split(" ").collect(); | ||
// "your id is {uuid}" | ||
match message.get(3) { | ||
None => {} | ||
Some(uuid) => { | ||
match Uuid::parse_str(uuid) { | ||
Ok(uuid) => { | ||
*user_id.lock().unwrap() = uuid; | ||
*has_id.lock().unwrap() = true; | ||
} | ||
Err(_) => println!("Invalid Uuid format"), | ||
}; | ||
println!("Received message from server: {:?}", uuid.clone()); | ||
} | ||
}; | ||
} else { | ||
println!("Received message from server: {}", text); | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest refactor this code, its is deeply nested and shifted too much to the right.
let msg = match line.trim().to_lowercase().as_str() { | ||
"left" => Msg::Left, | ||
"right" => Msg::Right, | ||
"wait" => Msg::Wait, | ||
"hit" => Msg::Hit, | ||
_ => continue, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the FromStr
impl be adapted and reused for this?
if let Some(player) = self.players.get(player_id) { | ||
Ok(player) | ||
} else { | ||
return Err(AppError { | ||
message: "Player not found".to_string(), | ||
error_type: AppErrorType::PlayerNotFoundError, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like can be simplified to
self.players.get(player_id).ok_or_else(|| AppError {
message: "Player not found".to_string(),
error_type: AppErrorType::PlayerNotFoundError,
})?;
for (uuid, _) in &self.players { | ||
if uuid != player_id { | ||
return Ok(uuid.clone()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it more idiomatic
if self.players.iter().any(|(uuid, _)| uuid != player_id) { return Ok(uuid.clone) }
Up to you
No description provided.