2022-10-01 17:25:41 +00:00
use std ::error ::Error ;
use std ::io ;
2022-10-02 14:21:08 +00:00
use std ::io ::ErrorKind ;
2022-10-01 17:25:41 +00:00
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 ;
2022-10-01 18:49:05 +00:00
use tui ::backend ::{ Backend , CrosstermBackend } ;
2022-10-01 17:25:41 +00:00
use tui ::Terminal ;
2022-10-01 18:49:05 +00:00
2022-10-17 16:47:33 +00:00
use sea_battle_backend ::consts ::{
INVITE_CODE_LENGTH , MAX_PLAYER_NAME_LENGTH , MIN_PLAYER_NAME_LENGTH ,
} ;
2022-10-02 17:17:29 +00:00
use sea_battle_backend ::data ::GameRules ;
2022-10-17 06:24:40 +00:00
use sea_battle_backend ::utils ::res_utils ::Res ;
2022-10-17 17:13:16 +00:00
use sea_battle_cli_player ::cli_args ::{ cli_args , TestDevScreen } ;
2022-10-18 06:58:36 +00:00
use sea_battle_cli_player ::client ::{ Client , GetRemoteVersionError } ;
2022-10-17 17:13:16 +00:00
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 ::* ;
2022-10-01 18:49:05 +00:00
2022-10-10 15:51:51 +00:00
/// Test code screens
2022-10-10 15:17:57 +00:00
async fn run_dev < B : Backend > (
terminal : & mut Terminal < B > ,
d : TestDevScreen ,
) -> Result < ( ) , Box < dyn Error > > {
let res = match d {
2022-10-18 06:58:36 +00:00
TestDevScreen ::Popup = > PopupScreen ::new ( " Welcome there!! " )
2022-10-10 15:17:57 +00:00
. show ( terminal ) ?
. as_string ( ) ,
2022-10-18 06:58:36 +00:00
TestDevScreen ::Input = > InputScreen ::new ( " What it your name ? " )
2022-10-10 15:17:57 +00:00
. set_title ( " A custom title " )
. show ( terminal ) ?
. as_string ( ) ,
2022-10-10 15:51:51 +00:00
TestDevScreen ::Confirm = > {
2022-10-15 13:53:27 +00:00
confirm_dialog_screen ::ConfirmDialogScreen ::new ( " Do you really want to quit game? " )
2022-10-10 15:51:51 +00:00
. show ( terminal ) ?
. as_string ( )
}
2022-10-15 13:53:27 +00:00
TestDevScreen ::SelectBotType = > select_bot_type_screen ::SelectBotTypeScreen ::default ( )
2022-10-10 15:51:51 +00:00
. show ( terminal ) ?
. as_string ( ) ,
2022-10-18 06:58:36 +00:00
TestDevScreen ::SelectPlayMode = > {
SelectPlayModeScreen ::default ( ) . show ( terminal ) ? . as_string ( )
}
2022-10-10 15:51:51 +00:00
TestDevScreen ::SetBoatsLayout = > {
let rules = GameRules {
boats_can_touch : true ,
.. Default ::default ( )
} ;
2022-10-15 13:53:27 +00:00
set_boats_layout_screen ::SetBoatsLayoutScreen ::new ( & rules )
2022-10-10 15:51:51 +00:00
. show ( terminal ) ?
. as_string ( )
}
TestDevScreen ::ConfigureGameRules = > {
2022-10-18 06:58:36 +00:00
GameRulesConfigurationScreen ::new ( GameRules ::default ( ) )
2022-10-10 15:51:51 +00:00
. show ( terminal ) ?
. as_string ( )
}
2022-10-10 15:17:57 +00:00
} ;
2022-10-02 16:00:45 +00:00
Err ( io ::Error ::new (
2022-10-02 14:21:08 +00:00
ErrorKind ::Other ,
2022-10-10 15:17:57 +00:00
format! ( " DEV result: {:?} " , res ) ,
) ) ?
}
2022-10-17 16:47:33 +00:00
/// Ask the user to specify the name he should be identified with
fn query_player_name < B : Backend > ( terminal : & mut Terminal < B > ) -> Res < String > {
2022-10-16 16:51:05 +00:00
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 ( ) ;
}
2022-10-16 15:47:41 +00:00
let res =
InputScreen ::new ( " Please specify the name to which other players should identify you: " )
. set_title ( " Player name " )
. set_value ( & hostname )
2022-10-16 16:51:05 +00:00
. set_min_length ( MIN_PLAYER_NAME_LENGTH )
. set_max_length ( MAX_PLAYER_NAME_LENGTH )
2022-10-16 15:47:41 +00:00
. show ( terminal ) ? ;
Ok ( res . value ( ) . unwrap_or ( hostname ) )
}
async fn run_app < B : Backend > ( terminal : & mut Terminal < B > ) -> Res {
2022-10-10 15:17:57 +00:00
if let Some ( d ) = cli_args ( ) . dev_screen {
2022-10-15 09:45:45 +00:00
return run_dev ( terminal , d ) . await ;
}
2022-10-18 06:58:36 +00:00
let mut checked_online_compatibility = false ;
2022-10-15 09:45:45 +00:00
let mut rules = GameRules ::default ( ) ;
2022-10-16 15:47:41 +00:00
let mut username = " " . to_string ( ) ;
2022-10-15 09:45:45 +00:00
loop {
2022-10-18 07:13:59 +00:00
terminal . clear ( ) ? ;
2022-10-16 15:47:41 +00:00
let choice = SelectPlayModeScreen ::default ( ) . show ( terminal ) ? ;
if let ScreenResult ::Ok ( c ) = choice {
2022-10-18 06:58:36 +00:00
// Check compatibility
if c . is_online_play_mode ( ) & & ! checked_online_compatibility {
PopupScreen ::new ( " 🖥 Checking remote server version... " ) . show_once ( terminal ) ? ;
let valid_version = match Client ::get_server_version ( ) . await {
Ok ( v ) = > v . is_compatible_with_static_version ( ) . unwrap_or ( false ) ,
Err ( GetRemoteVersionError ::ConnectionFailed ) = > {
PopupScreen ::new ( " ❌ Could not connect to remote server! " )
. show ( terminal ) ? ;
continue ;
}
Err ( GetRemoteVersionError ::Other ( e ) ) = > {
log ::error! ( " Could not load remote server information! {:?} " , e ) ;
false
}
} ;
if ! valid_version {
PopupScreen ::new ( " ❌ Unfortunately, it seems that your version of Sea Battle Cli player is too old to be used online... \n \n Please update it before trying to play online... " ) . show ( terminal ) ? ;
continue ;
}
checked_online_compatibility = true ;
}
2022-10-17 16:47:33 +00:00
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 ,
}
2022-10-16 15:47:41 +00:00
}
}
2022-10-17 16:47:33 +00:00
PopupScreen ::new ( " 🔌 Connecting... " ) . show_once ( terminal ) ? ;
2022-10-16 16:06:31 +00:00
let client = match choice {
2022-10-16 15:47:41 +00:00
ScreenResult ::Ok ( SelectPlayModeResult ::PlayRandom ) = > {
2022-10-18 07:04:25 +00:00
Client ::start_random_play ( & username ) . await
2022-10-16 15:47:41 +00:00
}
2022-10-15 09:45:45 +00:00
// Play against bot
ScreenResult ::Ok ( SelectPlayModeResult ::PlayAgainstBot ) = > {
2022-10-18 07:04:25 +00:00
Client ::start_bot_play ( & rules ) . await
2022-10-17 16:47:33 +00:00
}
// Create invite
ScreenResult ::Ok ( SelectPlayModeResult ::CreateInvite ) = > {
2022-10-18 07:04:25 +00:00
Client ::start_create_invite ( & rules , & username ) . await
2022-10-17 16:47:33 +00:00
}
// 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 ,
2022-10-15 09:45:45 +00:00
} ;
2022-10-17 16:47:33 +00:00
PopupScreen ::new ( " 🔌 Connecting... " ) . show_once ( terminal ) ? ;
2022-10-18 07:04:25 +00:00
Client ::start_accept_invite ( code , & username ) . await
2022-10-15 09:45:45 +00:00
}
ScreenResult ::Canceled | ScreenResult ::Ok ( SelectPlayModeResult ::Exit ) = > return Ok ( ( ) ) ,
2022-10-16 16:06:31 +00:00
} ;
2022-10-18 07:04:25 +00:00
match client {
Ok ( client ) = > {
// Display game screen
GameScreen ::new ( client ) . show ( terminal ) . await ? ;
}
Err ( e ) = > {
log ::error! ( " Failed to connect to server: {} " , e ) ;
PopupScreen ::new ( " ❌ Failed to connect to server! " ) . show ( terminal ) ? ;
}
} ;
2022-10-10 15:17:57 +00:00
}
2022-10-01 18:49:05 +00:00
}
2022-10-01 17:25:41 +00:00
2022-10-01 18:44:01 +00:00
#[ tokio::main ]
pub async fn main ( ) -> Result < ( ) , Box < dyn Error > > {
env_logger ::Builder ::from_env ( Env ::default ( ) ) . init ( ) ;
2022-10-16 17:52:34 +00:00
if cli_args ( ) . serve {
run_server ( ) . await ;
return Ok ( ( ) ) ;
}
2022-10-01 18:44:01 +00:00
2022-10-01 17:25:41 +00:00
// 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
2022-10-01 18:49:05 +00:00
let res = run_app ( & mut terminal ) . await ;
2022-10-01 17:25:41 +00:00
// restore terminal
disable_raw_mode ( ) ? ;
execute! (
terminal . backend_mut ( ) ,
LeaveAlternateScreen ,
DisableMouseCapture
) ? ;
terminal . show_cursor ( ) ? ;
2022-10-01 18:49:05 +00:00
if let Err ( err ) = res {
2022-10-01 17:25:41 +00:00
println! ( " {:?} " , err )
}
Ok ( ( ) )
}