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