diff --git a/src/bin/client.rs b/src/bin/client.rs index 0f3e99f..416020a 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,9 +1,12 @@ extern crate ctrlc; -use clichess::{RecvPositionError, UserRole, EXIT_MSG}; + +use clichess::Player; +use clichess::{PlayingStatus, RecvPositionError, UserRole, EXIT_MSG}; use serde_json::json; use shakmaty::fen::Fen; use shakmaty::{Chess, Color, Outcome, Position, Setup}; use std::io; +use std::iter::Iterator; use std::os::unix::net::UnixStream; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, TryRecvError}; @@ -12,11 +15,21 @@ use std::thread; use std::time; struct Client { - player: clichess::Player, + player: Player, side: Color, running: Arc, input_buffer: Arc>, server_message_recv: Receiver, + playing: bool, +} + +enum LobbyLoopOutcome { + ClientLeft, +} + +enum PlayingLoopOutcome { + PlayerLeft, + Outcome(Outcome), } fn main() { @@ -41,33 +54,111 @@ fn main() { let server_message_recv = setup_server_message_recv(&stream).unwrap(); let mut client = Client { - player: clichess::Player { - role: UserRole::Spectator, + player: Player { + id: 0, username: username, public_key: public_key, + playing_status: PlayingStatus::WaitingInLobby, + role: UserRole::Spectator, }, side: Color::White, running: running.clone(), input_buffer: input_buffer.clone(), server_message_recv, + playing: false, }; - match prompt_user_for_role(&client, &mut stream) { - Some(role) => client.player.role = role.clone(), - None => return, - }; - if client.player.role == UserRole::Spectator { - println!( - "Hello, {} !\n\r You're spectating !", - client.player.username - ) - } else { - println!( - "Hello, {} !\n\r You're playing with the {} pieces", - client.player.username, - client.player.role.to_string() - ); + //get id from server + let player_id = fetch_message_from_server(&client).parse::().unwrap(); + println!("got id {} from server", player_id); + client.player.id = player_id; + + loop { + lobby_loop(&client, &mut stream); + match playing_loop(&client, &mut stream) { + PlayingLoopOutcome::PlayerLeft => { + println!("Bye"); + break; + } + PlayingLoopOutcome::Outcome(Outcome::Draw) => println!("Draw game."), + PlayingLoopOutcome::Outcome(Outcome::Decisive { winner }) => { + if winner == Color::White { + println!("White has won the game.") + } else { + println!("Black has won the game.") + } + } + }; } +} + +fn lobby_loop(client: &Client, stream: &mut UnixStream) { + println!("Sending first refresh to server..."); + clichess::write_to_stream(stream, String::from("refresh")).unwrap(); + println!("Getting list of players from server..."); + let players = fetch_players_from_server(client); + print_players(client, &players); + while client.running.load(Ordering::SeqCst) { + let mut user_input = client.input_buffer.lock().unwrap(); + if user_input.is_empty() { + continue; + } + let mut sent_to_server = String::from(user_input.trim()); + if sent_to_server == "refresh" || sent_to_server == "r" { + sent_to_server = String::from("refresh"); + } + println!("Sending {} to server...", sent_to_server); + clichess::write_to_stream(stream, sent_to_server.clone()).unwrap(); + user_input.clear(); + if sent_to_server == "refresh" { + let players = fetch_players_from_server(client); + print_players(client, &players); + } else { + let response = fetch_message_from_server(client); + println!("response from server: {}", response); + } + } +} + +fn fetch_players_from_server(client: &Client) -> Vec { + let data = fetch_message_from_server(client); + let v: Vec = serde_json::from_str(&data).unwrap(); + v +} + +fn print_players(client: &Client, players: &Vec) { + let other_players: Vec<&Player> = players.iter().filter(|p| p.id != client.player.id).collect(); + if other_players.is_empty() { + println!("No one is connected."); + } + for player in other_players { + let mut message_to_add: String; + match player.playing_status { + PlayingStatus::WaitingInLobby => message_to_add = String::default(), + PlayingStatus::Challenging(id) => { + message_to_add = String::from(if id == client.player.id { + " is challenging you" + } else { + "" + }) + } + PlayingStatus::PlayingAgainst(id) => { + message_to_add = " is playing against ".to_owned(); + message_to_add.push_str( + &players + .iter() + .filter(|p| p.id == id) + .last() + .expect("id is from the list") + .username, + ); + } + }; + println!("{}{}", player.username, message_to_add); + } +} + +fn playing_loop(client: &Client, stream: &mut UnixStream) -> PlayingLoopOutcome { let mut current_position = fetch_initial_chess_position(&client); loop { println!( @@ -80,31 +171,24 @@ fn main() { } if clichess::is_player_turn(&client.player, current_position.turn()) { //it's the user turn, taking user input - let input = read_user_input(&client); - clichess::write_to_stream(&mut stream, String::from(input.trim())).unwrap(); + let input = read_user_input(client); + clichess::write_to_stream(stream, String::from(input.trim())).unwrap(); if input.trim() == EXIT_MSG { break; } } //update position after playing. - match get_current_position(&client) { + match get_current_position(client) { Ok(position) => current_position = position, Err(_) => { - clichess::write_to_stream(&mut stream, String::from(EXIT_MSG)).unwrap(); + clichess::write_to_stream(stream, String::from(EXIT_MSG)).unwrap(); break; } }; } match current_position.outcome() { - None => println!("Bye"), - Some(Outcome::Draw) => println!("Draw game."), - Some(Outcome::Decisive { winner }) => { - if winner == Color::White { - println!("White has won the game.") - } else { - println!("Black has won the game.") - } - } + None => PlayingLoopOutcome::PlayerLeft, + Some(outcome) => PlayingLoopOutcome::Outcome(outcome), } } @@ -121,8 +205,6 @@ fn setup_input_buffer() -> Arc> { let mut user_input = buf2.lock().unwrap(); if user_input.is_empty() { *user_input = buffer; - } else { - println!("It's not your turn !"); } } } @@ -216,74 +298,6 @@ fn parse_position(string: &str) -> Chess { position } -fn prompt_user_for_role(client: &Client, stream: &mut UnixStream) -> Option { - let mut role = None; - loop { - let available_roles = fetch_available_roles(client); - let mut prompt = String::new(); - if !available_roles.contains(&UserRole::White) - && !available_roles.contains(&UserRole::Black) - { - prompt.push_str("You can only spectate this game. Press enter to start spectating."); - } else if available_roles.contains(&UserRole::White) { - prompt = String::from("Do you want to play as White (W)"); - if available_roles.contains(&UserRole::Black) { - prompt.push_str(", Black (B)"); - } - prompt.push_str(" or Spectate (S)?"); - } else { - prompt = String::from("Do you want to play as Black (B) or Spectate (S)?"); - } - println!("{}", prompt); - //wait for user to give a correct answer. - let mut input = String::from(read_user_input(client).trim()); - if input.trim() == EXIT_MSG { - clichess::write_to_stream(stream, String::from(EXIT_MSG)).unwrap(); - break; - } - - if !available_roles.contains(&UserRole::White) - && !available_roles.contains(&UserRole::Black) - { - //we can only spectate - input = String::from("S"); - } - match clichess::parse_to_role(&input) { - Ok(r) => role = Some(r), - Err(e) => { - println!("{}", e); - continue; - } - }; - if role.is_some() && !available_roles.contains(&role.expect("role is some")) { - println!("Sorry, this side is not available."); - continue; - } - //send info to server - clichess::write_to_stream(stream, String::from(input)).unwrap(); - //get confirmation from server - let response = fetch_message_from_server(&client); - if response != "OK" { - println!( - "There was an issue with the server. Maybe your choice is not available anymore?" - ); - clichess::write_to_stream(stream, String::from("ACK")).unwrap(); - continue; - } - clichess::write_to_stream(stream, String::from("OK")).unwrap(); - break; - } - role -} - fn fetch_initial_chess_position(client: &Client) -> Chess { parse_position(&fetch_message_from_server(client)) } - -fn fetch_available_roles(client: &Client) -> Vec { - roles_from_str(&fetch_message_from_server(client)).unwrap() -} - -fn roles_from_str(s: &str) -> Result, String> { - s.split(',').map(clichess::parse_to_role).collect() -} diff --git a/src/bin/server.rs b/src/bin/server.rs index 0de9acf..d68c9d1 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,5 +1,5 @@ use clichess; -use clichess::{Player, RecvPositionError, UserRole, EXIT_MSG}; +use clichess::{Player, RecvPositionError, UserRole, EXIT_MSG, PlayingStatus}; use serde_json::Value; use shakmaty::{fen, Chess, Color, Setup}; use std::collections::HashMap; @@ -67,6 +67,15 @@ fn initialize_client( //create player let player = create_player(server, stream)?; let player_turn: Color; + //send its id to the player + println!( + "server {}, send current id: {} to the player...", + server.id, + player.id + ); + clichess::write_to_stream(stream, player.id.to_string()).unwrap(); + println!("entering lobby..."); + lobby_loop(stream, server, &player); //send current position to the player println!( "server {}, send current position to the player...", @@ -81,6 +90,38 @@ fn initialize_client( Ok((player, player_turn)) } +fn lobby_loop(stream: &mut UnixStream, server: &Server, player: &Player) { + loop { + let input; + match server.client_message_recv.recv() { + Ok(i) => input = i, + Err(e) => { + println!("Error while getting user input: {}", e); + break; + } + }; + println!("got user input: {}", input); + if input == EXIT_MSG { + break; + } else if input == "refresh" { + send_players_to_client(stream, server); + } else { + clichess::write_to_stream(stream, format!("You challenged user {}", input)); + } + } +} + +fn send_players_to_client(stream: &mut UnixStream, server: &Server) { + println!("Sending players to client..."); + let players = server.players.lock().unwrap(); + let mut players_vec = Vec::new(); + for (_, (player, _)) in players.iter() { + players_vec.push(player); + } + let json_msg = serde_json::to_string(&players_vec).unwrap(); + clichess::write_to_stream(stream, json_msg).unwrap(); +} + fn main_loop(stream: &mut UnixStream, server: &Server, player: Player, mut player_turn: Color) { loop { if clichess::is_player_turn(&player, player_turn) { @@ -176,12 +217,14 @@ fn create_player(server: &Server, stream: &mut UnixStream) -> Result