Skip to content
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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Code review #1

wants to merge 5 commits into from

Conversation

advanced-user
Copy link
Owner

No description provided.

};
}

fn in_range(&self, val: &f64, left_border: &f64, right_border: &f64) -> bool {

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 {
Copy link

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 {
Copy link

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.");
Copy link

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")?
Copy link

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");
Copy link

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);
Copy link

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);
Copy link

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

Copy link

@VictoriaGrasshopper VictoriaGrasshopper left a 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()

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?

Copy link

@nerditation nerditation left a 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.

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.

Comment on lines +88 to +91
return Err(AppError {
message: "Player not found".to_string(),
error_type: AppErrorType::PlayerNotFoundError,
})

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);

Comment on lines +141 to +144
return Err(AppError {
message: format!("Game state is {:?}", self.state),
error_type: AppErrorType::GameStateError,
});

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 });

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()));

Comment on lines +164 to +167
Err(AppError {
message: "Opponent player not found".to_string(),
error_type: AppErrorType::PlayerNotFoundError,
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Err(AppError::OpponentNotFoundError)

Comment on lines +82 to +84
Err(err) => {
let _ = msg.addr.do_send(WsMessage(err.message));
return;

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()));

Comment on lines +114 to +119
let response;

match game.handle_msg(&msg) {
Ok(_) => response = serde_json::to_string(&game).unwrap(),
Err(err) => response = err.message,
}

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(),
);

Comment on lines +52 to +65
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(())
},
}

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.

Comment on lines +40 to +67
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);
}
}
_ => {}
}
}
}

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.

Comment on lines +75 to +81
let msg = match line.trim().to_lowercase().as_str() {
"left" => Msg::Left,
"right" => Msg::Right,
"wait" => Msg::Wait,
"hit" => Msg::Hit,
_ => continue,
};

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?

Comment on lines +96 to +102
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,
})

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,
            })?;

Comment on lines +158 to +162
for (uuid, _) in &self.players {
if uuid != player_id {
return Ok(uuid.clone());
}
}

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants