Fix double roll bug

This commit is contained in:
timokoesters 2019-06-29 19:49:51 +02:00
parent b0ea959f67
commit c80351f6ca
No known key found for this signature in database
GPG Key ID: CD80BE9AAEE78097
10 changed files with 188 additions and 110 deletions

View File

@ -374,11 +374,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);
self.state.write_component(entity, action_state);
}
None => {}
},

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

@ -7,6 +7,7 @@ mod inventory;
mod phys;
mod player;
mod stats;
mod action_state;
// Reexports
pub use agent::Agent;
@ -18,3 +19,4 @@ pub use inventory::{item, Inventory};
pub use phys::{ForceUpdate, Ori, Pos, Vel};
pub use player::Player;
pub use stats::{Dying, HealthSource, Stats};
pub use action_state::ActionState;

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,6 +35,7 @@ pub enum ServerMsg {
pos: comp::Pos,
vel: comp::Vel,
ori: comp::Ori,
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,60 @@
use crate::{
comp::{
Animation, AnimationInfo, Attacking, ForceUpdate, Gliding, Jumping, OnGround, Ori, Pos,
Rolling, Vel, ActionState,
},
state::DeltaTime,
};
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, 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,
velocities,
on_grounds,
jumpings,
glidings,
attackings,
rollings,
mut action_states,
): Self::SystemData,
) {
for (entity, vel, on_ground, jumping, gliding, attacking, rolling, mut action_state) in (
&entities,
&velocities,
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() > 10.0,
attacking: attacking.is_some(),
rolling: rolling.is_some(),
gliding: gliding.is_some(),
};
}
}
}

View File

@ -1,7 +1,6 @@
use crate::{
comp::{
Animation, AnimationInfo, Attacking, ForceUpdate, Gliding, Jumping, OnGround, Ori, Pos,
Rolling, Vel,
Animation, AnimationInfo, ForceUpdate, ActionState
},
state::DeltaTime,
};
@ -13,12 +12,7 @@ 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>,
);
@ -27,23 +21,13 @@ impl<'a> System<'a> for Sys {
(
entities,
dt,
velocities,
on_grounds,
jumpings,
glidings,
attackings,
rollings,
action_states,
mut animation_infos,
): Self::SystemData,
) {
for (entity, vel, on_ground, jumping, gliding, attacking, rolling, mut animation_info) in (
for (entity, a, mut animation_info) in (
&entities,
&velocities,
on_grounds.maybe(),
jumpings.maybe(),
glidings.maybe(),
attackings.maybe(),
rollings.maybe(),
&action_states,
&mut animation_infos,
)
.join()
@ -56,11 +40,11 @@ impl<'a> System<'a> for Sys {
}
let animation = match (
on_ground.is_some(),
vel.0.magnitude_squared() > 10.0, // Moving
attacking.is_some(),
gliding.is_some(),
rolling.is_some(),
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"),

View File

@ -4,6 +4,7 @@ pub mod combat;
pub mod controller;
pub mod phys;
mod stats;
mod action_state;
// External
use specs::DispatcherBuilder;
@ -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,10 @@ 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

@ -166,6 +166,7 @@ impl Server {
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);
@ -210,74 +211,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() {
@ -618,11 +551,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()
{
@ -631,6 +565,7 @@ impl Server {
pos,
vel,
ori,
action_state,
});
}
@ -645,12 +580,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()
@ -660,6 +596,7 @@ impl Server {
pos,
vel,
ori,
action_state,
};
let state = &self.state;
@ -694,6 +631,74 @@ impl Server {
}
}
// 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);
}
}
// Remove all force flags.
self.state
.ecs_mut()