Merge branch 'master' into 'master'

Fix animation states being sent every frame (#21)

Closes #21

See merge request veloren/veloren!45

Former-commit-id: aabce2aba011e7b02ec35714c519369bf1eea754
This commit is contained in:
Joshua Barretto 2019-04-17 20:46:12 +00:00
commit ac71a933eb
9 changed files with 113 additions and 46 deletions

View File

@ -167,9 +167,11 @@ impl Client {
_ => {}, _ => {},
} }
// Update the server about the player's currently playing animation // Update the server about the player's currently playing animation and the previous one
if let Some(animation) = self.state.read_storage().get(self.player).cloned() { if let Some(animation_history) = self.state.read_storage::<comp::AnimationHistory>().get(self.player).cloned() {
self.postbox.send_message(ClientMsg::PlayerAnimation(animation)); if Some(animation_history.current) != animation_history.last {
self.postbox.send_message(ClientMsg::PlayerAnimation(animation_history));
}
} }
// Request chunks from the server // Request chunks from the server
@ -228,9 +230,9 @@ impl Client {
}, },
None => {}, None => {},
}, },
ServerMsg::EntityAnimation { entity, animation } => match self.state.ecs().entity_from_uid(entity) { ServerMsg::EntityAnimation { entity, animation_history } => match self.state.ecs().entity_from_uid(entity) {
Some(entity) => { Some(entity) => {
self.state.write_component(entity, animation); self.state.write_component(entity, animation_history);
}, },
None => {}, None => {},
}, },

View File

@ -19,6 +19,12 @@ pub enum Gender {
} }
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct AnimationHistory {
pub last: Option<Animation>,
pub current: Animation,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Animation { pub enum Animation {
Idle, Idle,
Run, Run,
@ -55,6 +61,6 @@ impl Component for Character {
type Storage = FlaggedStorage<Self, VecStorage<Self>>; type Storage = FlaggedStorage<Self, VecStorage<Self>>;
} }
impl Component for Animation { impl Component for AnimationHistory {
type Storage = FlaggedStorage<Self, VecStorage<Self>>; type Storage = FlaggedStorage<Self, VecStorage<Self>>;
} }

View File

@ -7,4 +7,5 @@ pub mod phys;
pub use agent::{Agent, Control}; pub use agent::{Agent, Control};
pub use character::Character; pub use character::Character;
pub use player::Player; pub use player::Player;
pub use character::AnimationHistory;
pub use character::Animation; pub use character::Animation;

View File

@ -11,7 +11,7 @@ pub enum ClientMsg {
Ping, Ping,
Pong, Pong,
Chat(String), Chat(String),
PlayerAnimation(comp::character::Animation), PlayerAnimation(comp::character::AnimationHistory),
PlayerPhysics { PlayerPhysics {
pos: comp::phys::Pos, pos: comp::phys::Pos,
vel: comp::phys::Vel, vel: comp::phys::Vel,

View File

@ -25,7 +25,7 @@ pub enum ServerMsg {
}, },
EntityAnimation { EntityAnimation {
entity: u64, entity: u64,
animation: comp::Animation, animation_history: comp::AnimationHistory,
}, },
TerrainChunkUpdate { TerrainChunkUpdate {
key: Vec3<i32>, key: Vec3<i32>,

View File

@ -100,7 +100,7 @@ impl State {
ecs.internal_mut().register::<comp::phys::Pos>(); ecs.internal_mut().register::<comp::phys::Pos>();
ecs.internal_mut().register::<comp::phys::Vel>(); ecs.internal_mut().register::<comp::phys::Vel>();
ecs.internal_mut().register::<comp::phys::Dir>(); ecs.internal_mut().register::<comp::phys::Dir>();
ecs.internal_mut().register::<comp::Animation>(); ecs.internal_mut().register::<comp::AnimationHistory>();
ecs.internal_mut().register::<comp::Agent>(); ecs.internal_mut().register::<comp::Agent>();
ecs.internal_mut().register::<comp::Control>(); ecs.internal_mut().register::<comp::Control>();

View File

@ -3,7 +3,7 @@ use specs::{Join, Read, ReadStorage, System, WriteStorage, Entities};
use vek::*; use vek::*;
// Crate // Crate
use crate::comp::{Control, Animation, phys::{Pos, Vel, Dir}}; use crate::comp::{Control, Animation, AnimationHistory, phys::{Pos, Vel, Dir}};
// Basic ECS AI agent system // Basic ECS AI agent system
pub struct Sys; pub struct Sys;
@ -13,7 +13,7 @@ impl<'a> System<'a> for Sys {
Entities<'a>, Entities<'a>,
WriteStorage<'a, Vel>, WriteStorage<'a, Vel>,
WriteStorage<'a, Dir>, WriteStorage<'a, Dir>,
WriteStorage<'a, Animation>, WriteStorage<'a, AnimationHistory>,
ReadStorage<'a, Control>, ReadStorage<'a, Control>,
); );
@ -23,12 +23,20 @@ impl<'a> System<'a> for Sys {
// Apply physics to the player: acceleration and non-linear decceleration // Apply physics to the player: acceleration and non-linear decceleration
vel.0 += control.move_dir * 2.0 - vel.0.map(|e| e * e.abs() + e) * 0.03; vel.0 += control.move_dir * 2.0 - vel.0.map(|e| e * e.abs() + e) * 0.03;
if control.move_dir.magnitude() > 0.01 { let animation =
dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0); if control.move_dir.magnitude() > 0.01 {
anims.insert(entity, Animation::Run); dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
} else { Animation::Run
anims.insert(entity, Animation::Idle); } else {
} Animation::Idle
};
let last_animation = anims.get_mut(entity).map(|h| h.current);
anims.insert(entity, AnimationHistory {
last: last_animation,
current: animation,
});
} }
} }
} }

View File

@ -15,6 +15,7 @@ use common::{
net::PostOffice, net::PostOffice,
state::{State, Uid}, state::{State, Uid},
terrain::TerrainChunk, terrain::TerrainChunk,
comp::character::Animation,
}; };
use specs::{ use specs::{
join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder, join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder,
@ -235,28 +236,7 @@ impl Server {
match client.state { match client.state {
ClientState::Connecting => match msg { ClientState::Connecting => match msg {
ClientMsg::Connect { player, character } => { ClientMsg::Connect { player, character } => {
Self::initialize_client(state, entity, client, player, character);
// Write client components
state.write_component(entity, player);
state.write_component(entity, comp::phys::Pos(Vec3::zero()));
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
if let Some(character) = character {
state.write_component(entity, character);
}
state.write_component(entity, comp::phys::ForceUpdate);
client.state = ClientState::Connected;
// Return a handshake with the state of the current world
client.notify(ServerMsg::Handshake {
ecs_state: state.ecs().gen_state_package(),
player_entity: state
.ecs()
.uid_from_entity(entity)
.unwrap()
.into(),
});
} }
_ => disconnect = true, _ => disconnect = true,
}, },
@ -266,7 +246,7 @@ impl Server {
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong), ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
ClientMsg::Pong => {} ClientMsg::Pong => {}
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)), ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
ClientMsg::PlayerAnimation(animation) => state.write_component(entity, animation), ClientMsg::PlayerAnimation(animation_history) => state.write_component(entity, animation_history),
ClientMsg::PlayerPhysics { pos, vel, dir } => { ClientMsg::PlayerPhysics { pos, vel, dir } => {
state.write_component(entity, pos); state.write_component(entity, pos);
state.write_component(entity, vel); state.write_component(entity, vel);
@ -341,6 +321,63 @@ impl Server {
Ok(frontend_events) Ok(frontend_events)
} }
/// Initialize a new client states with important information
fn initialize_client(
state: &mut State,
entity: specs::Entity,
client: &mut Client,
player: comp::Player,
character: Option<comp::Character>,
) {
// Save player metadata (for example the username)
state.write_component(entity, player);
// Give the player it's character if he wants one
// (Chat only clients don't need one for example)
if let Some(character) = character {
state.write_component(entity, character);
// Every character has to have these components
state.write_component(entity, comp::phys::Pos(Vec3::zero()));
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
// Make sure everything is accepted
state.write_component(entity, comp::phys::ForceUpdate);
// Set initial animation
state.write_component(entity, comp::AnimationHistory {
last: None,
current: Animation::Idle
});
}
client.state = ClientState::Connected;
// Return a handshake with the state of the current world
// (All components Sphynx tracks)
client.notify(ServerMsg::Handshake {
ecs_state: state.ecs().gen_state_package(),
player_entity: state
.ecs()
.uid_from_entity(entity)
.unwrap()
.into(),
});
// Sync logical information other players have authority over, not the server
for (other_entity, &uid, &animation_history) in (
&state.ecs().internal().entities(),
&state.ecs().internal().read_storage::<common::state::Uid>(),
&state.ecs().internal().read_storage::<comp::AnimationHistory>(),
).join() {
// AnimationHistory
client.postbox.send_message(ServerMsg::EntityAnimation {
entity: uid.into(),
animation_history: animation_history,
});
}
}
/// Sync client states with the most up to date information /// Sync client states with the most up to date information
fn sync_clients(&mut self) { fn sync_clients(&mut self) {
// Sync 'logical' state using Sphynx // Sync 'logical' state using Sphynx
@ -369,17 +406,30 @@ impl Server {
} }
// Sync animation states // Sync animation states
for (entity, &uid, &animation) in ( for (entity, &uid, &animation_history) in (
&self.state.ecs().internal().entities(), &self.state.ecs().internal().entities(),
&self.state.ecs().internal().read_storage::<Uid>(), &self.state.ecs().internal().read_storage::<Uid>(),
&self.state.ecs().internal().read_storage::<comp::Animation>(), &self.state.ecs().internal().read_storage::<comp::AnimationHistory>(),
).join() { ).join() {
// Check if we need to sync
if Some(animation_history.current) == animation_history.last {
continue;
}
self.clients.notify_connected_except(entity, ServerMsg::EntityAnimation { self.clients.notify_connected_except(entity, ServerMsg::EntityAnimation {
entity: uid.into(), entity: uid.into(),
animation, animation_history,
}); });
} }
// Update animation last/current state
for (entity, mut animation_history) in (
&self.state.ecs().internal().entities(),
&mut self.state.ecs().internal().write_storage::<comp::AnimationHistory>()
).join() {
animation_history.last = Some(animation_history.current);
}
// Remove all force flags // Remove all force flags
self.state.ecs_mut().internal_mut().write_storage::<comp::phys::ForceUpdate>().clear(); self.state.ecs_mut().internal_mut().write_storage::<comp::phys::ForceUpdate>().clear();
} }

View File

@ -83,18 +83,18 @@ impl Figures {
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) { pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
let time = client.state().get_time(); let time = client.state().get_time();
let ecs = client.state_mut().ecs_mut().internal_mut(); let ecs = client.state_mut().ecs_mut().internal_mut();
for (entity, pos, dir, character, animation) in ( for (entity, pos, dir, character, animation_history) in (
&ecs.entities(), &ecs.entities(),
&ecs.read_storage::<comp::phys::Pos>(), &ecs.read_storage::<comp::phys::Pos>(),
&ecs.read_storage::<comp::phys::Dir>(), &ecs.read_storage::<comp::phys::Dir>(),
&ecs.read_storage::<comp::Character>(), &ecs.read_storage::<comp::Character>(),
&ecs.read_storage::<comp::Animation>(), &ecs.read_storage::<comp::AnimationHistory>(),
).join() { ).join() {
let state = self.states let state = self.states
.entry(entity) .entry(entity)
.or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new())); .or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new()));
let target_skeleton = match animation { let target_skeleton = match animation_history.current {
comp::character::Animation::Idle => IdleAnimation::update_skeleton(&mut state.skeleton, time), comp::character::Animation::Idle => IdleAnimation::update_skeleton(&mut state.skeleton, time),
comp::character::Animation::Run => RunAnimation::update_skeleton(&mut state.skeleton, time), comp::character::Animation::Run => RunAnimation::update_skeleton(&mut state.skeleton, time),
}; };