From 0a2aba9619ed2dab528fd0dd878faba3afe99d22 Mon Sep 17 00:00:00 2001 From: Artlef Date: Sun, 1 Mar 2020 19:09:39 +0100 Subject: [PATCH] Manage when user leave during connection. --- src/bin/client.rs | 27 +++++++++++----------- src/bin/server.rs | 57 +++++++++++++++++++++++++++++++++++------------ src/lib.rs | 26 ++++++++++++++++++--- 3 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/bin/client.rs b/src/bin/client.rs index 725063d..1a10d38 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,4 +1,6 @@ extern crate ctrlc; +use clichess::RecvPositionError; +use serde_json::json; use shakmaty::fen::Fen; use shakmaty::{Chess, Color, Outcome, Position, Setup}; use std::io; @@ -7,7 +9,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::thread; use std::time; -use serde_json::json; struct Client { player: clichess::Player, @@ -17,12 +18,6 @@ struct Client { server_message: Arc>, } -enum RecvPositionError { - UserCanceledError, - CommunicationError, - ParsePositionError, -} - fn main() { let running = setupctrlc(); let username = std::env::args().nth(1).expect("no name given"); @@ -56,8 +51,10 @@ fn main() { server_message: server_message.clone(), }; - let role = prompt_user_for_role(&client, &mut stream); - client.player.role = role.clone(); + match prompt_user_for_role(&client, &mut stream) { + Some(role) => client.player.role = role.clone(), + None => return, + }; println!( "Hello, {} !\n\r You're playing with the {} pieces", client.player.username, @@ -218,8 +215,8 @@ fn parse_position(string: &str) -> Chess { position } -fn prompt_user_for_role(client: &Client, stream: &mut UnixStream) -> clichess::UserRole { - let mut role: clichess::UserRole; +fn prompt_user_for_role(client: &Client, stream: &mut UnixStream) -> Option { + let mut role = None; loop { println!("fetching roles from server..."); let available_roles = fetch_available_roles(client); @@ -241,14 +238,18 @@ fn prompt_user_for_role(client: &Client, stream: &mut UnixStream) -> clichess::U println!("{}", prompt); //wait for user to give a correct answer. let input = String::from(read_user_input(client).trim()); + if input.trim() == "exit" { + clichess::write_to_stream(stream, String::from("exit")).unwrap(); + break; + } match clichess::parse_to_role(&input) { - Ok(r) => role = r, + Ok(r) => role = Some(r), Err(e) => { println!("{}", e); continue; } }; - if !available_roles.contains(&role) { + if role.is_some() && !available_roles.contains(&role.expect("role is some")) { println!("Sorry, this side is not available."); continue; } diff --git a/src/bin/server.rs b/src/bin/server.rs index a95b3bd..dbef881 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,5 +1,6 @@ use clichess; use clichess::Player; +use clichess::RecvPositionError; use clichess::UserRole; use serde_json::Value; use shakmaty::{fen, Chess, Color, Setup}; @@ -49,9 +50,20 @@ fn main() { } fn handle_player(mut stream: UnixStream, server: Server) { + match initialize_client(&mut stream, &server) { + Ok((player, player_turn)) => main_loop(&mut stream, &server, player, player_turn), + Err(e) => println!("User id {} could not be initialized: {}", server.id, e), + }; + player_disconnected(); +} + +fn initialize_client( + stream: &mut UnixStream, + server: &Server, +) -> Result<(Player, Color), RecvPositionError> { //create player - let player = create_player(&server, &mut stream); - let mut player_turn: Color; + let player = create_player(server, stream)?; + let player_turn: Color; //send current position to the player println!( "server {}, send current position to the player...", @@ -60,15 +72,19 @@ fn handle_player(mut stream: UnixStream, server: Server) { { let chess = server.chess_position.lock().unwrap(); player_turn = chess.turn(); - clichess::write_to_stream(&mut stream, fen::fen(&*chess)).unwrap(); + clichess::write_to_stream(stream, fen::fen(&*chess)).unwrap(); } println!("server {}, current position to the player sent", server.id); + Ok((player, player_turn)) +} + +fn main_loop(stream: &mut UnixStream, server: &Server, player: Player, mut player_turn: Color) { loop { if clichess::is_player_turn(&player, player_turn) { //let go of the lock while waiting for user input. println!("server {}, waiting for player move..", server.id); let input; - match clichess::read_line_from_stream(&stream) { + match clichess::read_line_from_stream(stream) { Ok(i) => input = i, Err(e) => { println!("Error while getting user input: {}", e); @@ -93,7 +109,7 @@ fn handle_player(mut stream: UnixStream, server: Server) { cvar_buffer.1.notify_one(); } } - clichess::write_to_stream(&mut stream, chessfen).unwrap(); + clichess::write_to_stream(stream, chessfen).unwrap(); player_turn = chess.turn(); } } else { @@ -105,7 +121,7 @@ fn handle_player(mut stream: UnixStream, server: Server) { buffer = cvar.wait(buffer).unwrap(); } println!("server id: {}, sending {}", server.id, buffer); - match clichess::write_to_stream(&mut stream, buffer.clone()) { + match clichess::write_to_stream(stream, buffer.clone()) { Ok(()) => buffer.clear(), Err(e) => { println!("{}", e); @@ -117,10 +133,9 @@ fn handle_player(mut stream: UnixStream, server: Server) { } } } - println!("Player disconnected.") } -fn create_player(server: &Server, stream: &mut UnixStream) -> Player { +fn create_player(server: &Server, stream: &mut UnixStream) -> Result { println!("Creating player {}...", server.id); //get player name and pubkey let username_pubkey_json = @@ -130,7 +145,7 @@ fn create_player(server: &Server, stream: &mut UnixStream) -> Player { let public_key = username_pubkey_value["pubkey"].to_string(); println!("got username: {}", username); println!("got pubkey: {}", public_key); - let role = receive_user_role(server, stream); + let role = receive_user_role(server, stream)?; let mut players = server.players.lock().unwrap(); let player = Player { role, @@ -139,10 +154,13 @@ fn create_player(server: &Server, stream: &mut UnixStream) -> Player { }; players.insert(server.id, (player.clone(), server.receiving_buffer.clone())); println!("Created player {}", server.id); - player + Ok(player) } -fn receive_user_role(server: &Server, stream: &mut UnixStream) -> UserRole { +fn receive_user_role( + server: &Server, + stream: &mut UnixStream, +) -> Result { let mut chosen_role: UserRole; loop { //send available roles @@ -152,9 +170,16 @@ fn receive_user_role(server: &Server, stream: &mut UnixStream) -> UserRole { //receive chosen role let chosen_role_str = clichess::read_line_from_stream(stream).expect("Player closed connection."); + if chosen_role_str == "exit" { + return Err(RecvPositionError::UserCanceledError); + } println!("Client id {} has chosen {}", server.id, chosen_role_str); - chosen_role = - clichess::parse_to_role(&chosen_role_str).expect("Client did not send parsable role."); + let chosen_role_parse = clichess::parse_to_role(&chosen_role_str); + + if chosen_role_parse.is_err() { + return Err(RecvPositionError::ParsePositionError); + } + chosen_role = chosen_role_parse.unwrap(); //check if role is still available after the choice { let mut available_roles_after_choice = @@ -183,7 +208,7 @@ fn receive_user_role(server: &Server, stream: &mut UnixStream) -> UserRole { } } } - chosen_role + Ok(chosen_role) } fn compute_available_roles_to_str(server: &Server) -> String { @@ -201,3 +226,7 @@ fn compute_available_roles_to_str(server: &Server) -> String { .collect(); available_roles_str.join(",") } + +fn player_disconnected() { + println!("Player disconnected."); +} diff --git a/src/lib.rs b/src/lib.rs index 0031ae2..95347d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,6 @@ use std::io; use std::io::prelude::*; use std::io::{BufReader, Write}; use std::os::unix::net::UnixStream; -use std::ops::Add; #[derive(Clone)] pub struct Player { @@ -38,6 +37,27 @@ impl fmt::Display for UserRole { } } +pub enum RecvPositionError { + UserCanceledError, + CommunicationError, + ParsePositionError, +} + +impl fmt::Display for RecvPositionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + RecvPositionError::CommunicationError => + "Error during the communication between client and server.", + RecvPositionError::ParsePositionError => "Error when parsing current position.", + RecvPositionError::UserCanceledError => "Cancelled by user.", + } + ) + } +} + struct SquareToPrint { color: String, background_color: String, @@ -205,7 +225,7 @@ pub fn parse_to_role(s: &str) -> Result { "w" => Ok(UserRole::White), "b" => Ok(UserRole::Black), "s" => Ok(UserRole::Spectator), - _ => Err(String::from("Please enter a valid answer.")) + _ => Err(String::from("Please enter a valid answer.")), } } @@ -213,7 +233,7 @@ pub fn role_to_str(r: &UserRole) -> String { String::from(match *r { UserRole::White => "w", UserRole::Black => "b", - UserRole::Spectator => "s" + UserRole::Spectator => "s", }) }