Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
1fb3194786 | |||
ee7d52f401 | |||
c692be6159 | |||
2c4b339bc2 | |||
56699033d9 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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)",
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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(¤t_position, client.side)
|
clichess::board_representation(¤t_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()
|
||||||
|
}
|
||||||
|
@ -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() {
|
||||||
|
23
src/lib.rs
23
src/lib.rs
@ -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,
|
||||||
|
Reference in New Issue
Block a user