Can play against random player
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2022-10-16 17:47:41 +02:00
parent 161391db04
commit 3c2b96f3a2
8 changed files with 351 additions and 15 deletions

View File

@ -18,7 +18,10 @@ num = "0.4.0"
num-traits = "0.2.15"
num-derive = "0.3.3"
textwrap = "0.15.1"
tokio-tungstenite = "0.17.2"
tokio-tungstenite = { version = "0.17.2", features = ["__rustls-tls", "rustls-tls-native-roots"] }
serde_urlencoded = "0.7.1"
futures = "0.3.23"
serde_json = "1.0.85"
hostname = "0.3.1"
rustls = "0.20.6"
hyper-rustls = { version = "0.23.0", features = ["rustls-native-certs"] }

View File

@ -13,15 +13,14 @@ pub enum TestDevScreen {
#[derive(Parser, Debug)]
pub struct CliArgs {
// TODO: switch default sever uri to real one when we get one
/// Upstream server to use
#[clap(
short,
long,
value_parser,
default_value = "https://fixme.communiquons.org"
default_value = "https://seabattleapi.communiquons.org"
)]
pub server_uri: String,
pub remote_server_uri: String,
/// Local server listen address
#[clap(short, long, default_value = "127.0.0.1:5679")]

View File

@ -2,12 +2,14 @@ use crate::cli_args::cli_args;
use crate::server;
use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt};
use hyper_rustls::ConfigBuilderExt;
use sea_battle_backend::data::GameRules;
use sea_battle_backend::human_player_ws::{ClientMessage, ServerMessage};
use sea_battle_backend::server::BotPlayQuery;
use sea_battle_backend::server::{BotPlayQuery, PlayRandomQuery};
use sea_battle_backend::utils::{boxed_error, Res};
use std::sync::mpsc;
use std::fmt::Display;
use std::sync::mpsc::TryRecvError;
use std::sync::{mpsc, Arc};
use tokio::net::TcpStream;
use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
@ -44,14 +46,41 @@ impl Client {
.await
}
/// Start to play against a random player
pub async fn start_random_play<D: Display>(player_name: D) -> Res<Self> {
Self::connect_url(
&cli_args().remote_server_uri,
&format!(
"/play/random?{}",
serde_urlencoded::to_string(&PlayRandomQuery {
player_name: player_name.to_string()
})
.unwrap()
),
)
.await
}
/// Do connect to a server, returning
async fn connect_url(server: &str, uri: &str) -> Res<Self> {
let mut url = server.replace("http", "ws");
url.push_str(uri);
log::debug!("Connecting to {}", url);
let mut ws_url = server.replace("http", "ws");
ws_url.push_str(uri);
log::debug!("Connecting to {}", ws_url);
let (socket, _) = if ws_url.starts_with("wss") {
// Perform a connection over TLS
let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_native_roots()
.with_no_client_auth();
let connector = tokio_tungstenite::Connector::Rustls(Arc::new(config));
tokio_tungstenite::connect_async_tls_with_config(ws_url, None, Some(connector)).await?
} else {
// Perform an unsecure connection
tokio_tungstenite::connect_async(ws_url).await?
};
// Connect to websocket & split streams
let (socket, _) = tokio_tungstenite::connect_async(url).await?;
let (sink, mut stream) = socket.split();
// Receive server message on a separate task

View File

@ -17,11 +17,13 @@ use cli_player::client::Client;
use cli_player::server::start_server_if_missing;
use cli_player::ui_screens::configure_game_rules::GameRulesConfigurationScreen;
use cli_player::ui_screens::game_screen::GameScreen;
use cli_player::ui_screens::input_screen::InputScreen;
use cli_player::ui_screens::popup_screen::PopupScreen;
use cli_player::ui_screens::select_play_mode_screen::{SelectPlayModeResult, SelectPlayModeScreen};
use cli_player::ui_screens::*;
use sea_battle_backend::data::GameRules;
use sea_battle_backend::human_player_ws::ServerMessage;
use sea_battle_backend::utils::Res;
/// Test code screens
async fn run_dev<B: Backend>(
@ -70,17 +72,53 @@ async fn run_dev<B: Backend>(
))?
}
async fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> Result<(), Box<dyn Error>> {
/// Ask the user to specify its username
fn query_username<B: Backend>(terminal: &mut Terminal<B>) -> Res<String> {
let hostname = hostname::get()?.to_string_lossy().to_string();
let res =
InputScreen::new("Please specify the name to which other players should identify you:")
.set_title("Player name")
.set_value(&hostname)
.show(terminal)?;
Ok(res.value().unwrap_or(hostname))
}
async fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> Res {
if let Some(d) = cli_args().dev_screen {
return run_dev(terminal, d).await;
}
let mut rules = GameRules::default();
let mut username = "".to_string();
loop {
match SelectPlayModeScreen::default().show(terminal)? {
// TODO : Play against random player
ScreenResult::Ok(SelectPlayModeResult::PlayRandom) => todo!(),
let choice = SelectPlayModeScreen::default().show(terminal)?;
if let ScreenResult::Ok(c) = choice {
if c.need_user_name() && username.is_empty() {
username = query_username(terminal)?;
}
}
match choice {
ScreenResult::Ok(SelectPlayModeResult::PlayRandom) => {
PopupScreen::new("Connecting...").show_once(terminal)?;
let client = Client::start_random_play(&username).await?;
PopupScreen::new("Waiting for opponent...").show_once(terminal)?;
// Wait for the server to become ready
while !matches!(
client.recv_next_message().await?,
ServerMessage::OpponentConnected
) {}
// Display game screen
GameScreen::new(client).show(terminal).await?;
}
// Play against bot
ScreenResult::Ok(SelectPlayModeResult::PlayAgainstBot) => {

View File

@ -1,3 +1,4 @@
use std::fmt::Display;
use std::io;
use std::time::{Duration, Instant};
use tui::style::*;
@ -49,6 +50,11 @@ impl<'a> InputScreen<'a> {
self
}
pub fn set_value<D: Display>(mut self, value: D) -> Self {
self.value = value.to_string();
self
}
/// Get error contained in input
fn error(&self) -> Option<&'static str> {
if self.value.len() > self.max_len {

View File

@ -17,6 +17,13 @@ pub enum ScreenResult<E = ()> {
}
impl<E: Debug> ScreenResult<E> {
pub fn value(self) -> Option<E> {
match self {
ScreenResult::Ok(v) => Some(v),
ScreenResult::Canceled => None,
}
}
pub fn as_string(&self) -> String {
format!("{:#?}", self)
}

View File

@ -20,6 +20,13 @@ pub enum SelectPlayModeResult {
Exit,
}
impl SelectPlayModeResult {
/// Specify whether a selected play mode requires a user name or not
pub fn need_user_name(&self) -> bool {
self != &SelectPlayModeResult::PlayAgainstBot && self != &SelectPlayModeResult::Exit
}
}
#[derive(Debug, Clone)]
struct PlayModeDescription {
name: &'static str,