From 4a2e90eccb8b13f9350c856b66f2632f08025e96 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 23 Mar 2025 23:06:12 +0100 Subject: pause game if all players are idle, closes #120 --- server/src/server.rs | 6 +++++- server/src/state.rs | 40 ++++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 11 deletions(-) (limited to 'server/src') diff --git a/server/src/server.rs b/server/src/server.rs index 3da95a43..a06f4f36 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -42,7 +42,8 @@ use tokio::sync::broadcast::Sender; pub struct Server { pub tx: Sender, - pub connections: HashMap>, + pub connections: HashMap, bool)>, + pub paused: bool, pub game: Game, @@ -328,9 +329,11 @@ impl Server { .await .context("Failed to load scoreboards")?, editor_address: None, + paused: false, }) } } + impl Server { pub fn load( &mut self, @@ -629,6 +632,7 @@ impl Server { self.score_changed = true; } PacketS::ReplayTick { .. } => return Err(tre!("s.error.packet_not_supported")), + PacketS::Idle { .. } => (), } Ok(()) } diff --git a/server/src/state.rs b/server/src/state.rs index 20e2bf80..53ca3b42 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -18,14 +18,32 @@ use crate::{message::TrError, server::Server, tre, trm, ConnectionID}; use anyhow::Result; use hurrycurry_protocol::{Message, PacketC, PacketS, PlayerID}; -use log::{debug, trace}; +use log::{debug, info, trace}; impl Server { pub async fn tick_outer(&mut self, dt: f32) -> anyhow::Result<()> { - let r = self.tick(dt); - if let Some((name, timer)) = r { - self.scoreboard.save().await?; - self.load(self.index.generate(&name).await?, timer); + let should_pause = self.connections.iter().all(|c| c.1 .1); + if should_pause != self.paused { + info!("Game paused: {}", should_pause); + self.paused = should_pause; + for p in self.game.players.keys() { + self.packet_out.push_back(PacketC::ServerHint { + position: None, + message: if self.paused { + Some(trm!("s.state.paused")) + } else { + None + }, + player: *p, + }); + } + } + if !self.paused { + let r = self.tick(dt); + if let Some((name, timer)) = r { + self.scoreboard.save().await?; + self.load(self.index.generate(&name).await?, timer); + } } while let Some(p) = self.packet_out.pop_front() { if matches!(p, PacketC::UpdateMap { .. } | PacketC::Movement { .. }) { @@ -43,7 +61,7 @@ impl Server { packet: PacketS, ) -> Result, TrError> { if let Some(p) = get_packet_player(&packet) { - if !self.connections.entry(conn).or_default().contains(&p) { + if !self.connections.entry(conn).or_default().0.contains(&p) { return Err(tre!("s.error.packet_sender_invalid")); } } @@ -65,11 +83,12 @@ impl Server { } } } + PacketS::Idle { paused } => self.connections.entry(conn).or_default().1 = *paused, PacketS::Leave { player } => { - self.connections.entry(conn).or_default().remove(player); + self.connections.entry(conn).or_default().0.remove(player); } PacketS::Join { .. } => { - if self.connections.entry(conn).or_default().len() > 8 { + if self.connections.entry(conn).or_default().0.len() > 8 { return Err(tre!("s.error.conn_too_many_players")); } } @@ -79,7 +98,7 @@ impl Server { for p in &replies { if let PacketC::Joined { id } = p { - self.connections.entry(conn).or_default().insert(*id); + self.connections.entry(conn).or_default().0.insert(*id); } } @@ -102,7 +121,7 @@ impl Server { } pub async fn disconnect(&mut self, conn: ConnectionID) { - if let Some(players) = self.connections.get(&conn) { + if let Some((players, _)) = self.connections.get(&conn) { for player in players.clone() { let _ = self.packet_in_outer(conn, PacketS::Leave { player }).await; } @@ -114,6 +133,7 @@ impl Server { fn get_packet_player(packet: &PacketS) -> Option { match packet { PacketS::Join { .. } => None, + PacketS::Idle { .. } => None, PacketS::Leave { player } => Some(*player), PacketS::Movement { player, .. } => Some(*player), PacketS::Interact { player, .. } => Some(*player), -- cgit v1.2.3-70-g09d2