diff options
author | metamuffin <metamuffin@disroot.org> | 2024-07-01 16:24:21 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-07-01 16:24:21 +0200 |
commit | cd41deaf64d5b5f4f6e2488396fbf29778c4f91c (patch) | |
tree | 70691e65bbefae8b48f2869c21daac2f6a5f0199 | |
parent | 17024786036868b66a86caaf967a65d77dc2bd15 (diff) | |
download | hurrycurry-cd41deaf64d5b5f4f6e2488396fbf29778c4f91c.tar hurrycurry-cd41deaf64d5b5f4f6e2488396fbf29778c4f91c.tar.bz2 hurrycurry-cd41deaf64d5b5f4f6e2488396fbf29778c4f91c.tar.zst |
game ends with timer
-rw-r--r-- | server/src/game.rs | 26 | ||||
-rw-r--r-- | server/src/main.rs | 4 | ||||
-rw-r--r-- | server/src/protocol.rs | 1 | ||||
-rw-r--r-- | server/src/state.rs | 25 | ||||
-rw-r--r-- | test-client/main.ts | 4 | ||||
-rw-r--r-- | test-client/protocol.ts | 2 | ||||
-rw-r--r-- | test-client/visual.ts | 5 |
7 files changed, 48 insertions, 19 deletions
diff --git a/server/src/game.rs b/server/src/game.rs index 96cfa93a..599aadbd 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -28,7 +28,7 @@ use std::{ collections::{HashMap, VecDeque}, ops::Deref, sync::Arc, - time::Instant, + time::{Duration, Instant}, }; #[derive(Debug, PartialEq)] @@ -65,7 +65,8 @@ pub struct Game { players: HashMap<PlayerID, Player>, packet_out: VecDeque<PacketC>, demand: Option<DemandState>, - points: i64, + pub points: i64, + end: Option<Instant>, } impl Game { @@ -76,6 +77,7 @@ impl Game { players: Default::default(), tiles: Default::default(), demand: None, + end: None, points: 0, } } @@ -95,7 +97,7 @@ impl Game { } self.demand = None; } - pub fn load(&mut self, gamedata: Gamedata) { + pub fn load(&mut self, gamedata: Gamedata, timer: Option<Duration>) { let players = self .players .iter() @@ -107,6 +109,7 @@ impl Game { self.data = gamedata.into(); self.points = 0; + self.end = timer.map(|dur| Instant::now() + dur); for (&p, (tile, item)) in &self.data.initial_map { self.tiles.insert( @@ -201,6 +204,7 @@ impl Game { 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.demand.as_ref().map(|d| d.failed).unwrap_or_default(), demands_completed: self @@ -375,6 +379,9 @@ impl Game { } self.packet_out.push_back({ PacketC::Score { + time_remaining: self + .end + .map(|t| (t - Instant::now()).as_secs_f32()), points: self.points, demands_failed: self .demand @@ -423,7 +430,8 @@ impl Game { Ok(()) } - pub fn tick(&mut self, dt: f32) { + /// Returns true if the game should end + pub fn tick(&mut self, dt: f32) -> bool { if let Some(demand) = &mut self.demand { let mut packet_out = Vec::new(); if let Err(err) = demand.tick( @@ -436,12 +444,8 @@ impl Game { warn!("demand tick {err}"); } if demand.score_changed { - self.packet_out.push_back(PacketC::Score { - points: self.points, - demands_failed: demand.failed, - demands_completed: demand.completed, - }); - demand.score_changed = false + demand.score_changed = false; + self.packet_out.push_back(self.score()); } for (player, packet) in packet_out { if let Err(err) = self.packet_in(player, packet) { @@ -473,6 +477,8 @@ impl Game { } } } + + return self.end.map(|t| t < Instant::now()).unwrap_or_default(); } } diff --git a/server/src/main.rs b/server/src/main.rs index 68927eaa..a144c704 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -90,7 +90,9 @@ async fn run() -> anyhow::Result<()> { let mut tick = interval(Duration::from_secs_f32(dt)); loop { tick.tick().await; - state.write().await.tick(dt).await; + if let Err(e) = state.write().await.tick(dt).await { + warn!("tick failed: {e}"); + } } }); } diff --git a/server/src/protocol.rs b/server/src/protocol.rs index 39bc0887..176eb31b 100644 --- a/server/src/protocol.rs +++ b/server/src/protocol.rs @@ -141,6 +141,7 @@ pub enum PacketC { points: i64, demands_failed: usize, demands_completed: usize, + time_remaining: Option<f32>, }, SetIngame { state: bool, diff --git a/server/src/state.rs b/server/src/state.rs index 41ffce20..ffcc8fa2 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use crate::{ data::DataIndex, game::Game, @@ -20,6 +22,8 @@ enum Command { Start { #[arg(default_value = "small-default-default")] spec: String, + #[arg(default_value = "300")] + timer: u64, }, Effect { name: String, @@ -33,17 +37,26 @@ impl State { index.reload()?; let mut game = Game::new(); - game.load(index.generate("lobby-none-none".to_string())?); + game.load(index.generate("lobby-none-none".to_string())?, None); Ok(Self { game, index, tx }) } - pub async fn tick(&mut self, dt: f32) { - self.game.tick(dt); + 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-none".to_string())?, None); + } while let Some(p) = self.game.packet_out() { debug!("-> {p:?}"); let _ = self.tx.send(p); } + Ok(()) } pub async fn packet_in(&mut self, player: PlayerID, packet: PacketS) -> Result<Vec<PacketC>> { match &packet { @@ -81,13 +94,13 @@ impl State { async fn handle_command(&mut self, player: PlayerID, command: Command) -> Result<()> { match command { - Command::Start { spec } => { + Command::Start { spec, timer } => { let data = self.index.generate(spec)?; - self.game.load(data); + self.game.load(data, Some(Duration::from_secs(timer))); } Command::End => { self.game - .load(self.index.generate("lobby-none-none".to_string())?); + .load(self.index.generate("lobby-none-none".to_string())?, None); } Command::Effect { name } => { self.tx diff --git a/test-client/main.ts b/test-client/main.ts index 38b9e730..aeca8084 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -99,6 +99,7 @@ export const items_removed = new Set<ItemData>() export let data: Gamedata = { item_names: [], tile_names: [], spawn: [0, 0], tile_collide: [], tile_interact: [] } +export let time_remaining: number | null = null export let global_message: MessageData | undefined = undefined let global_message_clear: number | undefined = undefined export let my_id: PlayerID = -1 @@ -204,6 +205,7 @@ function packet(p: PacketC) { demands_completed = p.demands_completed demands_failed = p.demands_failed points = p.points + time_remaining = p.time_remaining ?? null break; case "error": if (global_message_clear) clearTimeout(global_message_clear) @@ -288,6 +290,8 @@ function frame_update(dt: number) { const p = players.get(my_id) if (!p) return + if (time_remaining != null) time_remaining -= dt + const input = normalize({ x: (+keys_down.has(KEY_RIGHT) - +keys_down.has(KEY_LEFT)), y: (+keys_down.has(KEY_DOWN) - +keys_down.has(KEY_UP)) diff --git a/test-client/protocol.ts b/test-client/protocol.ts index 81a0d1a4..1e76700a 100644 --- a/test-client/protocol.ts +++ b/test-client/protocol.ts @@ -49,7 +49,7 @@ 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, } // Supplies information for score OSD + | { type: "score", points: number, demands_failed: number, demands_completed: number, time_remaining?: number } // Supplies information for score OSD | { type: "set_ingame", state: boolean } // Set to false when entering the game or switching maps | { type: "error", message: string } // Your client did something wrong. diff --git a/test-client/visual.ts b/test-client/visual.ts index e966b5cf..dd676435 100644 --- a/test-client/visual.ts +++ b/test-client/visual.ts @@ -15,7 +15,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { ItemData, MessageData, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, demands_completed, demands_failed, get_interact_target, global_message, interact_active_anim, interact_possible_anim, interact_target_anim, items_removed, keys_down, my_id, players, points, tiles } from "./main.ts"; +import { ItemData, MessageData, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, demands_completed, demands_failed, get_interact_target, global_message, interact_active_anim, interact_possible_anim, interact_target_anim, items_removed, keys_down, my_id, players, points, tiles, time_remaining } from "./main.ts"; import { PLAYER_SIZE } from "./movement.ts"; import { FALLBACK_TILE, ITEMS, TILES, FALLBACK_ITEM } from "./tiles.ts"; import { V2, ceil_v2, floor_v2 } from "./util.ts"; @@ -79,6 +79,9 @@ export function draw_ingame() { ctx.fillStyle = "white" ctx.textAlign = "left" ctx.textBaseline = "bottom" + ctx.font = "20px sans-serif" + if (time_remaining != undefined) + ctx.fillText(`Time remaining: ${time_remaining?.toFixed(2)}`, 10, canvas.height - 90) ctx.font = "30px sans-serif" ctx.fillText(`Points: ${points}`, 10, canvas.height - 60) ctx.font = "20px sans-serif" |