Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
ccd7ab749d |
@ -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<AtomicBool>,
|
||||
input_buffer: Arc<Mutex<String>>,
|
||||
server_message_recv: Receiver<String>,
|
||||
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::<usize>().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<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);
|
||||
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<Mutex<String>> {
|
||||
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<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 {
|
||||
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,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<Player, Rec
|
||||
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 = UserRole::Spectator;
|
||||
let mut players = server.players.lock().unwrap();
|
||||
let player = Player {
|
||||
role,
|
||||
id: server.id,
|
||||
username,
|
||||
public_key,
|
||||
playing_status: PlayingStatus::WaitingInLobby,
|
||||
role
|
||||
};
|
||||
players.insert(
|
||||
server.id,
|
||||
|
22
src/lib.rs
22
src/lib.rs
@ -9,18 +9,28 @@ use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{BufReader, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
//message to send to the server to signal we disconnected.
|
||||
pub const EXIT_MSG: &str = "exit";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Player {
|
||||
pub role: UserRole,
|
||||
pub username: String,
|
||||
pub public_key: String,
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum PlayingStatus {
|
||||
WaitingInLobby,
|
||||
Challenging(usize),
|
||||
PlayingAgainst(usize)
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Player {
|
||||
pub id: usize,
|
||||
pub username: String,
|
||||
pub public_key: String,
|
||||
pub playing_status: PlayingStatus,
|
||||
pub role: UserRole
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum UserRole {
|
||||
White,
|
||||
Black,
|
||||
|
Loading…
Reference in New Issue
Block a user