Can play against random player
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@ -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"] }
|
@ -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")]
|
||||
|
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user