Can play using invites
This commit is contained in:
		@@ -5,7 +5,9 @@ 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, PlayRandomQuery};
 | 
			
		||||
use sea_battle_backend::server::{
 | 
			
		||||
    AcceptInviteQuery, BotPlayQuery, CreateInviteQuery, PlayRandomQuery,
 | 
			
		||||
};
 | 
			
		||||
use sea_battle_backend::utils::res_utils::{boxed_error, Res};
 | 
			
		||||
use std::fmt::Display;
 | 
			
		||||
use std::sync::mpsc::TryRecvError;
 | 
			
		||||
@@ -61,6 +63,38 @@ impl Client {
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Start a play by creating an invite
 | 
			
		||||
    pub async fn start_create_invite<D: Display>(rules: &GameRules, player_name: D) -> Res<Self> {
 | 
			
		||||
        Self::connect_url(
 | 
			
		||||
            &cli_args().remote_server_uri,
 | 
			
		||||
            &format!(
 | 
			
		||||
                "/play/create_invite?{}",
 | 
			
		||||
                serde_urlencoded::to_string(&CreateInviteQuery {
 | 
			
		||||
                    rules: rules.clone(),
 | 
			
		||||
                    player_name: player_name.to_string()
 | 
			
		||||
                })
 | 
			
		||||
                .unwrap()
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Start a play by accepting an invite
 | 
			
		||||
    pub async fn start_accept_invite<D: Display>(code: String, player_name: D) -> Res<Self> {
 | 
			
		||||
        Self::connect_url(
 | 
			
		||||
            &cli_args().remote_server_uri,
 | 
			
		||||
            &format!(
 | 
			
		||||
                "/play/accept_invite?{}",
 | 
			
		||||
                serde_urlencoded::to_string(&AcceptInviteQuery {
 | 
			
		||||
                    code,
 | 
			
		||||
                    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 ws_url = server.replace("http", "ws");
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,9 @@ 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::consts::{MAX_PLAYER_NAME_LENGTH, MIN_PLAYER_NAME_LENGTH};
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
@@ -72,8 +74,8 @@ async fn run_dev<B: Backend>(
 | 
			
		||||
    ))?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Ask the user to specify its username
 | 
			
		||||
fn query_username<B: Backend>(terminal: &mut Terminal<B>) -> Res<String> {
 | 
			
		||||
/// Ask the user to specify the name he should be identified with
 | 
			
		||||
fn query_player_name<B: Backend>(terminal: &mut Terminal<B>) -> Res<String> {
 | 
			
		||||
    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();
 | 
			
		||||
@@ -103,29 +105,49 @@ async fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> Res {
 | 
			
		||||
        let choice = SelectPlayModeScreen::default().show(terminal)?;
 | 
			
		||||
 | 
			
		||||
        if let ScreenResult::Ok(c) = choice {
 | 
			
		||||
            if c.need_user_name() && username.is_empty() {
 | 
			
		||||
                username = query_username(terminal)?;
 | 
			
		||||
            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) => {
 | 
			
		||||
                PopupScreen::new("Connecting...").show_once(terminal)?;
 | 
			
		||||
 | 
			
		||||
                Client::start_random_play(&username).await?
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Play against bot
 | 
			
		||||
            ScreenResult::Ok(SelectPlayModeResult::PlayAgainstBot) => {
 | 
			
		||||
                // First, ask for custom rules
 | 
			
		||||
                rules = match GameRulesConfigurationScreen::new(rules.clone()).show(terminal)? {
 | 
			
		||||
                    ScreenResult::Ok(r) => r,
 | 
			
		||||
                    ScreenResult::Canceled => continue,
 | 
			
		||||
                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,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                // Then connect to server
 | 
			
		||||
                PopupScreen::new("Connecting...").show_once(terminal)?;
 | 
			
		||||
                Client::start_bot_play(&rules).await?
 | 
			
		||||
                PopupScreen::new("🔌 Connecting...").show_once(terminal)?;
 | 
			
		||||
                Client::start_accept_invite(code, &username).await?
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ScreenResult::Canceled | ScreenResult::Ok(SelectPlayModeResult::Exit) => return Ok(()),
 | 
			
		||||
 
 | 
			
		||||
@@ -53,21 +53,21 @@ impl GameStatus {
 | 
			
		||||
 | 
			
		||||
    pub fn status_text(&self) -> &str {
 | 
			
		||||
        match self {
 | 
			
		||||
            GameStatus::Connecting => "Connecting...",
 | 
			
		||||
            GameStatus::WaitingForAnotherPlayer => "Waiting for another player...",
 | 
			
		||||
            GameStatus::OpponentConnected => "Opponent connected!",
 | 
			
		||||
            GameStatus::WaitingForOpponentBoatsConfig => "Waiting for ### boats configuration",
 | 
			
		||||
            GameStatus::OpponentReady => "### is ready!",
 | 
			
		||||
            GameStatus::Starting => "Game is starting...",
 | 
			
		||||
            GameStatus::MustFire => "You must fire!",
 | 
			
		||||
            GameStatus::OpponentMustFire => "### must fire!",
 | 
			
		||||
            GameStatus::WonGame => "You win the game!",
 | 
			
		||||
            GameStatus::LostGame => "### wins the game. You loose.",
 | 
			
		||||
            GameStatus::RematchRequestedByOpponent => "Rematch requested by ###",
 | 
			
		||||
            GameStatus::RematchRequestedByPlayer => "Rematch requested by you",
 | 
			
		||||
            GameStatus::RematchAccepted => "Rematch accepted!",
 | 
			
		||||
            GameStatus::RematchRejected => "Rematch rejected!",
 | 
			
		||||
            GameStatus::OpponentLeftGame => "Opponent left game!",
 | 
			
		||||
            GameStatus::Connecting => "🔌 Connecting...",
 | 
			
		||||
            GameStatus::WaitingForAnotherPlayer => "🕑 Waiting for another player...",
 | 
			
		||||
            GameStatus::OpponentConnected => "✅ Opponent connected!",
 | 
			
		||||
            GameStatus::WaitingForOpponentBoatsConfig => "🕑 Waiting for ### boats configuration",
 | 
			
		||||
            GameStatus::OpponentReady => "✅ ### is ready!",
 | 
			
		||||
            GameStatus::Starting => "🕑 Game is starting...",
 | 
			
		||||
            GameStatus::MustFire => "🚨 You must fire!",
 | 
			
		||||
            GameStatus::OpponentMustFire => "💣 ### must fire!",
 | 
			
		||||
            GameStatus::WonGame => "🎉 You win the game!",
 | 
			
		||||
            GameStatus::LostGame => "😿 ### wins the game. You loose.",
 | 
			
		||||
            GameStatus::RematchRequestedByOpponent => "❓ Rematch requested by ###",
 | 
			
		||||
            GameStatus::RematchRequestedByPlayer => "❓ Rematch requested by you",
 | 
			
		||||
            GameStatus::RematchAccepted => "✅ Rematch accepted!",
 | 
			
		||||
            GameStatus::RematchRejected => "❌ Rematch rejected!",
 | 
			
		||||
            GameStatus::OpponentLeftGame => "⛔ Opponent left game!",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -83,10 +83,10 @@ enum Buttons {
 | 
			
		||||
impl Buttons {
 | 
			
		||||
    pub fn text(&self) -> &str {
 | 
			
		||||
        match self {
 | 
			
		||||
            Buttons::RequestRematch => "Request rematch",
 | 
			
		||||
            Buttons::AcceptRematch => "Accept rematch",
 | 
			
		||||
            Buttons::RejectRematch => "Reject rematch",
 | 
			
		||||
            Buttons::QuitGame => "Quit game",
 | 
			
		||||
            Buttons::RequestRematch => "❓ Request rematch",
 | 
			
		||||
            Buttons::AcceptRematch => "✅ Accept rematch",
 | 
			
		||||
            Buttons::RejectRematch => "❌ Reject rematch",
 | 
			
		||||
            Buttons::QuitGame => "❌ Quit game",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -241,7 +241,7 @@ impl GameScreen {
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    ServerMessage::InvalidInviteCode => {
 | 
			
		||||
                        PopupScreen::new("Invalid invite code!").show(terminal)?;
 | 
			
		||||
                        PopupScreen::new("❌ Invalid invite code!").show(terminal)?;
 | 
			
		||||
                        return Ok(ScreenResult::Ok(()));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@@ -475,7 +475,7 @@ impl GameScreen {
 | 
			
		||||
        if !self.status.can_show_game_maps() {
 | 
			
		||||
            if self.status == GameStatus::WaitingForAnotherPlayer {
 | 
			
		||||
                if let Some(code) = &self.invite_code {
 | 
			
		||||
                    status_text.push_str(&format!("\n Invite code: {}", code));
 | 
			
		||||
                    status_text.push_str(&format!("\n\n🎫 Invite code: {}", code));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,14 +17,22 @@ pub enum SelectPlayModeResult {
 | 
			
		||||
    #[default]
 | 
			
		||||
    PlayAgainstBot,
 | 
			
		||||
    PlayRandom,
 | 
			
		||||
    CreateInvite,
 | 
			
		||||
    AcceptInvite,
 | 
			
		||||
    Exit,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SelectPlayModeResult {
 | 
			
		||||
    /// Specify whether a selected play mode requires a user name or not
 | 
			
		||||
    pub fn need_user_name(&self) -> bool {
 | 
			
		||||
    pub fn need_player_name(&self) -> bool {
 | 
			
		||||
        self != &SelectPlayModeResult::PlayAgainstBot && self != &SelectPlayModeResult::Exit
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Specify whether a selected play mode requires a the user to specify its own game rules or
 | 
			
		||||
    /// not
 | 
			
		||||
    pub fn need_custom_rules(&self) -> bool {
 | 
			
		||||
        self == &SelectPlayModeResult::PlayAgainstBot || self == &SelectPlayModeResult::CreateInvite
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
@@ -33,17 +41,25 @@ struct PlayModeDescription {
 | 
			
		||||
    value: SelectPlayModeResult,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AVAILABLE_PLAY_MODES: [PlayModeDescription; 3] = [
 | 
			
		||||
const AVAILABLE_PLAY_MODES: [PlayModeDescription; 5] = [
 | 
			
		||||
    PlayModeDescription {
 | 
			
		||||
        name: "Play against bot (offline)",
 | 
			
		||||
        name: "🤖 Play against bot (offline)",
 | 
			
		||||
        value: SelectPlayModeResult::PlayAgainstBot,
 | 
			
		||||
    },
 | 
			
		||||
    PlayModeDescription {
 | 
			
		||||
        name: "Play against random player (online)",
 | 
			
		||||
        name: "🎲 Play against random player (online)",
 | 
			
		||||
        value: SelectPlayModeResult::PlayRandom,
 | 
			
		||||
    },
 | 
			
		||||
    PlayModeDescription {
 | 
			
		||||
        name: "Exit app",
 | 
			
		||||
        name: "➕ Create play invite (online)",
 | 
			
		||||
        value: SelectPlayModeResult::CreateInvite,
 | 
			
		||||
    },
 | 
			
		||||
    PlayModeDescription {
 | 
			
		||||
        name: "🎫 Accept play invite (online)",
 | 
			
		||||
        value: SelectPlayModeResult::AcceptInvite,
 | 
			
		||||
    },
 | 
			
		||||
    PlayModeDescription {
 | 
			
		||||
        name: "❌ Exit app",
 | 
			
		||||
        value: SelectPlayModeResult::Exit,
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
@@ -92,7 +108,7 @@ impl SelectPlayModeScreen {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn ui<B: Backend>(&mut self, f: &mut Frame<B>) {
 | 
			
		||||
        let area = centered_rect_size(50, 5, &f.size());
 | 
			
		||||
        let area = centered_rect_size(50, 2 + AVAILABLE_PLAY_MODES.len() as u16, &f.size());
 | 
			
		||||
 | 
			
		||||
        // Create a List from all list items and highlight the currently selected one
 | 
			
		||||
        let items = AVAILABLE_PLAY_MODES
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user