diff --git a/robusta/src/gadgets.rs b/robusta/src/gadgets.rs index d2c5304..e86f65f 100644 --- a/robusta/src/gadgets.rs +++ b/robusta/src/gadgets.rs @@ -39,7 +39,6 @@ impl GadgetState { if let Some(cooldown) = self.cooldown.as_mut() { *cooldown -= delta; if *cooldown < 0.0 { - self.can_be_used = true; self.cooldown = None; } } @@ -51,7 +50,6 @@ impl GadgetState { pub fn try_use(&mut self, gadget: &T, cooldown: f32) -> bool { if self.can_be_used && self.cooldown.is_none() && self.used.insert(mem::discriminant(gadget)) { - self.can_be_used = false; self.cooldown = Some(cooldown); true } else { diff --git a/robusta/src/main.rs b/robusta/src/main.rs index f6f09f4..8a4c5df 100644 --- a/robusta/src/main.rs +++ b/robusta/src/main.rs @@ -384,45 +384,16 @@ async fn run_game_loop(mut recv: Receiver, state: SharedState) { .build("logs") .expect("failed to initialize rolling file appender"); - let mut special_pos = None; + // the time for a single frame + let mut interval = tokio::time::interval(Duration::from_millis(500)); - let mut interval = tokio::time::interval(Duration::from_millis(100)); + let running_state = Arc::new(tokio::sync::Mutex::new(RunningState::new())); + start_game(Arc::clone(&running_state)).await; - let mut time = Instant::now(); - let mut position_cooldown = None; - let mut mr_x_gadgets = GadgetState::new(); - let mut detective_gadgets = GadgetState::new(); loop { interval.tick().await; - let old_time = time; - time = Instant::now(); - let delta = (time - old_time).as_secs_f32(); - if let Some(cooldown) = position_cooldown.as_mut() { - *cooldown -= delta; - if *cooldown < 0.0 { - position_cooldown = Some(COOLDOWN); - // broadcast Mr. X position - match special_pos { - Some(SpecialPos::Stop(stop_id)) => { - // TODO: broadcast stop id - } - Some(SpecialPos::Image(image)) => { - // TODO: broadcast image - } - Some(SpecialPos::NotFound) => { - // TODO: broadcast 404 - } - None => { - // TODO: broadcast Mr. X position - } - } - special_pos = None; - } - } - mr_x_gadgets.update_time(delta); - detective_gadgets.update_time(delta); - + // handle messages let mut state = state.lock().await; while let Ok(msg) = recv.try_recv() { match msg { @@ -472,21 +443,22 @@ async fn run_game_loop(mut recv: Receiver, state: SharedState) { warn!("Client {} tried to use MrX Gadget, but is not MrX", id); continue; } - if !mr_x_gadgets.try_use(&gadget, COOLDOWN) { + let mut running_state = running_state.lock().await; + if !running_state.mr_x_gadgets.try_use(&gadget, COOLDOWN) { warn!("Client {} tried to use MrX Gadget, but is not allowed to", id); continue; } match &gadget { AlternativeFacts { stop_id } => { - special_pos = Some(SpecialPos::Stop(stop_id.clone())); + running_state.special_pos = Some(SpecialPos::Stop(stop_id.clone())); continue; } Midjourney { image } => { - special_pos = Some(SpecialPos::Image(image.clone())); + running_state.special_pos = Some(SpecialPos::Image(image.clone())); } NotFound => { - special_pos = Some(SpecialPos::NotFound); + running_state.special_pos = Some(SpecialPos::NotFound); continue; } Teleport => {} @@ -514,7 +486,8 @@ async fn run_game_loop(mut recv: Receiver, state: SharedState) { warn!("Client {} tried to use Detective Gadget, but is not Detective", id); continue; } - if !detective_gadgets.try_use(&gadget, COOLDOWN) { + let mut running_state = running_state.lock().await; + if !running_state.detective_gadgets.try_use(&gadget, COOLDOWN) { warn!("Client {} tried to use Detective Gadget, but is not allowed to", id); continue; } @@ -551,6 +524,7 @@ async fn run_game_loop(mut recv: Receiver, state: SharedState) { } } + // compute train positions let time = chrono::Utc::now(); let mut trains = kvv::train_positions(&departures, time); trains.retain(|x| !x.line_id.contains("bus")); @@ -565,12 +539,16 @@ async fn run_game_loop(mut recv: Receiver, state: SharedState) { } } - let game_state = GameState { - teams: state.teams.clone(), - trains, - position_cooldown, - mr_x_gadget_cooldown: mr_x_gadgets.remaining(), - detective_gadget_cooldown: detective_gadgets.remaining(), + // log game state + let game_state = { + let running_state = running_state.lock().await; + GameState { + teams: state.teams.clone(), + trains, + position_cooldown: running_state.position_cooldown, + mr_x_gadget_cooldown: running_state.mr_x_gadgets.remaining(), + detective_gadget_cooldown: running_state.detective_gadgets.remaining(), + } }; writeln!( log_file, @@ -581,6 +559,7 @@ async fn run_game_loop(mut recv: Receiver, state: SharedState) { .unwrap(); fs::write(TEAMS_FILE, serde_json::to_string_pretty(&game_state.teams).unwrap()).unwrap(); + // broadcast game state to clients for connection in state.connections.iter_mut() { let game_state = GameState { teams: game_state @@ -605,3 +584,69 @@ async fn run_game_loop(mut recv: Receiver, state: SharedState) { } } } + +struct RunningState { + position_cooldown: Option, + mr_x_gadgets: GadgetState, + detective_gadgets: GadgetState, + special_pos: Option, +} + +impl RunningState { + fn new() -> Self { + Self { + position_cooldown: None, + mr_x_gadgets: GadgetState::new(), + detective_gadgets: GadgetState::new(), + special_pos: None, + } + } +} + +async fn start_game(state: Arc>) { + tokio::spawn(async move { + let mut warmup = true; + + let mut time = Instant::now(); + let mut interval = tokio::time::interval(Duration::from_millis(100)); + loop { + interval.tick().await; + let mut state = state.lock().await; + + let old_time = time; + time = Instant::now(); + let delta = (time - old_time).as_secs_f32(); + if let Some(cooldown) = state.position_cooldown.as_mut() { + *cooldown -= delta; + if *cooldown < 0.0 { + state.position_cooldown = Some(COOLDOWN); + if warmup { + // TODO: broadcast Detective start + warmup = false; + state.mr_x_gadgets.allow_use(); + state.detective_gadgets.allow_use(); + } else { + // broadcast Mr. X position + match &state.special_pos { + Some(SpecialPos::Stop(stop_id)) => { + // TODO: broadcast stop id + } + Some(SpecialPos::Image(image)) => { + // TODO: broadcast image + } + Some(SpecialPos::NotFound) => { + // TODO: broadcast 404 + } + None => { + // TODO: broadcast Mr. X position + } + } + state.special_pos = None; + } + } + } + state.mr_x_gadgets.update_time(delta); + state.detective_gadgets.update_time(delta); + } + }); +}