no time control for practice

This commit is contained in:
ntr 2019-06-09 17:38:25 +10:00
parent 39501c167a
commit 1070d3482a
8 changed files with 117 additions and 40 deletions

View File

@ -31,6 +31,8 @@
mobile info page mobile info page
mnml tv
rework scatter rework scatter
hatred maybe hatred maybe

View File

@ -333,11 +333,11 @@
height: 2px; height: 2px;
transform-origin: center; transform-origin: center;
background-color: whitesmoke; background-color: whitesmoke;
animation: equipping-skill 2s infinite ease alternate; animation: equipping-skill 2s infinite ease-out alternate;
opacity: 0; opacity: 0;
} }
.equipping::after, .equip-spec::after { .equipping::after {
content: ''; content: '';
position: absolute; position: absolute;
bottom: 2px; bottom: 2px;
@ -346,7 +346,7 @@
height: 2px; height: 2px;
transform-origin: center; transform-origin: center;
background-color: whitesmoke; background-color: whitesmoke;
animation: equipping-skill 2s infinite ease alternate; animation: equipping-skill 2s infinite ease-out alternate;
opacity: 0; opacity: 0;
animation-delay: 0.75s animation-delay: 0.75s
} }
@ -373,7 +373,16 @@
} }
.equip-spec::after { .equip-spec::after {
animation-delay: 0; content: '';
position: absolute;
bottom: 2px;
left: 50%;
width: 100%;
height: 2px;
transform-origin: center;
background-color: whitesmoke;
animation: equipping-skill 2s infinite ease-out alternate;
opacity: 0;
} }
.equip .specs figcaption { .equip .specs figcaption {

View File

@ -108,7 +108,7 @@ function GameFooter(props) {
const now = Date.now(); const now = Date.now();
const end = Date.parse(game.phase_end); const end = Date.parse(game.phase_end);
const timerPct = ((now - zero) / (end - zero) * 100); const timerPct = ((now - zero) / (end - zero) * 100);
const displayPct = resolution || game.phase === 'Finish' const displayPct = resolution || game.phase === 'Finish' || !game.phase_end
? 0 ? 0
: Math.min(timerPct, 100); : Math.min(timerPct, 100);
@ -123,11 +123,13 @@ function GameFooter(props) {
background: displayColour, background: displayColour,
}; };
const timer = ( const timer = game.phase_end
? (
<div class="timer-container"> <div class="timer-container">
<div class="timer" style={timerStyles} >&nbsp;</div> <div class="timer" style={timerStyles} >&nbsp;</div>
</div> </div>
); )
: null;
return ( return (
<footer> <footer>

View File

@ -67,7 +67,7 @@ class InstanceCreateForm extends Component {
placeholder="game name" placeholder="game name"
onInput={this.nameInput} onInput={this.nameInput}
/> />
<label htmlFor="pveSelect">Practice Mode - Bo10, vs CPU, 2x turn time</label> <label htmlFor="pveSelect">Practice Mode - Bo10, vs CPU, no time control</label>
<input id="pveSelect" <input id="pveSelect"
type="checkbox" type="checkbox"
disabled={disabled} disabled={disabled}

View File

@ -94,7 +94,9 @@ function Instance(args) {
const zero = Date.parse(instance.phase_start); const zero = Date.parse(instance.phase_start);
const now = Date.now(); const now = Date.now();
const end = Date.parse(instance.phase_end); const end = Date.parse(instance.phase_end);
const timerPct = ((now - zero) / (end - zero) * 100); const timerPct = instance.phase_end
? ((now - zero) / (end - zero) * 100)
: 0;
const displayColour = player.ready const displayColour = player.ready
? 'forestgreen' ? 'forestgreen'

View File

@ -38,8 +38,8 @@ pub struct Game {
pub resolved: Vec<Resolution>, pub resolved: Vec<Resolution>,
pub instance: Option<Uuid>, pub instance: Option<Uuid>,
time_control: TimeControl, time_control: TimeControl,
phase_end: DateTime<Utc>,
phase_start: DateTime<Utc>, phase_start: DateTime<Utc>,
phase_end: Option<DateTime<Utc>>,
} }
impl Game { impl Game {
@ -54,13 +54,14 @@ impl Game {
resolved: vec![], resolved: vec![],
instance: None, instance: None,
time_control: TimeControl::Standard, time_control: TimeControl::Standard,
phase_end: Utc::now(), phase_end: None,
phase_start: Utc::now(), phase_start: Utc::now(),
}; };
} }
pub fn set_time_control(&mut self, tc: TimeControl) -> &mut Game { pub fn set_time_control(&mut self, tc: TimeControl) -> &mut Game {
self.time_control = tc; self.time_control = tc;
self.phase_end = Some(tc.lobby_timeout());
self self
} }
@ -159,13 +160,12 @@ impl Game {
fn skill_phase_start(mut self, num_resolutions: usize) -> Game { fn skill_phase_start(mut self, num_resolutions: usize) -> Game {
let resolution_animation_ms = num_resolutions as i64 * 2500; let resolution_animation_ms = num_resolutions as i64 * 2500;
let phase_add_time_ms = self.time_control.game_time_seconds() * 1000 + resolution_animation_ms;
self.phase_start = Utc::now() self.phase_start = Utc::now()
.checked_add_signed(Duration::milliseconds(resolution_animation_ms)) .checked_add_signed(Duration::milliseconds(resolution_animation_ms))
.expect("could not set phase start"); .expect("could not set phase start");
self.phase_end = Utc::now()
.checked_add_signed(Duration::milliseconds(phase_add_time_ms as i64)) self.phase_end = self.time_control.game_phase_end(resolution_animation_ms);
.expect("could not set phase end");
for player in self.players.iter_mut() { for player in self.players.iter_mut() {
if player.skills_required() == 0 { if player.skills_required() == 0 {
@ -571,7 +571,10 @@ impl Game {
} }
fn phase_timed_out(&self) -> bool { fn phase_timed_out(&self) -> bool {
Utc::now().signed_duration_since(self.phase_end).num_milliseconds() > 0 match self.phase_end {
Some(t) => Utc::now().signed_duration_since(t).num_milliseconds() > 0,
None => false,
}
} }
pub fn upkeep(mut self) -> Game { pub fn upkeep(mut self) -> Game {
@ -1435,7 +1438,7 @@ mod tests {
fn upkeep_test() { fn upkeep_test() {
let mut game = create_2v2_test_game(); let mut game = create_2v2_test_game();
game.players[0].set_ready(true); game.players[0].set_ready(true);
game.phase_end = Utc::now().checked_sub_signed(Duration::seconds(61)).unwrap(); game.phase_end = Some(Utc::now().checked_sub_signed(Duration::seconds(500)).unwrap());
game = game.upkeep(); game = game.upkeep();
assert!(game.players[1].warnings == 1); assert!(game.players[1].warnings == 1);
} }

View File

@ -45,6 +45,7 @@ impl Round {
#[derive(Debug,Clone,Copy,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,Serialize,Deserialize)]
pub enum TimeControl { pub enum TimeControl {
Standard, Standard,
Slow,
Practice, Practice,
} }
@ -52,21 +53,47 @@ impl TimeControl {
fn vbox_time_seconds(&self) -> i64 { fn vbox_time_seconds(&self) -> i64 {
match self { match self {
TimeControl::Standard => 120, TimeControl::Standard => 120,
TimeControl::Practice => 240, TimeControl::Slow => 240,
TimeControl::Practice => panic!("practice vbox seconds called"),
} }
} }
pub fn game_time_seconds(&self) -> i64 { fn game_time_seconds(&self) -> i64 {
match self { match self {
TimeControl::Standard => 60, TimeControl::Standard => 60,
TimeControl::Practice => 120, TimeControl::Slow => 120,
TimeControl::Practice => panic!("practice game seconds called"),
}
}
pub fn vbox_phase_end(&self) -> Option<DateTime<Utc>> {
match self {
TimeControl::Practice => None,
_ => Some(Utc::now()
.checked_add_signed(Duration::seconds(self.vbox_time_seconds()))
.expect("could not set vbox phase end")),
}
}
pub fn lobby_timeout(&self) -> DateTime<Utc> {
Utc::now()
.checked_add_signed(Duration::minutes(5))
.expect("could not set phase end")
}
pub fn game_phase_end(&self, resolution_time_ms: i64) -> Option<DateTime<Utc>> {
match self {
TimeControl::Practice => None,
_ => Some(Utc::now()
.checked_add_signed(Duration::milliseconds(self.vbox_time_seconds() * 1000 + resolution_time_ms))
.expect("could not set game phase end")),
} }
} }
} }
#[derive(Debug,Clone,Serialize,Deserialize)] #[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Instance { pub struct Instance {
id: Uuid, pub id: Uuid,
pub name: String, pub name: String,
players: Vec<Player>, players: Vec<Player>,
@ -79,7 +106,7 @@ pub struct Instance {
time_control: TimeControl, time_control: TimeControl,
phase: InstancePhase, phase: InstancePhase,
phase_end: DateTime<Utc>, phase_end: Option<DateTime<Utc>>,
phase_start: DateTime<Utc>, phase_start: DateTime<Utc>,
} }
@ -97,9 +124,7 @@ impl Instance {
time_control: TimeControl::Standard, time_control: TimeControl::Standard,
password: None, password: None,
phase_start: Utc::now(), phase_start: Utc::now(),
phase_end: Utc::now() phase_end: Some(TimeControl::Standard.lobby_timeout()),
.checked_add_signed(Duration::minutes(5))
.expect("could not set phase end"),
} }
} }
@ -116,12 +141,15 @@ impl Instance {
name: "Global Matchmaking".to_string(), name: "Global Matchmaking".to_string(),
password: None, password: None,
phase_start: Utc::now(), phase_start: Utc::now(),
phase_end: Utc::now(), phase_end: Some(TimeControl::Standard.lobby_timeout()),
} }
} }
fn phase_timed_out(&self) -> bool { fn phase_timed_out(&self) -> bool {
Utc::now().signed_duration_since(self.phase_end).num_milliseconds() > 0 match self.phase_end {
Some(t) => Utc::now().signed_duration_since(t).num_milliseconds() > 0,
None => false,
}
} }
fn timed_out_players(&self) -> Vec<Uuid> { fn timed_out_players(&self) -> Vec<Uuid> {
@ -312,9 +340,7 @@ impl Instance {
self.phase = InstancePhase::InProgress; self.phase = InstancePhase::InProgress;
self.phase_start = Utc::now(); self.phase_start = Utc::now();
self.phase_end = Utc::now() self.phase_end = self.time_control.vbox_phase_end();
.checked_add_signed(Duration::seconds(self.time_control.vbox_time_seconds()))
.expect("could not set phase end");
self.players.iter_mut().for_each(|p| { self.players.iter_mut().for_each(|p| {
@ -336,7 +362,7 @@ impl Instance {
|| self.rounds.len() == self.max_rounds || self.rounds.len() == self.max_rounds
} }
fn finish(&mut self) -> &mut Instance { pub fn finish(&mut self) -> &mut Instance {
self.phase = InstancePhase::Finished; self.phase = InstancePhase::Finished;
self self
} }
@ -637,6 +663,37 @@ pub fn instances_need_upkeep(tx: &mut Transaction) -> Result<Vec<Instance>, Erro
return Ok(list); return Ok(list);
} }
// timed out instances with no time control
pub fn instances_idle(tx: &mut Transaction) -> Result<Vec<Instance>, Error> {
let query = "
SELECT data, id
FROM instances
WHERE finished = false
AND updated_at < now() - interval '1 hour'
FOR UPDATE;
";
let result = tx
.query(query, &[])?;
let mut list = vec![];
for row in result.into_iter() {
let bytes: Vec<u8> = row.get(0);
let id = row.get(1);
match from_slice::<Instance>(&bytes) {
Ok(i) => list.push(i),
Err(_e) => {
instance_delete(tx, id)?;
}
};
}
return Ok(list);
}
pub fn instance_new(params: InstanceLobbyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> { pub fn instance_new(params: InstanceLobbyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let mut instance = match params.pve { let mut instance = match params.pve {
true => Instance::new() true => Instance::new()
@ -801,7 +858,7 @@ mod tests {
instance.player_ready(a_id).expect("a ready"); instance.player_ready(a_id).expect("a ready");
instance.player_ready(b_id).expect("b ready"); instance.player_ready(b_id).expect("b ready");
instance.phase_end = Utc::now().checked_sub_signed(Duration::seconds(61)).unwrap(); instance.phase_end = Some(Utc::now().checked_sub_signed(Duration::seconds(500)).unwrap());
let (mut instance, new_games) = instance.upkeep(); let (mut instance, new_games) = instance.upkeep();
@ -830,7 +887,7 @@ mod tests {
instance.add_player(player).expect("could not add player"); instance.add_player(player).expect("could not add player");
assert!(!instance.can_start()); assert!(!instance.can_start());
instance.phase_end = Utc::now().checked_sub_signed(Duration::minutes(61)).unwrap(); instance.phase_end = Some(Utc::now().checked_sub_signed(Duration::minutes(61)).unwrap());
let (instance, _new_games) = instance.upkeep(); let (instance, _new_games) = instance.upkeep();
assert!(instance.finished()); assert!(instance.finished());

View File

@ -4,7 +4,7 @@ use postgres::transaction::Transaction;
use failure::Error; use failure::Error;
use game::{games_need_upkeep, game_update, game_write, game_delete}; use game::{games_need_upkeep, game_update, game_write, game_delete};
use instance::{instances_need_upkeep, instance_update}; use instance::{instances_need_upkeep, instances_idle, instance_update, instance_delete};
use net::{Db}; use net::{Db};
fn fetch_games(mut tx: Transaction) -> Result<Transaction, Error> { fn fetch_games(mut tx: Transaction) -> Result<Transaction, Error> {
@ -25,9 +25,7 @@ fn fetch_games(mut tx: Transaction) -> Result<Transaction, Error> {
} }
fn fetch_instances(mut tx: Transaction) -> Result<Transaction, Error> { fn fetch_instances(mut tx: Transaction) -> Result<Transaction, Error> {
let instances = instances_need_upkeep(&mut tx)?; for mut instance in instances_need_upkeep(&mut tx)? {
for mut instance in instances {
let (instance, new_games) = instance.upkeep(); let (instance, new_games) = instance.upkeep();
for game in new_games { for game in new_games {
game_write(&mut tx, &game)?; game_write(&mut tx, &game)?;
@ -35,6 +33,10 @@ fn fetch_instances(mut tx: Transaction) -> Result<Transaction, Error> {
instance_update(&mut tx, instance)?; instance_update(&mut tx, instance)?;
} }
for mut instance in instances_idle(&mut tx)? {
instance_delete(&mut tx, instance.id)?;
}
Ok(tx) Ok(tx)
} }