aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-09-29 22:52:26 +0200
committermetamuffin <metamuffin@disroot.org>2025-09-29 22:53:12 +0200
commit66d60ed9ab61efc176808b17fc26445dbf5be705 (patch)
tree7774a7625948659c037514e0848ddd3c292fcc54
parentc1e826625f3ca3116d482b27e2f0b230cbd55346 (diff)
downloadhurrycurry-66d60ed9ab61efc176808b17fc26445dbf5be705.tar
hurrycurry-66d60ed9ab61efc176808b17fc26445dbf5be705.tar.bz2
hurrycurry-66d60ed9ab61efc176808b17fc26445dbf5be705.tar.zst
Implement ready packet + Refactor server pausing
-rw-r--r--locale/en.ini3
-rw-r--r--server/src/commands.rs21
-rw-r--r--server/src/main.rs18
-rw-r--r--server/src/server.rs6
-rw-r--r--server/src/state.rs100
5 files changed, 104 insertions, 44 deletions
diff --git a/locale/en.ini b/locale/en.ini
index 55dcdde5..b5b3849b 100644
--- a/locale/en.ini
+++ b/locale/en.ini
@@ -281,7 +281,8 @@ 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.state.paused.all_idle=Game paused
+s.state.paused.any_not_ready=Waiting for {0} players...
s.tutorial.accept_order=Approach the customer take their order
s.tutorial.active_cutting_board=Cut the item to slices here
s.tutorial.active=Interact here for {0}s
diff --git a/server/src/commands.rs b/server/src/commands.rs
index 8e3d926a..1ffe4cd1 100644
--- a/server/src/commands.rs
+++ b/server/src/commands.rs
@@ -78,6 +78,13 @@ enum Command {
Tile { name: String },
/// Show all possible demands for this map
Demands,
+ /// Ready yourself
+ Ready {
+ #[arg(short, long)]
+ force: bool,
+ #[arg(short, long)]
+ unready: bool,
+ },
/// Reload the resource index
ReloadIndex,
#[clap(alias = "summon", alias = "bot")]
@@ -167,9 +174,10 @@ impl Server {
.map_err(|e| TrError::Plain(e.to_string()))?;
self.load(data, timer.map(Duration::from_secs));
if !skip_announce {
- self.start_pause_timer = 3.5;
+ self.announce_timer = 3.5;
self.packet_out
.push_back(PacketC::Menu(Menu::AnnounceStart));
+ self.update_paused();
}
}
Command::End => {
@@ -206,6 +214,17 @@ impl Server {
None,
);
}
+ Command::Ready { force, unready } => {
+ for c in self.connections.values_mut() {
+ if c.players.contains(&player) || force {
+ c.ready = true;
+ }
+ if unready && c.players.contains(&player) {
+ c.ready = false;
+ }
+ }
+ self.update_paused();
+ }
Command::ReloadIndex => {
self.index
.reload()
diff --git a/server/src/main.rs b/server/src/main.rs
index c6b0d59f..2c47db7d 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -18,12 +18,8 @@
use anyhow::{bail, Result};
use clap::Parser;
use futures_util::{SinkExt, StreamExt};
-use hurrycurry_protocol::{PacketC, PacketS, VERSION};
-use hurrycurry_server::{
- data::DATA_DIR,
- server::{GameServerExt, Server},
- trm, ConnectionID,
-};
+use hurrycurry_protocol::{PacketC, PacketS};
+use hurrycurry_server::{data::DATA_DIR, server::Server, trm, ConnectionID};
use log::{debug, info, trace, warn, LevelFilter};
use std::{
env::var, net::SocketAddr, path::PathBuf, process::exit, str::FromStr, sync::Arc,
@@ -209,15 +205,7 @@ async fn run(args: Args) -> anyhow::Result<()> {
let mut rx = rx.resubscribe();
let (error_tx, mut error_rx) = channel::<PacketC>(8);
- let mut init = state.write().await.game.prime_client();
- init.insert(
- 0,
- PacketC::Version {
- major: VERSION.0,
- minor: VERSION.1,
- supports_bincode: true,
- },
- );
+ let init = state.write().await.connect(id).await;
// let supports_binary = Arc::new(AtomicBool::new(false));
// let supports_binary2 = supports_binary.clone();
diff --git a/server/src/server.rs b/server/src/server.rs
index a1a849c4..22ad5d23 100644
--- a/server/src/server.rs
+++ b/server/src/server.rs
@@ -51,7 +51,7 @@ pub struct Server {
pub tx: Sender<PacketC>,
pub connections: HashMap<ConnectionID, ConnectionData>,
pub paused: bool,
- pub start_pause_timer: f32,
+ pub announce_timer: f32,
pub game: Game,
@@ -326,7 +326,7 @@ impl Server {
game: Game::default(),
index: DataIndex::load().context("Failed to load data index")?,
tx,
- start_pause_timer: 0.,
+ announce_timer: 0.,
packet_out: VecDeque::new(),
connections: HashMap::new(),
data: Serverdata::default().into(),
@@ -385,6 +385,8 @@ impl Server {
dt: 0.,
});
}
+ self.connections.values_mut().for_each(|c| c.ready = false);
+ self.update_paused();
}
pub fn packet_in(
diff --git a/server/src/state.rs b/server/src/state.rs
index b4a8ce1f..1d2af247 100644
--- a/server/src/state.rs
+++ b/server/src/state.rs
@@ -15,33 +15,20 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::{message::TrError, server::Server, tre, trm, ConnectionID};
+use crate::{
+ message::TrError,
+ server::{ConnectionData, GameServerExt, Server},
+ tre, trm, ConnectionID,
+};
use anyhow::Result;
-use hurrycurry_protocol::{Message, PacketC, PacketS, PlayerID};
+use hurrycurry_protocol::{Message, PacketC, PacketS, PlayerID, VERSION};
use log::{debug, info, trace};
impl Server {
pub fn tick_outer(&mut self, dt: f32) -> anyhow::Result<()> {
- if self.start_pause_timer > 0. {
- self.start_pause_timer -= dt
- }
- let should_pause = self.start_pause_timer > 0. || self.connections.values().all(|c| c.idle);
+ let should_pause = self.announce_timer > 0.;
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::Pause { state: self.paused });
- self.packet_out.push_back(PacketC::ServerHint {
- position: None,
- message: if self.paused && self.start_pause_timer <= 0. {
- Some(trm!("s.state.paused"))
- } else {
- None
- },
- player: *p,
- });
- }
}
if !self.paused {
let r = self.tick(dt);
@@ -49,7 +36,13 @@ impl Server {
self.scoreboard.save()?;
self.load(self.index.generate_with_book(&name)?, timer);
}
+ } else if self.announce_timer > 0. {
+ self.announce_timer -= dt;
+ if self.announce_timer <= 0. {
+ self.update_paused();
+ }
}
+
while let Some(p) = self.packet_out.pop_front() {
if matches!(p, PacketC::UpdateMap { .. } | PacketC::Movement { .. }) {
trace!("-> {p:?}");
@@ -60,6 +53,32 @@ impl Server {
}
Ok(())
}
+
+ pub async fn connect(&mut self, id: ConnectionID) -> Vec<PacketC> {
+ let mut init = self.game.prime_client();
+ init.insert(
+ 0,
+ PacketC::Version {
+ major: VERSION.0,
+ minor: VERSION.1,
+ supports_bincode: true,
+ },
+ );
+ self.connections.insert(id, ConnectionData::default());
+ self.update_paused();
+ init
+ }
+
+ pub async fn disconnect(&mut self, conn: ConnectionID) {
+ if let Some(cd) = self.connections.get(&conn) {
+ for player in cd.players.clone() {
+ let _ = self.packet_in_outer(conn, PacketS::Leave { player }).await;
+ }
+ }
+ self.connections.remove(&conn);
+ self.update_paused();
+ }
+
pub async fn packet_in_outer(
&mut self,
conn: ConnectionID,
@@ -96,9 +115,11 @@ impl Server {
}
PacketS::Ready => {
self.connections.entry(conn).or_default().ready = true;
+ self.update_paused();
}
PacketS::Idle { paused } => {
self.connections.entry(conn).or_default().idle = *paused;
+ self.update_paused();
}
PacketS::Leave { player } => {
self.connections
@@ -143,13 +164,42 @@ impl Server {
Ok(replies)
}
- pub async fn disconnect(&mut self, conn: ConnectionID) {
- if let Some(cd) = self.connections.get(&conn) {
- for player in cd.players.clone() {
- let _ = self.packet_in_outer(conn, PacketS::Leave { player }).await;
+ pub fn update_paused(&mut self) {
+ let all_idle = self.connections.values().all(|c| c.idle);
+ let not_ready = self.connections.values().filter(|c| !c.ready).count();
+ let announcing = self.announce_timer > 0.;
+
+ let should_pause = all_idle || not_ready > 0 || announcing;
+
+ let reason = if not_ready > 0 {
+ info!("Game paused: {not_ready} player are not ready");
+ Some(trm!(
+ "s.state.paused.any_not_ready",
+ s = not_ready.to_string()
+ ))
+ } else if all_idle {
+ info!("Game paused: All players idle");
+ Some(trm!("s.state.paused.all_idle"))
+ } else if announcing {
+ info!("Game paused: Waiting for announcement");
+ None
+ } else {
+ if self.paused {
+ info!("Game unpaused");
}
+ None
+ };
+ self.paused = should_pause;
+
+ for p in self.game.players.keys() {
+ self.packet_out
+ .push_back(PacketC::Pause { state: self.paused });
+ self.packet_out.push_back(PacketC::ServerHint {
+ position: None,
+ message: reason.clone(),
+ player: *p,
+ });
}
- self.connections.remove(&conn);
}
}