diff --git a/client/src/lib.rs b/client/src/lib.rs index c043cc0df9..01abe759ef 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -60,7 +60,7 @@ impl Client { let mut postbox = PostBox::to(addr)?; // Wait for initial sync - let (state, entity, server_info) = match postbox.next_message() { + let (mut state, entity, server_info) = match postbox.next_message() { Some(ServerMsg::InitialSync { ecs_state, entity_uid, @@ -84,6 +84,12 @@ impl Client { // We reduce the thread count by 1 to keep rendering smooth thread_pool.set_num_threads((thread_pool.max_count() - 1).max(1)); + // Set client-only components + state + .ecs_mut() + .write_storage() + .insert(entity, comp::AnimationInfo::default()); + Ok(Self { client_state, thread_pool, @@ -304,16 +310,18 @@ impl Client { } // 6) Update the server about the player's physics attributes. - match ( - self.state.read_storage().get(self.entity).cloned(), - self.state.read_storage().get(self.entity).cloned(), - self.state.read_storage().get(self.entity).cloned(), - ) { - (Some(pos), Some(vel), Some(ori)) => { - self.postbox - .send_message(ClientMsg::PlayerPhysics { pos, vel, ori }); + if let ClientState::Character = self.client_state { + match ( + self.state.read_storage().get(self.entity).cloned(), + self.state.read_storage().get(self.entity).cloned(), + self.state.read_storage().get(self.entity).cloned(), + ) { + (Some(pos), Some(vel), Some(ori)) => { + self.postbox + .send_message(ClientMsg::PlayerPhysics { pos, vel, ori }); + } + _ => {} } - _ => {} } // Output debug metrics @@ -368,20 +376,13 @@ impl Client { pos, vel, ori, + action_state, } => match self.state.ecs().entity_from_uid(entity) { Some(entity) => { self.state.write_component(entity, pos); self.state.write_component(entity, vel); self.state.write_component(entity, ori); - } - None => {} - }, - ServerMsg::EntityAnimation { - entity, - animation_info, - } => match self.state.ecs().entity_from_uid(entity) { - Some(entity) => { - self.state.write_component(entity, animation_info); + self.state.write_component(entity, action_state); } None => {} }, @@ -393,7 +394,7 @@ impl Client { self.client_state = state; } ServerMsg::StateAnswer(Err((error, state))) => { - warn!("{:?}", error); + warn!("StateAnswer: {:?}", error); } ServerMsg::ForceState(state) => { self.client_state = state; diff --git a/common/src/comp/action_state.rs b/common/src/comp/action_state.rs new file mode 100644 index 0000000000..7296e1031f --- /dev/null +++ b/common/src/comp/action_state.rs @@ -0,0 +1,26 @@ +use specs::{Component, FlaggedStorage, NullStorage, VecStorage}; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct ActionState { + pub moving: bool, + pub on_ground: bool, + pub attacking: bool, + pub rolling: bool, + pub gliding: bool, +} + +impl Default for ActionState { + fn default() -> Self { + Self { + moving: false, + on_ground: false, + attacking: false, + rolling: false, + gliding: false, + } + } +} + +impl Component for ActionState { + type Storage = FlaggedStorage>; +} diff --git a/common/src/comp/animation.rs b/common/src/comp/animation.rs index d3e89dc18c..e59a506148 100644 --- a/common/src/comp/animation.rs +++ b/common/src/comp/animation.rs @@ -16,7 +16,6 @@ pub enum Animation { pub struct AnimationInfo { pub animation: Animation, pub time: f64, - pub changed: bool, } impl Default for AnimationInfo { @@ -24,7 +23,6 @@ impl Default for AnimationInfo { Self { animation: Animation::Idle, time: 0.0, - changed: true, } } } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 4f0b3a6477..fee0d63f16 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -1,3 +1,4 @@ +mod action_state; mod agent; mod animation; mod body; @@ -9,6 +10,7 @@ mod player; mod stats; // Reexports +pub use action_state::ActionState; pub use agent::Agent; pub use animation::{Animation, AnimationInfo}; pub use body::{humanoid, quadruped, quadruped_medium, Actor, Body}; diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 841b0d5a8e..1c533cfd0e 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -23,9 +23,6 @@ sphynx::sum_type! { Actor(comp::Actor), Player(comp::Player), Stats(comp::Stats), - Attacking(comp::Attacking), - Rolling(comp::Rolling), - Gliding(comp::Gliding), } } // Automatically derive From for EcsCompPhantom @@ -39,9 +36,6 @@ sphynx::sum_type! { Actor(PhantomData), Player(PhantomData), Stats(PhantomData), - Attacking(PhantomData), - Rolling(PhantomData), - Gliding(PhantomData), } } impl sphynx::CompPacket for EcsCompPacket { diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index f3604fb839..0fadce7afa 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -35,10 +35,7 @@ pub enum ServerMsg { pos: comp::Pos, vel: comp::Vel, ori: comp::Ori, - }, - EntityAnimation { - entity: u64, - animation_info: comp::AnimationInfo, + action_state: comp::ActionState, }, TerrainChunkUpdate { key: Vec2, diff --git a/common/src/state.rs b/common/src/state.rs index 919c9009f8..fa25027980 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -102,9 +102,6 @@ impl State { ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); - ecs.register_synced::(); - ecs.register_synced::(); - ecs.register_synced::(); // Register components synced by other means ecs.register::(); @@ -112,10 +109,14 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); - ecs.register::(); ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); // Register client-local components + ecs.register::(); ecs.register::(); // Register server-local components diff --git a/common/src/sys/action_state.rs b/common/src/sys/action_state.rs new file mode 100644 index 0000000000..08c1a3d514 --- /dev/null +++ b/common/src/sys/action_state.rs @@ -0,0 +1,74 @@ +use crate::{ + comp::{ + ActionState, Animation, AnimationInfo, Attacking, Controller, ForceUpdate, Gliding, + Jumping, OnGround, Ori, Pos, Rolling, Vel, + }, + state::DeltaTime, + sys::phys::MOVEMENT_THRESHOLD_VEL, +}; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; + +/// This system will set the ActionState component as specified by other components +pub struct Sys; +impl<'a> System<'a> for Sys { + type SystemData = ( + Entities<'a>, + Read<'a, DeltaTime>, + ReadStorage<'a, Controller>, + ReadStorage<'a, Vel>, + ReadStorage<'a, OnGround>, + ReadStorage<'a, Jumping>, + ReadStorage<'a, Gliding>, + ReadStorage<'a, Attacking>, + ReadStorage<'a, Rolling>, + WriteStorage<'a, ActionState>, + ); + + fn run( + &mut self, + ( + entities, + dt, + controllers, // To make sure it only runs on the single client and the server + velocities, + on_grounds, + jumpings, + glidings, + attackings, + rollings, + mut action_states, + ): Self::SystemData, + ) { + for ( + entity, + vel, + _controller, + on_ground, + jumping, + gliding, + attacking, + rolling, + mut action_state, + ) in ( + &entities, + &velocities, + &controllers, + on_grounds.maybe(), + jumpings.maybe(), + glidings.maybe(), + attackings.maybe(), + rollings.maybe(), + &mut action_states, + ) + .join() + { + *action_state = ActionState { + on_ground: on_ground.is_some(), + moving: vel.0.magnitude_squared() > MOVEMENT_THRESHOLD_VEL.powf(2.0), + attacking: attacking.is_some(), + rolling: rolling.is_some(), + gliding: gliding.is_some(), + }; + } + } +} diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 3ff07be4ab..7ecd5fec42 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -41,9 +41,7 @@ impl<'a> System<'a> for Sys { let tgt_pos = tgt_pos.0 + *offset; if tgt_pos.z > pos.0.z + 1.0 { - if let Err(err) = jumps.insert(entity, Jumping) { - warn!("Inserting Jumping for an entity failed: {:?}", err,); - } + controller.jump = true; } // Move towards the target. @@ -74,9 +72,7 @@ impl<'a> System<'a> for Sys { controller.move_dir = Vec2::zero(); if rand::random::() < 0.2 { - attacks - .insert(entity, Attacking::start()) - .expect("Inserting attacking for an entity failed!"); + controller.attack = true; } false diff --git a/common/src/sys/animation.rs b/common/src/sys/animation.rs index 95bb562f90..cebb5dde07 100644 --- a/common/src/sys/animation.rs +++ b/common/src/sys/animation.rs @@ -1,8 +1,5 @@ use crate::{ - comp::{ - Animation, AnimationInfo, Attacking, ForceUpdate, Gliding, Jumping, OnGround, Ori, Pos, - Rolling, Vel, - }, + comp::{ActionState, Animation, AnimationInfo, ForceUpdate}, state::DeltaTime, }; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; @@ -13,55 +10,18 @@ impl<'a> System<'a> for Sys { type SystemData = ( Entities<'a>, Read<'a, DeltaTime>, - ReadStorage<'a, Vel>, - ReadStorage<'a, OnGround>, - ReadStorage<'a, Jumping>, - ReadStorage<'a, Gliding>, - ReadStorage<'a, Attacking>, - ReadStorage<'a, Rolling>, + ReadStorage<'a, ActionState>, WriteStorage<'a, AnimationInfo>, ); - fn run( - &mut self, - ( - entities, - dt, - velocities, - on_grounds, - jumpings, - glidings, - attackings, - rollings, - mut animation_infos, - ): Self::SystemData, - ) { - for (entity, vel, on_ground, jumping, gliding, attacking, rolling, mut animation_info) in ( - &entities, - &velocities, - on_grounds.maybe(), - jumpings.maybe(), - glidings.maybe(), - attackings.maybe(), - rollings.maybe(), - &mut animation_infos, - ) - .join() - { - animation_info.time += dt.0 as f64; - + fn run(&mut self, (entities, dt, action_states, mut animation_infos): Self::SystemData) { + for (entity, a) in (&entities, &action_states).join() { fn impossible_animation(message: &str) -> Animation { warn!("{}", message); Animation::Idle } - let animation = match ( - on_ground.is_some(), - vel.0.magnitude() > 3.0, // Moving - attacking.is_some(), - gliding.is_some(), - rolling.is_some(), - ) { + let animation = match (a.on_ground, a.moving, a.attacking, a.gliding, a.rolling) { (_, _, true, true, _) => impossible_animation("Attack while gliding"), (_, _, true, _, true) => impossible_animation("Roll while attacking"), (_, _, _, true, true) => impossible_animation("Roll while gliding"), @@ -74,14 +34,18 @@ impl<'a> System<'a> for Sys { (_, _, true, false, false) => Animation::Attack, }; - let last = animation_info.clone(); - let changed = last.animation != animation; + let new_time = animation_infos + .get(entity) + .filter(|i| i.animation == animation) + .map(|i| i.time + dt.0 as f64); - *animation_info = AnimationInfo { - animation, - time: if changed { 0.0 } else { last.time }, - changed, - }; + animation_infos.insert( + entity, + AnimationInfo { + animation, + time: new_time.unwrap_or(0.0), + }, + ); } } } diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index 5bd45ac7c7..f78a912251 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, Jumping, MoveDir, - OnGround, Respawning, Rolling, Stats, {ForceUpdate, Ori, Pos, Vel}, + ActionState, Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, + Jumping, MoveDir, OnGround, Respawning, Rolling, Stats, {ForceUpdate, Ori, Pos, Vel}, }, state::DeltaTime, }; @@ -18,7 +18,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Pos>, ReadStorage<'a, Vel>, ReadStorage<'a, Ori>, - ReadStorage<'a, OnGround>, + ReadStorage<'a, ActionState>, WriteStorage<'a, MoveDir>, WriteStorage<'a, Jumping>, WriteStorage<'a, Attacking>, @@ -37,7 +37,7 @@ impl<'a> System<'a> for Sys { positions, velocities, orientations, - on_grounds, + action_states, mut move_dirs, mut jumpings, mut attackings, @@ -46,14 +46,14 @@ impl<'a> System<'a> for Sys { mut glidings, ): Self::SystemData, ) { - for (entity, controller, stats, pos, vel, ori, on_ground) in ( + for (entity, controller, stats, pos, vel, ori, a) in ( &entities, &controllers, &stats, &positions, &velocities, &orientations, - on_grounds.maybe(), + &action_states, ) .join() { @@ -66,10 +66,10 @@ impl<'a> System<'a> for Sys { } // Move dir - if rollings.get(entity).is_none() { + if !a.rolling { move_dirs.insert( entity, - MoveDir(if controller.move_dir.magnitude() > 1.0 { + MoveDir(if controller.move_dir.magnitude_squared() > 1.0 { controller.move_dir.normalized() } else { controller.move_dir @@ -78,37 +78,29 @@ impl<'a> System<'a> for Sys { } // Glide - if controller.glide - && on_ground.is_none() - && attackings.get(entity).is_none() - && rollings.get(entity).is_none() - { + if controller.glide && !a.on_ground && !a.attacking && !a.rolling { glidings.insert(entity, Gliding); } else { glidings.remove(entity); } // Attack - if controller.attack - && attackings.get(entity).is_none() - && glidings.get(entity).is_none() - && rollings.get(entity).is_none() - { + if controller.attack && !a.attacking && !a.gliding && !a.rolling { attackings.insert(entity, Attacking::start()); } // Jump - if controller.jump && on_ground.is_some() && vel.0.z <= 0.0 { + if controller.jump && a.on_ground && vel.0.z <= 0.0 { jumpings.insert(entity, Jumping); } // Roll if controller.roll - && rollings.get(entity).is_none() - && attackings.get(entity).is_none() - && glidings.get(entity).is_none() - && on_ground.is_some() - && vel.0.magnitude() > 5.0 + && !a.rolling + && a.on_ground + && a.moving + && !a.attacking + && !a.gliding { rollings.insert(entity, Rolling::start()); } diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs index 65b24f12fa..6507c09f7e 100644 --- a/common/src/sys/mod.rs +++ b/common/src/sys/mod.rs @@ -1,3 +1,4 @@ +mod action_state; pub mod agent; pub mod animation; pub mod combat; @@ -11,6 +12,7 @@ use specs::DispatcherBuilder; // System names const AGENT_SYS: &str = "agent_sys"; const CONTROLLER_SYS: &str = "controller_sys"; +const ACTION_STATE_SYS: &str = "action_state_sys"; const PHYS_SYS: &str = "phys_sys"; const COMBAT_SYS: &str = "combat_sys"; const ANIMATION_SYS: &str = "animation_sys"; @@ -18,9 +20,14 @@ const STATS_SYS: &str = "stats_sys"; pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); - dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[]); - dispatch_builder.add(phys::Sys, PHYS_SYS, &[]); - dispatch_builder.add(combat::Sys, COMBAT_SYS, &[]); - dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[]); + dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]); + dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS]); + dispatch_builder.add( + action_state::Sys, + ACTION_STATE_SYS, + &[CONTROLLER_SYS, PHYS_SYS], + ); + dispatch_builder.add(combat::Sys, COMBAT_SYS, &[ACTION_STATE_SYS]); + dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[ACTION_STATE_SYS]); dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]); } diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index d1538fd183..f39fee3751 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{Gliding, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Stats, Vel}, + comp::{ActionState, Gliding, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Stats, Vel}, state::DeltaTime, terrain::TerrainMap, vol::{ReadVol, Vox}, @@ -22,6 +22,8 @@ const GLIDE_SPEED: f32 = 45.0; // Gravity is 9.81 * 4, so this makes gravity equal to .15 const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95; +pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; + // Integrates forces, calculates the new velocity based off of the old velocity // dt = delta time // lv = linear velocity @@ -45,6 +47,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, MoveDir>, ReadStorage<'a, Gliding>, ReadStorage<'a, Stats>, + ReadStorage<'a, ActionState>, WriteStorage<'a, Jumping>, WriteStorage<'a, Rolling>, WriteStorage<'a, OnGround>, @@ -62,6 +65,7 @@ impl<'a> System<'a> for Sys { move_dirs, glidings, stats, + action_states, mut jumpings, mut rollings, mut on_grounds, @@ -71,11 +75,11 @@ impl<'a> System<'a> for Sys { ): Self::SystemData, ) { // Apply movement inputs - for (entity, stats, move_dir, gliding, mut pos, mut vel, mut ori) in ( + for (entity, stats, a, move_dir, mut pos, mut vel, mut ori) in ( &entities, &stats, + &action_states, move_dirs.maybe(), - glidings.maybe(), &mut positions, &mut velocities, &mut orientations, @@ -91,19 +95,25 @@ impl<'a> System<'a> for Sys { if let Some(move_dir) = move_dir { vel.0 += Vec2::broadcast(dt.0) * move_dir.0 - * match ( - on_grounds.get(entity).is_some(), - glidings.get(entity).is_some(), - rollings.get(entity).is_some(), - ) { - (true, false, false) if vel.0.magnitude() < HUMANOID_SPEED => { + * match (a.on_ground, a.gliding, a.rolling) { + (true, false, false) + if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) => + { HUMANOID_ACCEL } - (false, true, false) if vel.0.magnitude() < GLIDE_SPEED => GLIDE_ACCEL, - (false, false, false) if vel.0.magnitude() < HUMANOID_AIR_SPEED => { + (false, true, false) + if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) => + { + GLIDE_ACCEL + } + (false, false, false) + if vel.0.magnitude_squared() < HUMANOID_AIR_SPEED.powf(2.0) => + { HUMANOID_AIR_ACCEL } - (true, false, true) if vel.0.magnitude() < ROLL_SPEED => ROLL_ACCEL, + (true, false, true) if vel.0.magnitude_squared() < ROLL_SPEED.powf(2.0) => { + ROLL_ACCEL + } _ => 0.0, }; @@ -116,7 +126,7 @@ impl<'a> System<'a> for Sys { } // Glide - if gliding.is_some() && vel.0.magnitude() < GLIDE_SPEED && vel.0.z < 0.0 { + if a.gliding && vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) && vel.0.z < 0.0 { let lift = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2; vel.0.z += dt.0 * lift * Vec2::::from(vel.0 * 0.15).magnitude().min(1.0); } @@ -124,7 +134,7 @@ impl<'a> System<'a> for Sys { // Roll if let Some(time) = rollings.get_mut(entity).map(|r| &mut r.time) { *time += dt.0; - if *time > 0.55 { + if *time > 0.55 || !a.moving { rollings.remove(entity); } } @@ -136,12 +146,7 @@ impl<'a> System<'a> for Sys { // Integrate forces // Friction is assumed to be a constant dependent on location - let friction = 50.0 - * if on_grounds.get(entity).is_some() { - FRIC_GROUND - } else { - FRIC_AIR - }; + let friction = 50.0 * if a.on_ground { FRIC_GROUND } else { FRIC_AIR }; vel.0 = integrate_forces(dt.0, vel.0, friction); // Basic collision with terrain @@ -184,7 +189,7 @@ impl<'a> System<'a> for Sys { false }; - let was_on_ground = on_grounds.get(entity).is_some(); + let was_on_ground = a.on_ground; on_grounds.remove(entity); // Assume we're in the air - unless we can prove otherwise pos.0.z -= 0.0001; // To force collision with the floor diff --git a/server/src/lib.rs b/server/src/lib.rs index ad64bfbcb3..76c86cfc09 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -20,7 +20,7 @@ use common::{ terrain::{TerrainChunk, TerrainChunkSize}, vol::VolSize, }; -use log::warn; +use log::{debug, warn}; use specs::{join::Join, world::EntityBuilder as EcsEntityBuilder, Builder, Entity as EcsEntity}; use std::{ collections::HashSet, @@ -146,9 +146,9 @@ impl Server { .with(comp::Vel(Vec3::zero())) .with(comp::Ori(Vec3::unit_y())) .with(comp::Controller::default()) - .with(comp::AnimationInfo::default()) .with(comp::Actor::Character { name, body }) .with(comp::Stats::default()) + .with(comp::ActionState::default()) .with(comp::ForceUpdate) } @@ -163,11 +163,11 @@ impl Server { state.write_component(entity, comp::Actor::Character { name, body }); state.write_component(entity, comp::Stats::default()); - state.write_component(entity, comp::AnimationInfo::default()); state.write_component(entity, comp::Controller::default()); state.write_component(entity, comp::Pos(spawn_point)); state.write_component(entity, comp::Vel(Vec3::zero())); state.write_component(entity, comp::Ori(Vec3::unit_y())); + state.write_component(entity, comp::ActionState::default()); // Make sure physics are accepted. state.write_component(entity, comp::ForceUpdate); @@ -212,74 +212,6 @@ impl Server { // Tick the world self.world.tick(dt); - // Sync deaths. - let ecs = &self.state.ecs(); - let clients = &mut self.clients; - let todo_kill = (&ecs.entities(), &ecs.read_storage::()) - .join() - .map(|(entity, dying)| { - // Chat message - if let Some(player) = ecs.read_storage::().get(entity) { - let msg = if let comp::HealthSource::Attack { by } = dying.cause { - ecs.entity_from_uid(by.into()).and_then(|attacker| { - ecs.read_storage::() - .get(attacker) - .map(|attacker_alias| { - format!( - "{} was killed by {}", - &player.alias, &attacker_alias.alias - ) - }) - }) - } else { - None - } - .unwrap_or(format!("{} died", &player.alias)); - - clients.notify_registered(ServerMsg::Chat(msg)); - } - - entity - }) - .collect::>(); - - // Actually kill them - for entity in todo_kill { - if let Some(client) = self.clients.get_mut(&entity) { - self.state.write_component(entity, comp::Vel(Vec3::zero())); - self.state.write_component(entity, comp::ForceUpdate); - client.force_state(ClientState::Dead); - } else { - if let Err(err) = self.state.ecs_mut().delete_entity_synced(entity) { - warn!("Failed to delete client not found in kill list: {:?}", err); - } - continue; - } - } - - // 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.write_component(entity, comp::Stats::default()); - self.state - .ecs_mut() - .write_storage::() - .get_mut(entity) - .map(|pos| pos.0.z += 100.0); - self.state.write_component(entity, comp::Vel(Vec3::zero())); - self.state.write_component(entity, comp::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() { @@ -602,7 +534,7 @@ impl Server { // Handle client disconnects. for entity in disconnected_clients { if let Err(err) = self.state.ecs_mut().delete_entity_synced(entity) { - warn!("Failed to delete disconnected client: {:?}", err); + debug!("Failed to delete disconnected client: {:?}", err); } frontend_events.push(Event::ClientDisconnected { entity }); @@ -627,11 +559,12 @@ impl Server { state.write_component(entity, player); // Sync physics - for (&uid, &pos, &vel, &ori) in ( + for (&uid, &pos, &vel, &ori, &action_state) in ( &state.ecs().read_storage::(), &state.ecs().read_storage::(), &state.ecs().read_storage::(), &state.ecs().read_storage::(), + &state.ecs().read_storage::(), ) .join() { @@ -640,19 +573,7 @@ impl Server { pos, vel, ori, - }); - } - - // Sync animations - for (&uid, &animation_info) in ( - &state.ecs().read_storage::(), - &state.ecs().read_storage::(), - ) - .join() - { - client.notify(ServerMsg::EntityAnimation { - entity: uid.into(), - animation_info: animation_info.clone(), + action_state, }); } @@ -667,12 +588,13 @@ impl Server { .notify_registered(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package())); // Sync physics - for (entity, &uid, &pos, &vel, &ori, force_update) in ( + for (entity, &uid, &pos, &vel, &ori, &action_state, force_update) in ( &self.state.ecs().entities(), &self.state.ecs().read_storage::(), &self.state.ecs().read_storage::(), &self.state.ecs().read_storage::(), &self.state.ecs().read_storage::(), + &self.state.ecs().read_storage::(), self.state.ecs().read_storage::().maybe(), ) .join() @@ -682,6 +604,7 @@ impl Server { pos, vel, ori, + action_state, }; let state = &self.state; @@ -716,24 +639,71 @@ impl Server { } } - // Sync animations - for (entity, &uid, &animation_info, force_update) in ( + // Sync deaths. + let ecs = &self.state.ecs(); + let clients = &mut self.clients; + let todo_kill = (&ecs.entities(), &ecs.read_storage::()) + .join() + .map(|(entity, dying)| { + // Chat message + if let Some(player) = ecs.read_storage::().get(entity) { + let msg = if let comp::HealthSource::Attack { by } = dying.cause { + ecs.entity_from_uid(by.into()).and_then(|attacker| { + ecs.read_storage::() + .get(attacker) + .map(|attacker_alias| { + format!( + "{} was killed by {}", + &player.alias, &attacker_alias.alias + ) + }) + }) + } else { + None + } + .unwrap_or(format!("{} died", &player.alias)); + + clients.notify_registered(ServerMsg::Chat(msg)); + } + + entity + }) + .collect::>(); + + // Actually kill them + for entity in todo_kill { + if let Some(client) = self.clients.get_mut(&entity) { + self.state.write_component(entity, comp::Vel(Vec3::zero())); + self.state.write_component(entity, comp::ForceUpdate); + client.force_state(ClientState::Dead); + } else { + if let Err(err) = self.state.ecs_mut().delete_entity_synced(entity) { + warn!("Failed to delete client not found in kill list: {:?}", err); + } + continue; + } + } + + // Handle respawns + let todo_respawn = ( &self.state.ecs().entities(), - &self.state.ecs().read_storage::(), - &self.state.ecs().read_storage::(), - self.state.ecs().read_storage::().maybe(), + &self.state.ecs().read_storage::(), ) .join() - { - if animation_info.changed || force_update.is_some() { - let msg = ServerMsg::EntityAnimation { - entity: uid.into(), - animation_info: animation_info.clone(), - }; - match force_update { - Some(_) => self.clients.notify_ingame(msg), - None => self.clients.notify_ingame_except(entity, msg), - } + .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.write_component(entity, comp::Stats::default()); + self.state + .ecs_mut() + .write_storage::() + .get_mut(entity) + .map(|pos| pos.0.z += 100.0); + self.state.write_component(entity, comp::Vel(Vec3::zero())); + self.state.write_component(entity, comp::ForceUpdate); } } diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index 5c7ee582f7..8b42334535 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -439,7 +439,7 @@ impl FigureModelCache { fn load_wolf_head_lower(head_lower: quadruped_medium::HeadLower) -> Mesh { Self::load_mesh( match head_lower { - quadruped_medium::HeadLower::Default => "npc/wolf/head_lower.vox", + quadruped_medium::HeadLower::Default => "npc/wolf/wolf_head_lower.vox", }, Vec3::new(-7.0, -6.0, -5.5), )