diff options
author | metamuffin <metamuffin@disroot.org> | 2025-03-23 23:06:12 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-03-23 23:06:12 +0100 |
commit | 4a2e90eccb8b13f9350c856b66f2632f08025e96 (patch) | |
tree | 5ee4b053a987d5c7c6f131fe46015b0b6c9fbfe4 | |
parent | 2795f974e95ff9b18f3b6e20db88d797e6e6c66c (diff) | |
download | hurrycurry-4a2e90eccb8b13f9350c856b66f2632f08025e96.tar hurrycurry-4a2e90eccb8b13f9350c856b66f2632f08025e96.tar.bz2 hurrycurry-4a2e90eccb8b13f9350c856b66f2632f08025e96.tar.zst |
pause game if all players are idle, closes #120
-rw-r--r-- | client/menu/ingame.gd | 2 | ||||
-rw-r--r-- | client/multiplayer.gd | 6 | ||||
-rw-r--r-- | locale/en.ini | 3 | ||||
-rw-r--r-- | server/protocol/src/lib.rs | 3 | ||||
-rw-r--r-- | server/src/server.rs | 6 | ||||
-rw-r--r-- | server/src/state.rs | 40 |
6 files changed, 48 insertions, 12 deletions
diff --git a/client/menu/ingame.gd b/client/menu/ingame.gd index 33aa5fc1..85c03ad4 100644 --- a/client/menu/ingame.gd +++ b/client/menu/ingame.gd @@ -44,7 +44,9 @@ func anim_setup(): pass func _menu_open(): anim.play("activate") await anim.animation_finished + game.mp.send_idle(true) func _menu_exit(): + game.mp.send_idle(false) anim.play_backwards("activate") await anim.animation_finished diff --git a/client/multiplayer.gd b/client/multiplayer.gd index 5a9406ab..084622c3 100644 --- a/client/multiplayer.gd +++ b/client/multiplayer.gd @@ -135,6 +135,12 @@ func send_replay_tick(dt: float): "dt": dt }) +func send_idle(paused: bool): + send_packet({ + "type": "idle", + "paused": paused, + }) + func send_leave(player): send_packet({ "type": "leave", diff --git a/locale/en.ini b/locale/en.ini index 7b201183..71df1707 100644 --- a/locale/en.ini +++ b/locale/en.ini @@ -2,7 +2,6 @@ c.chat.write_message=Write message c.credits.contributors=with contributions from c.credits.developed_by=developed by -c.legal.using_godot=This game uses Godot Engine, available under the following license: c.credits.models=Models c.credits.sounds=Sounds c.credits.thanks=Thank You For Playing @@ -27,6 +26,7 @@ c.hint.reset_camera=Press {0} to reset the camera view c.hint.rotate=Use {0} to rotate the camera view c.hint.username_tags=Username tags can be enabled/disabled in the settings c.hint.zoom_camera=Use {0} to zoom in/out +c.legal.using_godot=This game uses Godot Engine, available under the following license: c.map.difficulty.0=Easy c.map.difficulty.1=Hard c.map.difficulty.2=Moderate @@ -254,6 +254,7 @@ s.replay.cannot_join=Replays cannot be joined. s.state.abort_no_players=Game was aborted due to a lack of players. s.state.game_aborted=Game was aborted by {0}. s.state.overflow_resubscribe=Lagging behind. Some clientbound packets were dropped. +s.state.paused=Game paused s.tutorial.accept_order=Approach the customer take their order s.tutorial.active_cuttingboard=Cut the item to slices here s.tutorial.active=Interact here for {0}s diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 552c4eed..24e547f6 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -142,6 +142,9 @@ pub enum PacketS { pin: Option<bool>, }, + Idle { + paused: bool, + }, /// For use in replay sessions only ReplayTick { dt: f64, 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<PacketC>, - pub connections: HashMap<ConnectionID, HashSet<PlayerID>>, + pub connections: HashMap<ConnectionID, (HashSet<PlayerID>, 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<Vec<PacketC>, 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<PlayerID> { match packet { PacketS::Join { .. } => None, + PacketS::Idle { .. } => None, PacketS::Leave { player } => Some(*player), PacketS::Movement { player, .. } => Some(*player), PacketS::Interact { player, .. } => Some(*player), |