From 1826a2b4f9dd78f2f7841c48f287927de98d7173 Mon Sep 17 00:00:00 2001 From: timokoesters Date: Thu, 23 May 2019 17:14:39 +0200 Subject: [PATCH] Implement fast respawn Former-commit-id: 17f9aceba5c40a5d3ddc2e0fd6795479487753fd --- common/src/comp/inputs.rs | 1 + common/src/comp/mod.rs | 1 + common/src/comp/player.rs | 9 ++- common/src/msg/mod.rs | 1 + common/src/state.rs | 3 +- common/src/sys/inputs.rs | 39 +++++----- server/src/lib.rs | 137 +++++++++++++++++++----------------- voxygen/src/scene/figure.rs | 35 +++++---- voxygen/src/session.rs | 47 ++++++++----- 9 files changed, 154 insertions(+), 119 deletions(-) diff --git a/common/src/comp/inputs.rs b/common/src/comp/inputs.rs index b1d3cacc47..b8870c4e86 100644 --- a/common/src/comp/inputs.rs +++ b/common/src/comp/inputs.rs @@ -5,6 +5,7 @@ use vek::*; pub enum InputEvent { Jump, Attack, + Respawn, } #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 9f709c3eb5..d9612e853f 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -18,5 +18,6 @@ pub use inputs::Actions; pub use inputs::InputEvent; pub use inputs::Inputs; pub use player::Player; +pub use player::Respawn; pub use stats::Dying; pub use stats::Stats; diff --git a/common/src/comp/player.rs b/common/src/comp/player.rs index 2c895083e0..4b52c6b529 100644 --- a/common/src/comp/player.rs +++ b/common/src/comp/player.rs @@ -1,4 +1,4 @@ -use specs::{Component, FlaggedStorage, VecStorage}; +use specs::{Component, NullStorage, FlaggedStorage, VecStorage}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Player { @@ -18,3 +18,10 @@ impl Player { impl Component for Player { type Storage = FlaggedStorage>; } + + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct Respawn; +impl Component for Respawn { + type Storage = NullStorage; +} diff --git a/common/src/msg/mod.rs b/common/src/msg/mod.rs index a5a94e89a3..84e653e9f6 100644 --- a/common/src/msg/mod.rs +++ b/common/src/msg/mod.rs @@ -12,5 +12,6 @@ pub enum ClientState { Connected, Registered, Spectator, + Dead, Character, } diff --git a/common/src/state.rs b/common/src/state.rs index d88f7f3c92..7e330ea419 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -103,6 +103,7 @@ impl State { ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); + ecs.register_synced::(); ecs.register::(); // Register unsynced (or synced by other means) components. @@ -111,8 +112,8 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); - ecs.register::(); ecs.register::(); + ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/common/src/sys/inputs.rs b/common/src/sys/inputs.rs index 2d107cd4fb..55c6d4f056 100644 --- a/common/src/sys/inputs.rs +++ b/common/src/sys/inputs.rs @@ -6,7 +6,7 @@ use vek::*; use crate::{ comp::{ phys::{Dir, ForceUpdate, Pos, Vel}, - Actions, Animation, AnimationInfo, InputEvent, Inputs, Stats, + Actions, Animation, AnimationInfo, Respawn, InputEvent, Inputs, Stats, }, state::{DeltaTime, Time}, terrain::TerrainMap, @@ -29,6 +29,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Dir>, WriteStorage<'a, AnimationInfo>, WriteStorage<'a, Stats>, + WriteStorage<'a, Respawn>, WriteStorage<'a, ForceUpdate>, ); @@ -46,6 +47,7 @@ impl<'a> System<'a> for Sys { mut directions, mut animation_infos, mut stats, + mut respawns, mut force_updates, ): Self::SystemData, ) { @@ -133,12 +135,9 @@ impl<'a> System<'a> for Sys { }, ); } - for (entity, inputs, mut action, pos, dir) in ( + for (entity, inputs) in ( &entities, &mut inputs, - &mut actions, - &positions, - &mut directions, ) .join() { @@ -147,24 +146,26 @@ impl<'a> System<'a> for Sys { match event { InputEvent::Attack => { // Attack delay - if action.attack_time.is_some() { - continue; - } - for (b, pos_b, mut stat_b, mut vel_b) in - (&entities, &positions, &mut stats, &mut velocities).join() - { - if entity != b - && pos.0.distance_squared(pos_b.0) < 50.0 - && dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0 + if let (Some(pos), Some(dir), Some(action)) = (positions.get(entity), directions.get(entity), actions.get_mut(entity)) { + for (b, pos_b, mut stat_b, mut vel_b) in + (&entities, &positions, &mut stats, &mut velocities).join() { - action.attack_time = Some(0.0); - stat_b.hp.change_by(-10); // TODO: variable damage - vel_b.0 += (pos_b.0 - pos.0).normalized() * 20.0; - vel_b.0.z = 20.0; - force_updates.insert(b, ForceUpdate); + if entity != b + && pos.0.distance_squared(pos_b.0) < 50.0 + && dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0 + { + action.attack_time = Some(0.0); + stat_b.hp.change_by(-10); // TODO: variable damage + vel_b.0 += (pos_b.0 - pos.0).normalized() * 20.0; + vel_b.0.z = 20.0; + force_updates.insert(b, ForceUpdate); + } } } } + InputEvent::Respawn => { + respawns.insert(entity, Respawn); + } InputEvent::Jump => {} } } diff --git a/server/src/lib.rs b/server/src/lib.rs index b5d80fb3e2..2f61b30d25 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -200,6 +200,52 @@ impl Server { // Tick the world self.world.tick(dt); + // Sync deaths. + let todo_kill = ( + &self.state.ecs().entities(), + &self.state.ecs().read_storage::(), + ) + .join() + .map(|(entity, _)| entity) + .collect::>(); + + for entity in todo_kill { + self.state + .ecs_mut() + .write_storage::() + .remove(entity); + self.state + .ecs_mut() + .write_storage::() + .remove(entity); + if let Some(client) = self.clients.get_mut(&entity) { + client.force_state(ClientState::Dead); + } else { + //self.state.ecs_mut().delete_entity_synced(entity); + } + } + + // Handle respawns + let todo_respawn = ( + &self.state.ecs().entities(), + &self.state.ecs().read_storage::(), + ) + .join() + .map(|(entity, _)| entity) + .collect::>(); + + for entity in todo_respawn { + if let Some(client) = self.clients.get_mut(&entity) { + client.allow_state(ClientState::Character); + self.state.ecs_mut().write_storage::().remove(entity); + self.state.write_component(entity, comp::Stats::default()); + self.state.write_component(entity, comp::Actions::default()); + self.state.write_component(entity, comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0))); + self.state.write_component(entity, comp::phys::Vel(Vec3::zero())); + self.state.write_component(entity, comp::phys::ForceUpdate); + } + } + // 5) Fetch any generated `TerrainChunk`s and insert them into the terrain. // Also, send the chunk data to anybody that is close by. if let Ok((key, chunk)) = self.chunk_rx.try_recv() { @@ -331,9 +377,9 @@ impl Server { ClientState::Registered => { client.error_state(RequestStateError::Already) } - ClientState::Spectator | ClientState::Character => { - client.allow_state(ClientState::Registered) - } + ClientState::Spectator + | ClientState::Character + | ClientState::Dead => client.allow_state(ClientState::Registered), }, ClientState::Spectator => match requested_state { // Become Registered first. @@ -343,14 +389,15 @@ impl Server { ClientState::Spectator => { client.error_state(RequestStateError::Already) } - ClientState::Registered | ClientState::Character => { - client.allow_state(ClientState::Spectator) - } + ClientState::Registered + | ClientState::Character + | ClientState::Dead => client.allow_state(ClientState::Spectator), }, // Use ClientMsg::Character instead. ClientState::Character => { client.error_state(RequestStateError::WrongMessage) } + ClientState::Dead => client.error_state(RequestStateError::Impossible), }, ClientMsg::Register { player } => match client.client_state { ClientState::Connected => { @@ -374,7 +421,9 @@ impl Server { ClientState::Connected => { client.error_state(RequestStateError::Impossible) } - ClientState::Registered | ClientState::Spectator => { + ClientState::Registered + | ClientState::Spectator + | ClientState::Dead => { Self::create_player_character(state, entity, client, name, body) } ClientState::Character => { @@ -387,17 +436,21 @@ impl Server { } ClientState::Registered | ClientState::Spectator + | ClientState::Dead | ClientState::Character => new_chat_msgs.push((entity, msg)), }, - ClientMsg::PlayerInputs(mut inputs) => { - state - .ecs_mut() - .write_storage::() - .get_mut(entity) - .map(|s| { - s.events.append(&mut inputs.events); - }); - } + ClientMsg::PlayerInputs(mut inputs) => match client.client_state { + ClientState::Character | ClientState::Dead => { + state + .ecs_mut() + .write_storage::() + .get_mut(entity) + .map(|s| { + s.events.append(&mut inputs.events); + }); + } + _ => client.error_state(RequestStateError::Impossible), + }, ClientMsg::PlayerAnimation(animation_info) => { match client.client_state { ClientState::Character => { @@ -417,7 +470,9 @@ impl Server { _ => client.error_state(RequestStateError::Impossible), }, ClientMsg::TerrainChunkRequest { key } => match client.client_state { - ClientState::Connected | ClientState::Registered => { + ClientState::Connected + | ClientState::Registered + | ClientState::Dead => { client.error_state(RequestStateError::Impossible); } ClientState::Spectator | ClientState::Character => { @@ -571,58 +626,12 @@ impl Server { } } - // Sync deaths. - let todo_kill = ( - &self.state.ecs().entities(), - &self.state.ecs().read_storage::(), - ) - .join() - .map(|(entity, _)| entity) - .collect::>(); - - for entity in todo_kill { - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - if let Some(client) = self.clients.get_mut(&entity) { - client.force_state(ClientState::Registered); - } - } - // Remove all force flags. self.state .ecs_mut() .write_storage::() .clear(); + } pub fn generate_chunk(&mut self, key: Vec2) { diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index 5e3bb54861..2b0d28273e 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -12,6 +12,7 @@ use crate::{ }; use client::Client; use common::{ + msg::ClientState, assets, comp::{ self, @@ -457,22 +458,26 @@ impl FigureMgr { let tick = client.get_tick(); let ecs = client.state().ecs(); - for (entity, actor) in (&ecs.entities(), &ecs.read_storage::()).join() { - match actor { - comp::Actor::Character { body, .. } => { - if let Some((locals, bone_consts)) = match body { - Body::Humanoid(_) => self - .character_states - .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), - Body::Quadruped(_) => self - .quadruped_states - .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), - } { - let model = self.model_cache.get_or_create_model(renderer, *body, tick); + for (entity, actor, _) in (&ecs.entities(), &ecs.read_storage::(), &ecs.read_storage::()).join() { + // Check if player is alive + + { + match actor { + comp::Actor::Character { body, .. } => { + if let Some((locals, bone_consts)) = match body { + Body::Humanoid(_) => self + .character_states + .get(&entity) + .map(|state| (state.locals(), state.bone_consts())), + Body::Quadruped(_) => self + .quadruped_states + .get(&entity) + .map(|state| (state.locals(), state.bone_consts())), + } { + let model = self.model_cache.get_or_create_model(renderer, *body, tick); - renderer.render_figure(model, globals, locals, bone_consts); + renderer.render_figure(model, globals, locals, bone_consts); + } } } } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 648b79aa22..5c352c23ef 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -8,7 +8,7 @@ use crate::{ Direction, Error, GlobalState, PlayState, PlayStateResult, }; use client::{self, Client}; -use common::{comp, clock::Clock, msg::ClientState}; +use common::{clock::Clock, comp, msg::ClientState}; use glutin::MouseButton; use std::{cell::RefCell, mem, rc::Rc, time::Duration}; use vek::*; @@ -131,44 +131,53 @@ impl PlayState for SessionState { // Game loop while self.client.borrow().is_request_pending() || self.client.borrow().get_client_state() == Some(ClientState::Character) + || self.client.borrow().get_client_state() == Some(ClientState::Dead) { + let alive = self.client.borrow().get_client_state() == Some(ClientState::Character); + // Handle window events. for event in global_state.window.fetch_events() { // Pass all events to the ui first. if self.hud.handle_event(event.clone(), global_state) { continue; } - let _handled = match event { + + match event { Event::Close => { return PlayStateResult::Shutdown; } // Attack key pressed - Event::Click(MouseButton::Left, state) => { - self.input_events.push(comp::InputEvent::Attack) + Event::Click(MouseButton::Left, state) => match alive { + true => { + self.input_events.push(comp::InputEvent::Attack); + } + false => { + self.input_events.push(comp::InputEvent::Respawn); + } + _ => unreachable!() } // Movement key pressed - Event::KeyDown(Key::MoveForward) => self.key_state.up = true, - Event::KeyDown(Key::MoveBack) => self.key_state.down = true, - Event::KeyDown(Key::MoveLeft) => self.key_state.left = true, - Event::KeyDown(Key::MoveRight) => self.key_state.right = true, - Event::KeyDown(Key::Jump) => { + Event::KeyDown(Key::MoveForward) if alive => self.key_state.up = true, + Event::KeyDown(Key::MoveBack) if alive => self.key_state.down = true, + Event::KeyDown(Key::MoveLeft) if alive => self.key_state.left = true, + Event::KeyDown(Key::MoveRight) if alive => self.key_state.right = true, + Event::KeyDown(Key::Jump) if alive => { self.input_events.push(comp::InputEvent::Jump); self.key_state.jump = true; } - Event::KeyDown(Key::Glide) => self.key_state.glide = true, + Event::KeyDown(Key::Glide) if alive => self.key_state.glide = true, // Movement key released - Event::KeyUp(Key::MoveForward) => self.key_state.up = false, - Event::KeyUp(Key::MoveBack) => self.key_state.down = false, - Event::KeyUp(Key::MoveLeft) => self.key_state.left = false, - Event::KeyUp(Key::MoveRight) => self.key_state.right = false, - Event::KeyUp(Key::Jump) => self.key_state.jump = false, - Event::KeyUp(Key::Glide) => self.key_state.glide = false, + Event::KeyUp(Key::MoveForward) if alive => self.key_state.up = false, + Event::KeyUp(Key::MoveBack) if alive => self.key_state.down = false, + Event::KeyUp(Key::MoveLeft) if alive => self.key_state.left = false, + Event::KeyUp(Key::MoveRight) if alive => self.key_state.right = false, + Event::KeyUp(Key::Jump) if alive => self.key_state.jump = false, + Event::KeyUp(Key::Glide) if alive => self.key_state.glide = false, // Pass all other events to the scene event => { self.scene.handle_input_event(event); - } - }; - // TODO: Do something if the event wasn't handled? + } // TODO: Do something if the event wasn't handled? + } } // Perform an in-game tick.