use std::error::Error; use std::io; use std::io::ErrorKind; use crossterm::event::DisableMouseCapture; use crossterm::event::EnableMouseCapture; use crossterm::execute; use crossterm::terminal::EnterAlternateScreen; use crossterm::terminal::LeaveAlternateScreen; use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use env_logger::Env; use tui::backend::{Backend, CrosstermBackend}; use tui::Terminal; use sea_battle_backend::consts::{ INVITE_CODE_LENGTH, MAX_PLAYER_NAME_LENGTH, MIN_PLAYER_NAME_LENGTH, }; use sea_battle_backend::data::GameRules; use sea_battle_backend::utils::res_utils::Res; use sea_battle_cli_player::cli_args::{cli_args, TestDevScreen}; use sea_battle_cli_player::client::Client; use sea_battle_cli_player::server::run_server; use sea_battle_cli_player::ui_screens::configure_game_rules::GameRulesConfigurationScreen; use sea_battle_cli_player::ui_screens::game_screen::GameScreen; use sea_battle_cli_player::ui_screens::input_screen::InputScreen; use sea_battle_cli_player::ui_screens::popup_screen::PopupScreen; use sea_battle_cli_player::ui_screens::select_play_mode_screen::{ SelectPlayModeResult, SelectPlayModeScreen, }; use sea_battle_cli_player::ui_screens::*; /// Test code screens async fn run_dev( terminal: &mut Terminal, d: TestDevScreen, ) -> Result<(), Box> { let res = match d { TestDevScreen::Popup => popup_screen::PopupScreen::new("Welcome there!!") .show(terminal)? .as_string(), TestDevScreen::Input => input_screen::InputScreen::new("What it your name ?") .set_title("A custom title") .show(terminal)? .as_string(), TestDevScreen::Confirm => { confirm_dialog_screen::ConfirmDialogScreen::new("Do you really want to quit game?") .show(terminal)? .as_string() } TestDevScreen::SelectBotType => select_bot_type_screen::SelectBotTypeScreen::default() .show(terminal)? .as_string(), TestDevScreen::SelectPlayMode => select_play_mode_screen::SelectPlayModeScreen::default() .show(terminal)? .as_string(), TestDevScreen::SetBoatsLayout => { let rules = GameRules { boats_can_touch: true, ..Default::default() }; set_boats_layout_screen::SetBoatsLayoutScreen::new(&rules) .show(terminal)? .as_string() } TestDevScreen::ConfigureGameRules => { configure_game_rules::GameRulesConfigurationScreen::new(GameRules::default()) .show(terminal)? .as_string() } }; Err(io::Error::new( ErrorKind::Other, format!("DEV result: {:?}", res), ))? } /// Ask the user to specify the name he should be identified with fn query_player_name(terminal: &mut Terminal) -> Res { let mut hostname = hostname::get()?.to_string_lossy().to_string(); if hostname.len() > MAX_PLAYER_NAME_LENGTH { hostname = hostname[0..MAX_PLAYER_NAME_LENGTH].to_string(); } let res = InputScreen::new("Please specify the name to which other players should identify you:") .set_title("Player name") .set_value(&hostname) .set_min_length(MIN_PLAYER_NAME_LENGTH) .set_max_length(MAX_PLAYER_NAME_LENGTH) .show(terminal)?; Ok(res.value().unwrap_or(hostname)) } async fn run_app(terminal: &mut Terminal) -> 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 { let choice = SelectPlayModeScreen::default().show(terminal)?; if let ScreenResult::Ok(c) = choice { if c.need_player_name() && username.is_empty() { username = query_player_name(terminal)?; } if c.need_custom_rules() { rules = match GameRulesConfigurationScreen::new(rules.clone()).show(terminal)? { ScreenResult::Ok(r) => r, ScreenResult::Canceled => continue, } } } PopupScreen::new("🔌 Connecting...").show_once(terminal)?; let client = match choice { ScreenResult::Ok(SelectPlayModeResult::PlayRandom) => { Client::start_random_play(&username).await? } // Play against bot ScreenResult::Ok(SelectPlayModeResult::PlayAgainstBot) => { Client::start_bot_play(&rules).await? } // Create invite ScreenResult::Ok(SelectPlayModeResult::CreateInvite) => { Client::start_create_invite(&rules, &username).await? } // Join invite ScreenResult::Ok(SelectPlayModeResult::AcceptInvite) => { let code = match InputScreen::new("Invite code") .set_min_length(INVITE_CODE_LENGTH) .set_max_length(INVITE_CODE_LENGTH) .show(terminal)? .value() { None => continue, Some(v) => v, }; PopupScreen::new("🔌 Connecting...").show_once(terminal)?; Client::start_accept_invite(code, &username).await? } ScreenResult::Canceled | ScreenResult::Ok(SelectPlayModeResult::Exit) => return Ok(()), }; // Display game screen GameScreen::new(client).show(terminal).await?; } } #[tokio::main] pub async fn main() -> Result<(), Box> { env_logger::Builder::from_env(Env::default()).init(); if cli_args().serve { run_server().await; return Ok(()); } // setup terminal enable_raw_mode()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; // create app and run it let res = run_app(&mut terminal).await; // restore terminal disable_raw_mode()?; execute!( terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture )?; terminal.show_cursor()?; if let Err(err) = res { println!("{:?}", err) } Ok(()) }