mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
commit
ac71a933eb
@ -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 => {},
|
||||||
},
|
},
|
||||||
|
@ -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>>;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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>,
|
||||||
|
@ -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>();
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
let animation =
|
||||||
if control.move_dir.magnitude() > 0.01 {
|
if control.move_dir.magnitude() > 0.01 {
|
||||||
dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
|
dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
|
||||||
anims.insert(entity, Animation::Run);
|
Animation::Run
|
||||||
} else {
|
} else {
|
||||||
anims.insert(entity, Animation::Idle);
|
Animation::Idle
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let last_animation = anims.get_mut(entity).map(|h| h.current);
|
||||||
|
|
||||||
|
anims.insert(entity, AnimationHistory {
|
||||||
|
last: last_animation,
|
||||||
|
current: animation,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user