WIP: lobby system
This commit is contained in:
parent
38d9026865
commit
ccd7ab749d
@ -1,9 +1,12 @@
|
|||||||
extern crate ctrlc;
|
extern crate ctrlc;
|
||||||
use clichess::{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::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::mpsc::{channel, Receiver, TryRecvError};
|
use std::sync::mpsc::{channel, Receiver, TryRecvError};
|
||||||
@ -12,11 +15,21 @@ use std::thread;
|
|||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
struct Client {
|
struct Client {
|
||||||
player: clichess::Player,
|
player: Player,
|
||||||
side: Color,
|
side: Color,
|
||||||
running: Arc<AtomicBool>,
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LobbyLoopOutcome {
|
||||||
|
ClientLeft,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PlayingLoopOutcome {
|
||||||
|
PlayerLeft,
|
||||||
|
Outcome(Outcome),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -41,33 +54,111 @@ 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: clichess::Player {
|
player: Player {
|
||||||
role: UserRole::Spectator,
|
id: 0,
|
||||||
username: username,
|
username: username,
|
||||||
public_key: public_key,
|
public_key: public_key,
|
||||||
|
playing_status: PlayingStatus::WaitingInLobby,
|
||||||
|
role: UserRole::Spectator,
|
||||||
},
|
},
|
||||||
side: Color::White,
|
side: Color::White,
|
||||||
running: running.clone(),
|
running: running.clone(),
|
||||||
input_buffer: input_buffer.clone(),
|
input_buffer: input_buffer.clone(),
|
||||||
server_message_recv,
|
server_message_recv,
|
||||||
|
playing: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
match prompt_user_for_role(&client, &mut stream) {
|
//get id from server
|
||||||
Some(role) => client.player.role = role.clone(),
|
let player_id = fetch_message_from_server(&client).parse::<usize>().unwrap();
|
||||||
None => return,
|
println!("got id {} from server", player_id);
|
||||||
};
|
client.player.id = player_id;
|
||||||
if client.player.role == UserRole::Spectator {
|
|
||||||
println!(
|
loop {
|
||||||
"Hello, {} !\n\r You're spectating !",
|
lobby_loop(&client, &mut stream);
|
||||||
client.player.username
|
match playing_loop(&client, &mut stream) {
|
||||||
)
|
PlayingLoopOutcome::PlayerLeft => {
|
||||||
} else {
|
println!("Bye");
|
||||||
println!(
|
break;
|
||||||
"Hello, {} !\n\r You're playing with the {} pieces",
|
}
|
||||||
client.player.username,
|
PlayingLoopOutcome::Outcome(Outcome::Draw) => println!("Draw game."),
|
||||||
client.player.role.to_string()
|
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);
|
let mut current_position = fetch_initial_chess_position(&client);
|
||||||
loop {
|
loop {
|
||||||
println!(
|
println!(
|
||||||
@ -80,31 +171,24 @@ fn main() {
|
|||||||
}
|
}
|
||||||
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(&mut stream, String::from(input.trim())).unwrap();
|
clichess::write_to_stream(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(client) {
|
||||||
Ok(position) => current_position = position,
|
Ok(position) => current_position = position,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
clichess::write_to_stream(&mut stream, String::from(EXIT_MSG)).unwrap();
|
clichess::write_to_stream(stream, String::from(EXIT_MSG)).unwrap();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
match current_position.outcome() {
|
match current_position.outcome() {
|
||||||
None => println!("Bye"),
|
None => PlayingLoopOutcome::PlayerLeft,
|
||||||
Some(Outcome::Draw) => println!("Draw game."),
|
Some(outcome) => PlayingLoopOutcome::Outcome(outcome),
|
||||||
Some(Outcome::Decisive { winner }) => {
|
|
||||||
if winner == Color::White {
|
|
||||||
println!("White has won the game.")
|
|
||||||
} else {
|
|
||||||
println!("Black has won the game.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +205,6 @@ 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 !");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,74 +298,6 @@ 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,5 +1,5 @@
|
|||||||
use clichess;
|
use clichess;
|
||||||
use clichess::{Player, RecvPositionError, UserRole, EXIT_MSG};
|
use clichess::{Player, RecvPositionError, UserRole, EXIT_MSG, PlayingStatus};
|
||||||
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;
|
||||||
@ -67,6 +67,15 @@ fn initialize_client(
|
|||||||
//create player
|
//create player
|
||||||
let player = create_player(server, stream)?;
|
let player = create_player(server, stream)?;
|
||||||
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...",
|
||||||
@ -81,6 +90,38 @@ fn initialize_client(
|
|||||||
Ok((player, player_turn))
|
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) {
|
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) {
|
||||||
@ -176,12 +217,14 @@ 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 = receive_user_role(server, stream)?;
|
let role = UserRole::Spectator;
|
||||||
let mut players = server.players.lock().unwrap();
|
let mut players = server.players.lock().unwrap();
|
||||||
let player = Player {
|
let player = Player {
|
||||||
role,
|
id: server.id,
|
||||||
username,
|
username,
|
||||||
public_key,
|
public_key,
|
||||||
|
playing_status: PlayingStatus::WaitingInLobby,
|
||||||
|
role
|
||||||
};
|
};
|
||||||
players.insert(
|
players.insert(
|
||||||
server.id,
|
server.id,
|
||||||
|
22
src/lib.rs
22
src/lib.rs
@ -9,18 +9,28 @@ 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)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Player {
|
pub enum PlayingStatus {
|
||||||
pub role: UserRole,
|
WaitingInLobby,
|
||||||
pub username: String,
|
Challenging(usize),
|
||||||
pub public_key: String,
|
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 {
|
pub enum UserRole {
|
||||||
White,
|
White,
|
||||||
Black,
|
Black,
|
||||||
|
Loading…
Reference in New Issue
Block a user