diff options
-rw-r--r-- | server/protocol/src/lib.rs | 28 | ||||
-rw-r--r-- | server/src/entity/conveyor.rs | 2 | ||||
-rw-r--r-- | server/src/entity/customers/mod.rs | 8 | ||||
-rw-r--r-- | server/src/entity/portal.rs | 2 | ||||
-rw-r--r-- | server/src/game.rs | 69 | ||||
-rw-r--r-- | server/src/interaction.rs | 10 | ||||
-rw-r--r-- | server/src/state.rs | 7 | ||||
-rw-r--r-- | test-client/protocol.ts | 18 |
8 files changed, 86 insertions, 58 deletions
diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index cf422761..61efd74f 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -193,12 +193,7 @@ pub enum PacketC { ServerMessage { text: String, }, - Score { - points: i64, - demands_failed: usize, - demands_completed: usize, - time_remaining: Option<f32>, - }, + Score(Score), SetIngame { state: bool, lobby: bool, @@ -206,12 +201,33 @@ pub enum PacketC { Error { message: String, }, + Menu(Menu), MovementSync, /// For use in replay sessions only ReplayStart, } +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] +#[serde(rename_all = "snake_case", tag = "type", content = "data")] +pub enum Menu { + Book, + Score(Score), +} + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Default)] +pub struct Score { + pub time_remaining: f64, + pub stars: u8, + pub points: i64, + pub demands_failed: usize, + pub demands_completed: usize, + pub players: usize, + pub active_recipes: usize, + pub passive_recipes: usize, + pub instant_recipes: usize, +} + #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Copy, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] pub enum ItemLocation { diff --git a/server/src/entity/conveyor.rs b/server/src/entity/conveyor.rs index 6067c679..38478db7 100644 --- a/server/src/entity/conveyor.rs +++ b/server/src/entity/conveyor.rs @@ -72,7 +72,7 @@ impl EntityT for Conveyor { ItemLocation::Tile(self.from), Some(to.kind), &mut game.packet_out, - &mut game.points, + &mut game.score, true, ); } diff --git a/server/src/entity/customers/mod.rs b/server/src/entity/customers/mod.rs index 7a8280bc..e8679dc9 100644 --- a/server/src/entity/customers/mod.rs +++ b/server/src/entity/customers/mod.rs @@ -151,8 +151,8 @@ impl EntityT for Customers { ) .expect("no path to exit"); *self.chairs.get_mut(chair).unwrap() = true; - game.demands_failed += 1; - game.points -= 1; + game.score.demands_failed += 1; + game.score.points -= 1; game.score_changed = true; info!("{id:?} -> exiting"); *state = CustomerState::Exiting { path } @@ -232,8 +232,8 @@ impl EntityT for Customers { ) .ok_or(anyhow!("no path to exit"))?; *self.chairs.get_mut(chair).unwrap() = true; - game.demands_completed += 1; - game.points += demand.points; + game.score.demands_completed += 1; + game.score.points += demand.points; game.score_changed = true; info!("{id:?} -> exiting"); *state = CustomerState::Exiting { path } diff --git a/server/src/entity/portal.rs b/server/src/entity/portal.rs index 3aed35ac..2d4a762b 100644 --- a/server/src/entity/portal.rs +++ b/server/src/entity/portal.rs @@ -43,7 +43,7 @@ impl EntityT for Portal { ItemLocation::Tile(self.from), Some(to.kind), &mut game.packet_out, - &mut game.points, + &mut game.score, true, ); } diff --git a/server/src/game.rs b/server/src/game.rs index 9a01e4a3..1f368b00 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -25,8 +25,8 @@ use anyhow::{anyhow, bail, Result}; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, movement::MovementBase, - ClientGamedata, ItemIndex, ItemLocation, Message, PacketC, PacketS, PlayerID, RecipeIndex, - TileIndex, + ClientGamedata, ItemIndex, ItemLocation, Menu, Message, PacketC, PacketS, PlayerID, + RecipeIndex, Score, TileIndex, }; use log::{info, warn}; use std::{ @@ -78,9 +78,7 @@ pub struct Game { pub lobby: bool, pub score_changed: bool, - pub points: i64, - pub demands_failed: usize, - pub demands_completed: usize, + pub score: Score, } impl Default for Game { @@ -101,9 +99,7 @@ impl Game { end: None, entities: Arc::new(RwLock::new(vec![])), players_spatial_index: SpatialIndex::default(), - points: 0, - demands_failed: 0, - demands_completed: 0, + score: Score::default(), score_changed: false, } } @@ -137,7 +133,7 @@ impl Game { self.lobby = gamedata.map_name == "lobby"; self.data = gamedata.into(); - self.points = 0; + self.score = Score::default(); self.end = timer.map(|dur| Instant::now() + dur); self.entities = Arc::new(RwLock::new(self.data.entities.clone())); @@ -187,10 +183,6 @@ impl Game { self.packet_out.extend(self.prime_client()); } - pub fn packet_out(&mut self) -> Option<PacketC> { - self.packet_out.pop_front() - } - pub fn prime_client(&self) -> Vec<PacketC> { let mut out = Vec::new(); out.push(PacketC::Data { @@ -256,7 +248,7 @@ impl Game { }) } } - out.push(self.score()); + out.push(PacketC::Score(self.score.clone())); out.push(PacketC::SetIngame { state: true, lobby: self.lobby, @@ -264,22 +256,12 @@ impl Game { out } - pub fn score(&self) -> PacketC { - PacketC::Score { - time_remaining: self.end.map(|t| (t - Instant::now()).as_secs_f32()), - points: self.points, - demands_failed: self.demands_failed, - demands_completed: self.demands_completed, - } - } pub fn packet_in( &mut self, player: PlayerID, packet: PacketS, replies: &mut Vec<PacketC>, ) -> Result<()> { - let points_before = self.points; - match packet { PacketS::Join { name, character } => { if self.players.contains_key(&player) { @@ -429,7 +411,7 @@ impl Game { ItemLocation::Player(pid), None, &mut self.packet_out, - &mut self.points, + &mut self.score, false, ) } else { @@ -447,7 +429,7 @@ impl Game { ItemLocation::Player(pid), Some(tile.kind), &mut self.packet_out, - &mut self.points, + &mut self.score, false, ) } @@ -481,10 +463,6 @@ impl Game { } PacketS::ReplayTick { .. } => bail!("packet not supported in this session"), } - - if self.points != points_before { - self.packet_out.push_back(self.score()) - } Ok(()) } @@ -492,11 +470,18 @@ impl Game { pub fn tick(&mut self, dt: f32) -> bool { if self.score_changed { self.score_changed = false; - self.packet_out.push_back(self.score()); + self.packet_out + .push_back(PacketC::Score(self.score.clone())); } for (&pos, tile) in &mut self.tiles { - if let Some(effect) = tick_slot(dt, &self.data, Some(tile.kind), &mut tile.item) { + if let Some(effect) = tick_slot( + dt, + &self.data, + Some(tile.kind), + &mut tile.item, + &mut self.score, + ) { match effect { TickEffect::Progress(warn) => self.packet_out.push_back(PacketC::SetProgress { warn, @@ -544,7 +529,8 @@ impl Game { rot: player.movement.rotation, }); - if let Some(effect) = tick_slot(dt, &self.data, None, &mut player.item) { + if let Some(effect) = tick_slot(dt, &self.data, None, &mut player.item, &mut self.score) + { match effect { TickEffect::Progress(warn) => self.packet_out.push_back(PacketC::SetProgress { warn, @@ -591,7 +577,18 @@ impl Game { } } - self.end.map(|t| t < Instant::now()).unwrap_or_default() + if let Some(end) = self.end { + self.score.time_remaining = (end - Instant::now()).as_secs_f64(); + if end < Instant::now() { + self.packet_out + .push_back(PacketC::Menu(Menu::Score(self.score.clone()))); + true + } else { + false + } + } else { + false + } } pub fn count_chefs(&self) -> usize { @@ -617,13 +614,13 @@ pub fn interact_effect( other_loc: ItemLocation, this_tile_kind: Option<TileIndex>, packet_out: &mut VecDeque<PacketC>, - points: &mut i64, + score: &mut Score, automated: bool, ) { let this_had_item = this.is_some(); let other_had_item = other.is_some(); - if let Some(effect) = interact(data, edge, this_tile_kind, this, other, points, automated) { + if let Some(effect) = interact(data, edge, this_tile_kind, this, other, score, automated) { match effect { InteractEffect::Put => { info!("put {this_loc} <- {other_loc}"); diff --git a/server/src/interaction.rs b/server/src/interaction.rs index 2f6c940a..71125ac4 100644 --- a/server/src/interaction.rs +++ b/server/src/interaction.rs @@ -19,7 +19,7 @@ use crate::{ data::Gamedata, game::{Involvement, Item}, }; -use hurrycurry_protocol::{ItemIndex, TileIndex}; +use hurrycurry_protocol::{ItemIndex, Score, TileIndex}; use log::info; use serde::{Deserialize, Serialize}; @@ -115,7 +115,7 @@ pub fn interact( tile: Option<TileIndex>, this: &mut Option<Item>, other: &mut Option<Item>, - points: &mut i64, + score: &mut Score, automated: bool, ) -> Option<InteractEffect> { let interactable = automated @@ -180,6 +180,7 @@ pub fn interact( }); } *this = Some(item); + score.active_recipes += 1; return Some(InteractEffect::Put); } } @@ -200,7 +201,8 @@ pub fn interact( let ok_rev = ok_rev as usize; *other = outputs[1 - ok_rev].map(|kind| Item { kind, active: None }); *this = outputs[ok_rev].map(|kind| Item { kind, active: None }); - *points += pd; + score.points += pd; + score.instant_recipes += 1; return Some(InteractEffect::Produce); } } @@ -234,6 +236,7 @@ pub fn tick_slot( data: &Gamedata, tile: Option<TileIndex>, slot: &mut Option<Item>, + score: &mut Score, ) -> Option<TickEffect> { if let Some(item) = slot { if let Some(a) = &mut item.active { @@ -246,6 +249,7 @@ pub fn tick_slot( if a.progress >= 1. { if let Recipe::Passive { output, .. } = &data.recipe(a.recipe) { *slot = output.map(|kind| Item { kind, active: None }); + score.passive_recipes += 1; return Some(TickEffect::Produce); }; a.progress = 1.; diff --git a/server/src/state.rs b/server/src/state.rs index 61795da4..3492e6e7 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -82,15 +82,10 @@ impl State { pub async fn tick(&mut self, dt: f32) -> anyhow::Result<()> { if self.game.tick(dt) { - self.tx - .send(PacketC::ServerMessage { - text: format!("Game finished. You reached {} points.", self.game.points), - }) - .ok(); self.game .load(self.index.generate("lobby-none".to_string()).await?, None); } - while let Some(p) = self.game.packet_out() { + while let Some(p) = self.game.packet_out.pop_front() { if matches!(p, PacketC::UpdateMap { .. } | PacketC::Position { .. }) { trace!("-> {p:?}"); } else { diff --git a/test-client/protocol.ts b/test-client/protocol.ts index 2a0f2b87..940c8cdc 100644 --- a/test-client/protocol.ts +++ b/test-client/protocol.ts @@ -56,10 +56,26 @@ export type PacketC = | { type: "update_map", tile: Vec2, kind: TileIndex | null, neighbors: [TileIndex | null] } // A map tile was changed | { type: "communicate", player: PlayerID, message?: Message, persist: boolean } // A player wants to communicate something, message is null when cleared | { type: "server_message", text: string } // Text message from the server - | { type: "score", points: number, demands_failed: number, demands_completed: number, time_remaining?: number } // Supplies information for score OSD + | { type: "score" } & Score // Supplies information for score OSD + | { type: "menu" } & Menu // Open a menu on the client-side | { type: "set_ingame", state: boolean, lobby: boolean } // Set to false when entering the game or switching maps | { type: "error", message: string } // Your client did something wrong. +export type Menu = + { menu: "book" } + | { menu: "score" } & Score + +export interface Score { + points: number, + demands_failed: number, + demands_completed: number, + time_remaining: number, + players: number, + active_recipes: number, + passive_recipes: number, + instant_recipes: number, +} + export type Message = { item: number } | { text: string } |