Implement strike timeout on server side
This commit is contained in:
		@@ -130,31 +130,31 @@ impl GameRulesConfigurationScreen {
 | 
			
		||||
                        KeyCode::Char(c) if ('0'..='9').contains(&c) => {
 | 
			
		||||
                            let val = c.to_string().parse::<usize>().unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
                            if self.curr_field == EditingField::MapWidth {
 | 
			
		||||
                                if self.rules.map_width <= MAX_MAP_WIDTH {
 | 
			
		||||
                            if self.curr_field == EditingField::MapWidth
 | 
			
		||||
                                && self.rules.map_width <= MAX_MAP_WIDTH
 | 
			
		||||
                            {
 | 
			
		||||
                                self.rules.map_width *= 10;
 | 
			
		||||
                                self.rules.map_width += val;
 | 
			
		||||
                            }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if self.curr_field == EditingField::MapHeight {
 | 
			
		||||
                                if self.rules.map_height <= MAX_MAP_HEIGHT {
 | 
			
		||||
                            if self.curr_field == EditingField::MapHeight
 | 
			
		||||
                                && self.rules.map_height <= MAX_MAP_HEIGHT
 | 
			
		||||
                            {
 | 
			
		||||
                                self.rules.map_height *= 10;
 | 
			
		||||
                                self.rules.map_height += val;
 | 
			
		||||
                            }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if self.curr_field == EditingField::BoatsList {
 | 
			
		||||
                                if self.rules.boats_list().len() < MAX_BOATS_NUMBER {
 | 
			
		||||
                            if self.curr_field == EditingField::BoatsList
 | 
			
		||||
                                && self.rules.boats_list().len() < MAX_BOATS_NUMBER
 | 
			
		||||
                            {
 | 
			
		||||
                                self.rules.add_boat(val);
 | 
			
		||||
                            }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if self.curr_field == EditingField::StrikeTimeout {
 | 
			
		||||
                                let mut timeout = self.rules.strike_timeout.unwrap_or(0);
 | 
			
		||||
                                if timeout <= MAX_STRIKE_TIMEOUT {
 | 
			
		||||
                                    timeout *= 10;
 | 
			
		||||
                                    timeout += val;
 | 
			
		||||
                                    timeout += val as u64;
 | 
			
		||||
                                    self.rules.strike_timeout = Some(timeout);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 
 | 
			
		||||
@@ -25,5 +25,5 @@ pub const INVITE_CODE_LENGTH: usize = 5;
 | 
			
		||||
pub const MIN_PLAYER_NAME_LENGTH: usize = 1;
 | 
			
		||||
pub const MAX_PLAYER_NAME_LENGTH: usize = 10;
 | 
			
		||||
 | 
			
		||||
pub const MIN_STRIKE_TIMEOUT: usize = 5;
 | 
			
		||||
pub const MAX_STRIKE_TIMEOUT: usize = 90;
 | 
			
		||||
pub const MIN_STRIKE_TIMEOUT: u64 = 5;
 | 
			
		||||
pub const MAX_STRIKE_TIMEOUT: u64 = 90;
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ pub struct GameRules {
 | 
			
		||||
    #[serde_as(as = "DisplayFromStr")]
 | 
			
		||||
    pub player_continue_on_hit: bool,
 | 
			
		||||
    #[serde_as(as = "NoneAsEmptyString")]
 | 
			
		||||
    pub strike_timeout: Option<usize>,
 | 
			
		||||
    pub strike_timeout: Option<u64>,
 | 
			
		||||
    pub bot_type: BotType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -59,8 +59,8 @@ pub struct PlayConfiguration {
 | 
			
		||||
    pub ordinate_alphabet: &'static str,
 | 
			
		||||
    pub min_player_name_len: usize,
 | 
			
		||||
    pub max_player_name_len: usize,
 | 
			
		||||
    pub min_strike_timeout: usize,
 | 
			
		||||
    pub max_strike_timeout: usize,
 | 
			
		||||
    pub min_strike_timeout: u64,
 | 
			
		||||
    pub max_strike_timeout: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PlayConfiguration {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use actix::prelude::*;
 | 
			
		||||
use actix::{Actor, Context, Handler};
 | 
			
		||||
@@ -6,6 +7,7 @@ use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
use crate::bot_player::BotPlayer;
 | 
			
		||||
use crate::data::*;
 | 
			
		||||
use crate::utils::time_utils::time;
 | 
			
		||||
 | 
			
		||||
pub trait Player {
 | 
			
		||||
    fn get_name(&self) -> &str;
 | 
			
		||||
@@ -51,6 +53,9 @@ pub trait Player {
 | 
			
		||||
    fn opponent_replaced_by_bot(&self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// How often strike timeout controller is run
 | 
			
		||||
const STRIKE_TIMEOUT_CONTROL: Duration = Duration::from_secs(1);
 | 
			
		||||
 | 
			
		||||
fn opponent(index: usize) -> usize {
 | 
			
		||||
    match index {
 | 
			
		||||
        0 => 1,
 | 
			
		||||
@@ -85,6 +90,7 @@ pub struct Game {
 | 
			
		||||
    map_0: Option<GameMap>,
 | 
			
		||||
    map_1: Option<GameMap>,
 | 
			
		||||
    turn: usize,
 | 
			
		||||
    curr_strike_request_started: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Game {
 | 
			
		||||
@@ -96,6 +102,7 @@ impl Game {
 | 
			
		||||
            map_0: None,
 | 
			
		||||
            map_1: None,
 | 
			
		||||
            turn: 0,
 | 
			
		||||
            curr_strike_request_started: 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -130,10 +137,14 @@ impl Game {
 | 
			
		||||
            self.turn
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        self.request_fire();
 | 
			
		||||
        self.request_fire(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn request_fire(&mut self, reset_counter: bool) {
 | 
			
		||||
        if reset_counter {
 | 
			
		||||
            self.curr_strike_request_started = time();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    fn request_fire(&self) {
 | 
			
		||||
        self.players[self.turn].request_fire(self.get_game_status_for_player(self.turn));
 | 
			
		||||
        self.players[opponent(self.turn)]
 | 
			
		||||
            .opponent_must_fire(self.get_game_status_for_player(opponent(self.turn)));
 | 
			
		||||
@@ -148,6 +159,27 @@ impl Game {
 | 
			
		||||
        .unwrap()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Replace user for a fire in case of timeout
 | 
			
		||||
    fn force_fire_in_case_of_timeout(&mut self) {
 | 
			
		||||
        if self.status != GameStatus::Started || self.rules.strike_timeout.is_none() {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let timeout = self.rules.strike_timeout.unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
        if time() <= self.curr_strike_request_started + timeout {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Determine target of fire
 | 
			
		||||
        let target = self
 | 
			
		||||
            .get_game_status_for_player(opponent(self.turn))
 | 
			
		||||
            .find_fire_coordinates_for_bot_type(self.rules.bot_type);
 | 
			
		||||
 | 
			
		||||
        // Fire as player
 | 
			
		||||
        self.handle_fire(target);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn player_map_mut(&mut self, id: usize) -> &mut GameMap {
 | 
			
		||||
        match id {
 | 
			
		||||
            0 => self.map_0.as_mut(),
 | 
			
		||||
@@ -166,7 +198,7 @@ impl Game {
 | 
			
		||||
        // Easiest case : player missed his fire
 | 
			
		||||
        if result == FireResult::Missed {
 | 
			
		||||
            self.turn = opponent(self.turn);
 | 
			
		||||
            self.request_fire();
 | 
			
		||||
            self.request_fire(true);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -188,7 +220,9 @@ impl Game {
 | 
			
		||||
            self.turn = opponent(self.turn);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.request_fire();
 | 
			
		||||
        self.request_fire(
 | 
			
		||||
            result != FireResult::AlreadyTargetedPosition && result != FireResult::Rejected,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn handle_request_rematch(&mut self, player_id: Uuid) {
 | 
			
		||||
@@ -234,6 +268,14 @@ impl Game {
 | 
			
		||||
 | 
			
		||||
impl Actor for Game {
 | 
			
		||||
    type Context = Context<Self>;
 | 
			
		||||
 | 
			
		||||
    fn started(&mut self, ctx: &mut Self::Context) {
 | 
			
		||||
        if self.rules.strike_timeout.is_some() {
 | 
			
		||||
            ctx.run_interval(STRIKE_TIMEOUT_CONTROL, |act, _ctx| {
 | 
			
		||||
                act.force_fire_in_case_of_timeout();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Message)]
 | 
			
		||||
@@ -387,7 +429,7 @@ impl Handler<PlayerLeftGame> for Game {
 | 
			
		||||
 | 
			
		||||
            // Re-do current action
 | 
			
		||||
            if self.status == GameStatus::Started {
 | 
			
		||||
                self.request_fire();
 | 
			
		||||
                self.request_fire(true);
 | 
			
		||||
            } else if self.status == GameStatus::WaitingForBoatsDisposition {
 | 
			
		||||
                self.players[offline_player].query_boats_layout(&self.rules);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ use std::io::ErrorKind;
 | 
			
		||||
 | 
			
		||||
pub mod network_utils;
 | 
			
		||||
pub mod string_utils;
 | 
			
		||||
pub mod time_utils;
 | 
			
		||||
 | 
			
		||||
pub type Res<E = ()> = Result<E, Box<dyn Error>>;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								rust/sea_battle_backend/src/utils/time_utils.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								rust/sea_battle_backend/src/utils/time_utils.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
use std::time::{SystemTime, UNIX_EPOCH};
 | 
			
		||||
 | 
			
		||||
/// Get the current time since epoch
 | 
			
		||||
pub fn time() -> u64 {
 | 
			
		||||
    SystemTime::now()
 | 
			
		||||
        .duration_since(UNIX_EPOCH)
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .as_secs()
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user