WIP: lobby system

This commit is contained in:
Artlef 2020-05-03 18:31:18 +02:00
parent 38d9026865
commit ccd7ab749d
3 changed files with 178 additions and 111 deletions

View File

@ -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()
}

View File

@ -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,

View File

@ -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,