Merge branch 'timo-rolling-fix' into 'master'

Rolling bugs + NPC bugs + "Impossible" bug

See merge request veloren/veloren!265
This commit is contained in:
Joshua Barretto 2019-06-29 21:47:05 +00:00
commit f1ba97ae7a
15 changed files with 273 additions and 246 deletions

View File

@ -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;

View File

@ -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<Self, VecStorage<Self>>;
}

View File

@ -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,
}
}
}

View File

@ -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};

View File

@ -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<T> for EcsCompPhantom
@ -39,9 +36,6 @@ sphynx::sum_type! {
Actor(PhantomData<comp::Actor>),
Player(PhantomData<comp::Player>),
Stats(PhantomData<comp::Stats>),
Attacking(PhantomData<comp::Attacking>),
Rolling(PhantomData<comp::Rolling>),
Gliding(PhantomData<comp::Gliding>),
}
}
impl sphynx::CompPacket for EcsCompPacket {

View File

@ -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<i32>,

View File

@ -102,9 +102,6 @@ impl State {
ecs.register_synced::<comp::Actor>();
ecs.register_synced::<comp::Player>();
ecs.register_synced::<comp::Stats>();
ecs.register_synced::<comp::Attacking>();
ecs.register_synced::<comp::Rolling>();
ecs.register_synced::<comp::Gliding>();
// Register components synced by other means
ecs.register::<comp::Pos>();
@ -112,10 +109,14 @@ impl State {
ecs.register::<comp::Ori>();
ecs.register::<comp::MoveDir>();
ecs.register::<comp::OnGround>();
ecs.register::<comp::AnimationInfo>();
ecs.register::<comp::Controller>();
ecs.register::<comp::Attacking>();
ecs.register::<comp::Rolling>();
ecs.register::<comp::Gliding>();
ecs.register::<comp::ActionState>();
// Register client-local components
ecs.register::<comp::AnimationInfo>();
ecs.register::<comp::Jumping>();
// Register server-local components

View File

@ -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(),
};
}
}
}

View File

@ -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::<f32>() < 0.2 {
attacks
.insert(entity, Attacking::start())
.expect("Inserting attacking for an entity failed!");
controller.attack = true;
}
false

View File

@ -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),
},
);
}
}
}

View File

@ -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());
}

View File

@ -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]);
}

View File

@ -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::<f32>::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

View File

@ -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::<comp::Dying>())
.join()
.map(|(entity, dying)| {
// Chat message
if let Some(player) = ecs.read_storage::<comp::Player>().get(entity) {
let msg = if let comp::HealthSource::Attack { by } = dying.cause {
ecs.entity_from_uid(by.into()).and_then(|attacker| {
ecs.read_storage::<comp::Player>()
.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::<Vec<_>>();
// 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::<comp::Respawning>(),
)
.join()
.map(|(entity, _)| entity)
.collect::<Vec<EcsEntity>>();
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::<comp::Pos>()
.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::<Uid>(),
&state.ecs().read_storage::<comp::Pos>(),
&state.ecs().read_storage::<comp::Vel>(),
&state.ecs().read_storage::<comp::Ori>(),
&state.ecs().read_storage::<comp::ActionState>(),
)
.join()
{
@ -640,19 +573,7 @@ impl Server {
pos,
vel,
ori,
});
}
// Sync animations
for (&uid, &animation_info) in (
&state.ecs().read_storage::<Uid>(),
&state.ecs().read_storage::<comp::AnimationInfo>(),
)
.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::<Uid>(),
&self.state.ecs().read_storage::<comp::Pos>(),
&self.state.ecs().read_storage::<comp::Vel>(),
&self.state.ecs().read_storage::<comp::Ori>(),
&self.state.ecs().read_storage::<comp::ActionState>(),
self.state.ecs().read_storage::<comp::ForceUpdate>().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::<comp::Dying>())
.join()
.map(|(entity, dying)| {
// Chat message
if let Some(player) = ecs.read_storage::<comp::Player>().get(entity) {
let msg = if let comp::HealthSource::Attack { by } = dying.cause {
ecs.entity_from_uid(by.into()).and_then(|attacker| {
ecs.read_storage::<comp::Player>()
.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::<Vec<_>>();
// 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::<Uid>(),
&self.state.ecs().read_storage::<comp::AnimationInfo>(),
self.state.ecs().read_storage::<comp::ForceUpdate>().maybe(),
&self.state.ecs().read_storage::<comp::Respawning>(),
)
.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::<Vec<EcsEntity>>();
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::<comp::Pos>()
.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);
}
}

View File

@ -439,7 +439,7 @@ impl FigureModelCache {
fn load_wolf_head_lower(head_lower: quadruped_medium::HeadLower) -> Mesh<FigurePipeline> {
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),
)