5 Commits
wip ... v0.2.0

Author SHA1 Message Date
1fb3194786 Print version in log
Release 0.2.0
2020-08-05 23:00:51 +02:00
ee7d52f401 Show the username that played received move 2020-05-23 22:36:24 +02:00
c692be6159 Do not use cpu when waiting for opponent move. 2020-05-04 23:45:34 +02:00
2c4b339bc2 Refactor code to simplify client deconnection 2020-05-03 19:44:49 +02:00
56699033d9 Default side for black player is black 2020-05-03 18:51:25 +02:00
5 changed files with 245 additions and 257 deletions

2
Cargo.lock generated
View File

@ -44,7 +44,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "clichess" name = "clichess"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "clichess" name = "clichess"
version = "0.1.0" version = "0.2.0"
authors = ["Artlef <artlef@localhost>"] authors = ["Artlef <artlef@localhost>"]
edition = "2018" edition = "2018"

View File

@ -1,39 +1,26 @@
extern crate ctrlc; extern crate ctrlc;
use clichess::{GameInfo, RecvPositionError, UserRole, EXIT_MSG};
use clichess::Player;
use clichess::{PlayingStatus, RecvPositionError, UserRole, EXIT_MSG};
use serde_json::json; use serde_json::json;
use shakmaty::fen::Fen; use shakmaty::fen::Fen;
use shakmaty::{Chess, Color, Outcome, Position, Setup}; use shakmaty::{Chess, Color, Outcome, Position, Setup};
use std::io; use std::io;
use std::iter::Iterator;
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Receiver, TryRecvError}; use std::sync::mpsc::{channel, Receiver, TryRecvError};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::time; use std::time;
struct Client { struct Client {
player: Player, player: clichess::Player,
side: Color, side: Color,
running: Arc<AtomicBool>,
input_buffer: Arc<Mutex<String>>, input_buffer: Arc<Mutex<String>>,
server_message_recv: Receiver<String>, server_message_recv: Receiver<String>,
playing: bool, opponent_name: Option<String>,
}
enum LobbyLoopOutcome {
ClientLeft,
}
enum PlayingLoopOutcome {
PlayerLeft,
Outcome(Outcome),
} }
fn main() { fn main() {
let running = setupctrlc(); let version = env!("CARGO_PKG_VERSION");
println!("Running clichess version {}", version);
let username = std::env::args().nth(1).expect("no name given"); let username = std::env::args().nth(1).expect("no name given");
let public_key = std::env::args().nth(2).expect("no public key given"); let public_key = std::env::args().nth(2).expect("no public key given");
//send username and public key to server //send username and public key to server
@ -54,113 +41,42 @@ fn main() {
let server_message_recv = setup_server_message_recv(&stream).unwrap(); let server_message_recv = setup_server_message_recv(&stream).unwrap();
let mut client = Client { let mut client = Client {
player: Player { player: clichess::Player {
id: 0,
username: username,
public_key: public_key,
playing_status: PlayingStatus::WaitingInLobby,
role: UserRole::Spectator, role: UserRole::Spectator,
username,
public_key,
}, },
side: Color::White, side: Color::White,
running: running.clone(),
input_buffer: input_buffer.clone(), input_buffer: input_buffer.clone(),
server_message_recv, server_message_recv,
playing: false, opponent_name: Option::None,
}; };
//get id from server match prompt_user_for_role(&client, &mut stream) {
let player_id = fetch_message_from_server(&client).parse::<usize>().unwrap(); Some(role) => client.player.role = role.clone(),
println!("got id {} from server", player_id); None => return,
client.player.id = player_id; };
if client.player.role == UserRole::Black {
loop { client.side = Color::Black;
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.")
}
}
};
} }
} if client.player.role == UserRole::Spectator {
println!(
fn lobby_loop(client: &Client, stream: &mut UnixStream) { "Hello, {} !\n\r You're spectating !",
println!("Sending first refresh to server..."); client.player.username
clichess::write_to_stream(stream, String::from("refresh")).unwrap(); )
println!("Getting list of players from server..."); } else {
let players = fetch_players_from_server(client); println!(
print_players(client, &players); "Hello, {} !\n\r You're playing with the {} pieces",
while client.running.load(Ordering::SeqCst) { client.player.username,
let mut user_input = client.input_buffer.lock().unwrap(); client.player.role.to_string()
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<Player> {
let data = fetch_message_from_server(client);
let v: Vec<Player> = serde_json::from_str(&data).unwrap();
v
}
fn print_players(client: &Client, players: &Vec<Player>) {
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); let mut current_position = fetch_initial_chess_position(&client);
loop { loop {
client
.opponent_name
.clone()
.map(|name| println!("{} played", &name[1..(name.len() - 1)]));
println!( println!(
"{}", "{}",
clichess::board_representation(&current_position, client.side) clichess::board_representation(&current_position, client.side)
@ -171,24 +87,31 @@ fn playing_loop(client: &Client, stream: &mut UnixStream) -> PlayingLoopOutcome
} }
if clichess::is_player_turn(&client.player, current_position.turn()) { if clichess::is_player_turn(&client.player, current_position.turn()) {
//it's the user turn, taking user input //it's the user turn, taking user input
let input = read_user_input(client); let input = read_user_input(&client);
clichess::write_to_stream(stream, String::from(input.trim())).unwrap(); clichess::write_to_stream(&mut stream, String::from(input.trim())).unwrap();
if input.trim() == EXIT_MSG { if input.trim() == EXIT_MSG {
break; break;
} }
} }
//update position after playing. //update position after playing.
match get_current_position(client) { match get_current_position(&mut client) {
Ok(position) => current_position = position, Ok(position) => current_position = position,
Err(_) => { Err(_) => {
clichess::write_to_stream(stream, String::from(EXIT_MSG)).unwrap(); clichess::write_to_stream(&mut stream, String::from(EXIT_MSG)).unwrap();
break; break;
} }
}; };
} }
match current_position.outcome() { match current_position.outcome() {
None => PlayingLoopOutcome::PlayerLeft, None => println!("Bye"),
Some(outcome) => PlayingLoopOutcome::Outcome(outcome), 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.")
}
}
} }
} }
@ -205,6 +128,8 @@ fn setup_input_buffer() -> Arc<Mutex<String>> {
let mut user_input = buf2.lock().unwrap(); let mut user_input = buf2.lock().unwrap();
if user_input.is_empty() { if user_input.is_empty() {
*user_input = buffer; *user_input = buffer;
} else {
println!("It's not your turn !");
} }
} }
} }
@ -212,16 +137,6 @@ fn setup_input_buffer() -> Arc<Mutex<String>> {
buf buf
} }
fn setupctrlc() -> Arc<AtomicBool> {
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
})
.expect("Error setting Ctrl-C handler");
running
}
fn setup_server_message_recv(stream: &UnixStream) -> io::Result<Receiver<String>> { fn setup_server_message_recv(stream: &UnixStream) -> io::Result<Receiver<String>> {
let (sender, receiver) = channel(); let (sender, receiver) = channel();
let thread_stream = stream.try_clone()?; let thread_stream = stream.try_clone()?;
@ -243,8 +158,8 @@ fn read_user_input(client: &Client) -> String {
let mut user_input = client.input_buffer.lock().unwrap(); let mut user_input = client.input_buffer.lock().unwrap();
user_input.clear(); user_input.clear();
} }
let mut input = String::new(); let input;
while client.running.load(Ordering::SeqCst) { loop {
thread::sleep(time::Duration::from_millis(10)); thread::sleep(time::Duration::from_millis(10));
{ {
let user_input = client.input_buffer.lock().unwrap(); let user_input = client.input_buffer.lock().unwrap();
@ -256,26 +171,26 @@ fn read_user_input(client: &Client) -> String {
} }
} }
} }
if client.running.load(Ordering::SeqCst) { input
input
} else {
String::from(EXIT_MSG)
}
} }
//wait for next position from server, then return the current board. //wait for next position from server, then return the current board.
fn get_current_position(client: &Client) -> Result<Chess, RecvPositionError> { fn get_current_position(client: &mut Client) -> Result<Chess, RecvPositionError> {
let response = fetch_message_from_server(client); let response = fetch_message_from_server(client);
let game_info: GameInfo = serde_json::from_str(&response).unwrap();
if game_info.opponent_name != "" {
client.opponent_name = Some(game_info.opponent_name);
}
if response.is_empty() { if response.is_empty() {
Err(RecvPositionError::UserCanceledError) Err(RecvPositionError::UserCanceledError)
} else { } else {
Ok(parse_position(&response)) Ok(parse_position(&game_info.game_fen))
} }
} }
fn fetch_message_from_server(client: &Client) -> String { fn fetch_message_from_server(client: &Client) -> String {
let mut response = String::new(); let response;
while client.running.load(Ordering::SeqCst) { loop {
thread::sleep(time::Duration::from_millis(10)); thread::sleep(time::Duration::from_millis(10));
{ {
let server_response_recv = &client.server_message_recv; let server_response_recv = &client.server_message_recv;
@ -298,6 +213,74 @@ fn parse_position(string: &str) -> Chess {
position position
} }
fn prompt_user_for_role(client: &Client, stream: &mut UnixStream) -> Option<UserRole> {
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 { fn fetch_initial_chess_position(client: &Client) -> Chess {
parse_position(&fetch_message_from_server(client)) parse_position(&fetch_message_from_server(client))
} }
fn fetch_available_roles(client: &Client) -> Vec<UserRole> {
roles_from_str(&fetch_message_from_server(client)).unwrap()
}
fn roles_from_str(s: &str) -> Result<Vec<UserRole>, String> {
s.split(',').map(clichess::parse_to_role).collect()
}

View File

@ -1,26 +1,29 @@
use clichess; use clichess;
use clichess::{Player, RecvPositionError, UserRole, EXIT_MSG, PlayingStatus}; use clichess::{GameInfo, Player, RecvPositionError, UserRole, EXIT_MSG};
use serde_json::Value; use serde_json::Value;
use shakmaty::{fen, Chess, Color, Setup}; use shakmaty::{fen, Chess, Color, Setup};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::io; use std::io;
use std::os::unix::net::{UnixListener, UnixStream}; use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::mpsc::{channel, Receiver, TryRecvError}; use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Condvar, Mutex};
use std::thread; use std::thread;
use std::time;
struct Server { struct Server {
id: usize, id: usize,
chess_position: Arc<Mutex<Chess>>, chess_position: Arc<Mutex<Chess>>,
players: Arc<Mutex<HashMap<usize, (Player, Arc<Mutex<String>>)>>>, players: Arc<Mutex<HashMap<usize, (Player, Sender<GameInfo>, Arc<(Mutex<bool>, Condvar)>)>>>,
others_serv_msg_buf: Arc<Mutex<String>>, others_serv_msg_recv: Receiver<GameInfo>,
client_message_recv: Receiver<String>, client_message_recv: Receiver<String>,
cvar: Arc<(Mutex<bool>, Condvar)>,
opponent_name: Option<String>,
} }
fn main() { fn main() {
let version = env!("CARGO_PKG_VERSION");
println!("Running clichess version {}", version);
let chess = Arc::new(Mutex::new(Chess::default())); let chess = Arc::new(Mutex::new(Chess::default()));
let players = Arc::new(Mutex::new(HashMap::new())); let players = Arc::new(Mutex::new(HashMap::new()));
let mut counter = 0; let mut counter = 0;
@ -32,16 +35,21 @@ fn main() {
for stream in listener.incoming() { for stream in listener.incoming() {
match stream { match stream {
Ok(stream) => { Ok(stream) => {
let client_message_recv = setup_client_message_recv(&stream).unwrap(); let condvar_pair = Arc::new((Mutex::new(false), Condvar::new()));
let server = Server { let client_message_recv =
setup_client_message_recv(&stream, condvar_pair.clone()).unwrap();
let (others_serv_msg_sender, others_serv_msg_recv) = channel();
let mut server = Server {
id: counter, id: counter,
chess_position: chess.clone(), chess_position: chess.clone(),
players: players.clone(), players: players.clone(),
others_serv_msg_buf: Arc::new(Mutex::new(String::new())), others_serv_msg_recv,
client_message_recv, client_message_recv,
cvar: condvar_pair,
opponent_name: Option::None,
}; };
/* connection succeeded */ /* connection succeeded */
thread::spawn(move || handle_player(stream, server)); thread::spawn(move || handle_player(stream, &mut server, others_serv_msg_sender));
counter += 1; counter += 1;
} }
Err(err) => { Err(err) => {
@ -52,9 +60,13 @@ fn main() {
} }
} }
fn handle_player(mut stream: UnixStream, server: Server) { fn handle_player(
match initialize_client(&mut stream, &server) { mut stream: UnixStream,
Ok((player, player_turn)) => main_loop(&mut stream, &server, player, player_turn), server: &mut Server,
others_serv_msg_sender: Sender<GameInfo>,
) {
match initialize_client(&mut stream, &server, others_serv_msg_sender) {
Ok((player, player_turn)) => main_loop(&mut stream, server, player, player_turn),
Err(e) => println!("User id {} could not be initialized: {}", server.id, e), Err(e) => println!("User id {} could not be initialized: {}", server.id, e),
}; };
player_disconnected(&server); player_disconnected(&server);
@ -63,19 +75,11 @@ fn handle_player(mut stream: UnixStream, server: Server) {
fn initialize_client( fn initialize_client(
stream: &mut UnixStream, stream: &mut UnixStream,
server: &Server, server: &Server,
others_serv_msg_sender: Sender<GameInfo>,
) -> Result<(Player, Color), RecvPositionError> { ) -> Result<(Player, Color), RecvPositionError> {
//create player //create player
let player = create_player(server, stream)?; let player = create_player(server, stream, others_serv_msg_sender)?;
let player_turn: Color; 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 //send current position to the player
println!( println!(
"server {}, send current position to the player...", "server {}, send current position to the player...",
@ -90,39 +94,7 @@ fn initialize_client(
Ok((player, player_turn)) Ok((player, player_turn))
} }
fn lobby_loop(stream: &mut UnixStream, server: &Server, player: &Player) { fn main_loop(stream: &mut UnixStream, server: &mut Server, player: Player, mut player_turn: Color) {
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 { loop {
if clichess::is_player_turn(&player, player_turn) { if clichess::is_player_turn(&player, player_turn) {
//let go of the lock while waiting for user input. //let go of the lock while waiting for user input.
@ -146,66 +118,96 @@ fn main_loop(stream: &mut UnixStream, server: &Server, player: Player, mut playe
Ok(played_chess) => *chess = played_chess, Ok(played_chess) => *chess = played_chess,
Err(e) => println!("Error: {}", e), Err(e) => println!("Error: {}", e),
}; };
let chessfen = fen::fen(&*chess); let game_info_to_send = GameInfo {
for (id, (_, others_serv_msg_buf)) in players.iter() { game_fen: fen::fen(&*chess),
opponent_name: players
.get(&server.id)
.expect("current server is in the server list")
.0
.username
.clone(),
};
for (id, (_, others_serv_msg_sender, cvar_pair)) in players.iter() {
if server.id != *id { if server.id != *id {
others_serv_msg_buf.lock().unwrap().push_str(&chessfen); others_serv_msg_sender
.send(game_info_to_send.clone())
.unwrap();
let (lock, cvar) = &**cvar_pair;
let mut message_sent = lock.lock().unwrap();
*message_sent = true;
cvar.notify_one();
} }
} }
clichess::write_to_stream(stream, chessfen).unwrap(); if clichess::write_to_stream(
stream,
serde_json::to_string(&GameInfo {
game_fen: fen::fen(&*chess),
opponent_name: server.opponent_name.clone().unwrap_or_default(),
})
.unwrap(),
)
.is_err()
{
break;
}
player_turn = chess.turn(); player_turn = chess.turn();
} }
} else { } else {
let position_as_fen_result = wait_for_opponent_move(server); let game_info_result = wait_for_opponent_move(server);
if position_as_fen_result.is_err() { if game_info_result.is_err() {
break; break;
} }
let position_as_fen = position_as_fen_result.unwrap(); let game_info = game_info_result.unwrap();
println!("server id: {}, sending {}", server.id, position_as_fen); clichess::write_to_stream(stream, serde_json::to_string(&game_info).unwrap()).unwrap();
clichess::write_to_stream(stream, position_as_fen.clone()).unwrap(); server.opponent_name = Some(game_info.opponent_name);
let chess = server.chess_position.lock().unwrap(); let chess = server.chess_position.lock().unwrap();
player_turn = chess.turn(); player_turn = chess.turn();
} }
} }
} }
fn wait_for_opponent_move(server: &Server) -> Result<String, RecvPositionError> { fn wait_for_opponent_move(server: &Server) -> Result<GameInfo, RecvPositionError> {
println!("server id: {}, waiting for move to send...", server.id); println!("server id: {}, waiting for move to send...", server.id);
//wait: either we receive next position from other server threads, or we receive //wait: either we receive next position from other server threads, or we receive
//"exit" from the client. //"exit" from the client.
let returned_result: Result<String, RecvPositionError>; let mut returned_result = Err(RecvPositionError::UserCanceledError);
loop { let (lock, cvar) = &*server.cvar;
{ let mut message_sent = lock.lock().unwrap();
let mut others_serv_msg_buf = server.others_serv_msg_buf.lock().unwrap(); *message_sent = false;
if !others_serv_msg_buf.is_empty() { while !*message_sent {
returned_result = Ok(others_serv_msg_buf.clone()); message_sent = cvar.wait(message_sent).unwrap();
others_serv_msg_buf.clear(); }
break; *message_sent = false;
match server.others_serv_msg_recv.try_recv() {
Ok(msg) => {
returned_result = Ok(msg);
}
Err(TryRecvError::Disconnected) => {
println!("Error: other server thread disconnected while sending move.")
}
Err(TryRecvError::Empty) => { /*nothing to do*/ }
}
match server.client_message_recv.try_recv() {
Ok(msg) => {
if msg == EXIT_MSG {
returned_result = Err(RecvPositionError::UserCanceledError);
} else { } else {
match server.client_message_recv.try_recv() { println!("Client sent message while it's not its turn, this is an error.");
Ok(msg) => { println!("Here is the message: {}", msg);
if msg == EXIT_MSG { returned_result = Err(RecvPositionError::UserCanceledError);
returned_result = Err(RecvPositionError::UserCanceledError);
break;
} else {
println!(
"Client sent message while it's not its turn, this is an error."
);
println!("Here is the message: {}", msg);
continue;
}
}
Err(TryRecvError::Disconnected) => println!("Error: client disconnected."),
Err(TryRecvError::Empty) => continue,
}
} }
} }
thread::sleep(time::Duration::from_millis(10)); Err(TryRecvError::Disconnected) => println!("Error: client disconnected."),
Err(TryRecvError::Empty) => {}
} }
returned_result returned_result
} }
fn create_player(server: &Server, stream: &mut UnixStream) -> Result<Player, RecvPositionError> { fn create_player(
server: &Server,
stream: &mut UnixStream,
others_serv_msg_sender: Sender<GameInfo>,
) -> Result<Player, RecvPositionError> {
println!("Creating player {}...", server.id); println!("Creating player {}...", server.id);
//get player name and pubkey //get player name and pubkey
let username_pubkey_json = server let username_pubkey_json = server
@ -217,18 +219,16 @@ fn create_player(server: &Server, stream: &mut UnixStream) -> Result<Player, Rec
let public_key = username_pubkey_value["pubkey"].to_string(); let public_key = username_pubkey_value["pubkey"].to_string();
println!("got username: {}", username); println!("got username: {}", username);
println!("got pubkey: {}", public_key); println!("got pubkey: {}", public_key);
let role = UserRole::Spectator; let role = receive_user_role(server, stream)?;
let mut players = server.players.lock().unwrap(); let mut players = server.players.lock().unwrap();
let player = Player { let player = Player {
id: server.id, role,
username, username,
public_key, public_key,
playing_status: PlayingStatus::WaitingInLobby,
role
}; };
players.insert( players.insert(
server.id, server.id,
(player.clone(), server.others_serv_msg_buf.clone()), (player.clone(), others_serv_msg_sender, server.cvar.clone()),
); );
println!("Created player {}", server.id); println!("Created player {}", server.id);
Ok(player) Ok(player)
@ -264,7 +264,7 @@ fn receive_user_role(
let mut available_roles_after_choice = let mut available_roles_after_choice =
vec![UserRole::White, UserRole::Black, UserRole::Spectator]; vec![UserRole::White, UserRole::Black, UserRole::Spectator];
let players = server.players.lock().unwrap(); let players = server.players.lock().unwrap();
for (_, (player, _)) in players.iter() { for (_, (player, _, _)) in players.iter() {
match available_roles_after_choice match available_roles_after_choice
.iter() .iter()
.position(|r| *r == player.role) .position(|r| *r == player.role)
@ -298,7 +298,7 @@ fn receive_user_role(
fn compute_available_roles_to_str(server: &Server) -> String { fn compute_available_roles_to_str(server: &Server) -> String {
let mut available_roles = vec![UserRole::White, UserRole::Black, UserRole::Spectator]; let mut available_roles = vec![UserRole::White, UserRole::Black, UserRole::Spectator];
let players = server.players.lock().unwrap(); let players = server.players.lock().unwrap();
for (_, (player, _)) in players.iter() { for (_, (player, _, _)) in players.iter() {
match available_roles.iter().position(|r| *r == player.role) { match available_roles.iter().position(|r| *r == player.role) {
Some(index) => available_roles.remove(index), Some(index) => available_roles.remove(index),
None => continue, None => continue,
@ -315,13 +315,16 @@ fn player_disconnected(server: &Server) {
let mut players = server.players.lock().unwrap(); let mut players = server.players.lock().unwrap();
let player_and_receiving_buf = players.get(&server.id); let player_and_receiving_buf = players.get(&server.id);
if player_and_receiving_buf.is_some() { if player_and_receiving_buf.is_some() {
let (player, _) = player_and_receiving_buf.expect("is some"); let (player, _, _) = player_and_receiving_buf.expect("is some");
println!("Player {} disconnected.", player.username); println!("Player {} disconnected.", player.username);
} }
players.remove(&server.id); players.remove(&server.id);
} }
fn setup_client_message_recv(stream: &UnixStream) -> io::Result<Receiver<String>> { fn setup_client_message_recv(
stream: &UnixStream,
cvar_pair: Arc<(Mutex<bool>, Condvar)>,
) -> io::Result<Receiver<String>> {
let (sender, receiver) = channel(); let (sender, receiver) = channel();
let thread_stream = stream.try_clone()?; let thread_stream = stream.try_clone()?;
@ -330,6 +333,11 @@ fn setup_client_message_recv(stream: &UnixStream) -> io::Result<Receiver<String>
//wait for client message //wait for client message
let buffer = let buffer =
clichess::read_line_from_stream(&thread_stream).expect("Error message from server"); clichess::read_line_from_stream(&thread_stream).expect("Error message from server");
//notify the server if waiting
let (lock, cvar) = &*cvar_pair;
let mut message_sent = lock.lock().unwrap();
*message_sent = true;
cvar.notify_one();
let send_result = sender.send(buffer); let send_result = sender.send(buffer);
// stop the thread during a disconnection // stop the thread during a disconnection
if send_result.is_err() { if send_result.is_err() {

View File

@ -1,4 +1,5 @@
use colored::Colorize; use colored::Colorize;
use serde::{Deserialize, Serialize};
use shakmaty::san::ParseSanError; use shakmaty::san::ParseSanError;
use shakmaty::san::San; use shakmaty::san::San;
use shakmaty::san::SanError; use shakmaty::san::SanError;
@ -9,28 +10,24 @@ use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::{BufReader, Write}; use std::io::{BufReader, Write};
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use serde::{Deserialize, Serialize};
//message to send to the server to signal we disconnected. //message to send to the server to signal we disconnected.
pub const EXIT_MSG: &str = "exit"; pub const EXIT_MSG: &str = "exit";
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone)]
pub enum PlayingStatus {
WaitingInLobby,
Challenging(usize),
PlayingAgainst(usize)
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Player { pub struct Player {
pub id: usize, pub role: UserRole,
pub username: String, pub username: String,
pub public_key: String, pub public_key: String,
pub playing_status: PlayingStatus,
pub role: UserRole
} }
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] #[derive(Serialize, Deserialize, Clone)]
pub struct GameInfo {
pub game_fen: String,
pub opponent_name: String,
}
#[derive(PartialEq, Copy, Clone)]
pub enum UserRole { pub enum UserRole {
White, White,
Black, Black,