Switched to EventBus system

This commit is contained in:
Joshua Barretto 2019-08-07 16:39:16 +01:00
parent 98a913195b
commit 192f5d355f
9 changed files with 79 additions and 105 deletions

View File

@ -1,30 +0,0 @@
use specs::Component;
use specs_idvs::IDVStorage;
use std::ops::{Deref, DerefMut};
use vek::*;
#[derive(Clone, Debug, Default)]
pub struct Events(pub Vec<EntityEvent>);
impl Deref for Events {
type Target = Vec<EntityEvent>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Events {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Component for Events {
type Storage = IDVStorage<Self>;
}
#[derive(Clone, Debug)]
pub enum EntityEvent {
HitGround { vel: Vec3<f32> },
}

View File

@ -3,7 +3,6 @@ mod agent;
mod animation;
mod body;
mod controller;
mod event;
mod inputs;
mod inventory;
mod last;
@ -18,7 +17,6 @@ pub use agent::Agent;
pub use animation::{Animation, AnimationInfo};
pub use body::{humanoid, object, quadruped, quadruped_medium, Body};
pub use controller::Controller;
pub use event::{EntityEvent, Events};
pub use inputs::{
Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding,
};

42
common/src/event.rs Normal file
View File

@ -0,0 +1,42 @@
use specs::Entity as EcsEntity;
use std::{collections::VecDeque, ops::DerefMut, sync::Mutex};
use vek::*;
pub enum Event {
LandOnGround { entity: EcsEntity, vel: Vec3<f32> },
}
#[derive(Default)]
pub struct EventBus {
queue: Mutex<VecDeque<Event>>,
}
impl EventBus {
pub fn emitter(&self) -> Emitter {
Emitter {
bus: self,
events: VecDeque::new(),
}
}
pub fn recv_all(&self) -> impl ExactSizeIterator<Item = Event> {
std::mem::replace(self.queue.lock().unwrap().deref_mut(), VecDeque::new()).into_iter()
}
}
pub struct Emitter<'a> {
bus: &'a EventBus,
events: VecDeque<Event>,
}
impl<'a> Emitter<'a> {
pub fn emit(&mut self, event: Event) {
self.events.push_front(event);
}
}
impl<'a> Drop for Emitter<'a> {
fn drop(&mut self) {
self.bus.queue.lock().unwrap().append(&mut self.events);
}
}

View File

@ -15,6 +15,7 @@ extern crate log;
pub mod assets;
pub mod clock;
pub mod comp;
pub mod event;
pub mod figure;
pub mod msg;
pub mod npc;

View File

@ -3,6 +3,7 @@ pub use sphynx::Uid;
use crate::{
comp,
event::EventBus,
msg::{EcsCompPacket, EcsResPacket},
sys,
terrain::{Block, TerrainChunk, TerrainMap},
@ -42,18 +43,11 @@ pub struct DeltaTime(pub f32);
/// lag. Ideally, we'd avoid such a situation.
const MAX_DELTA_TIME: f32 = 1.0;
#[derive(Default)]
pub struct BlockChange {
blocks: FxHashMap<Vec3<i32>, Block>,
}
impl Default for BlockChange {
fn default() -> Self {
Self {
blocks: FxHashMap::default(),
}
}
}
impl BlockChange {
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
self.blocks.insert(pos, block);
@ -64,6 +58,7 @@ impl BlockChange {
}
}
#[derive(Default)]
pub struct TerrainChanges {
pub new_chunks: FxHashSet<Vec2<i32>>,
pub modified_chunks: FxHashSet<Vec2<i32>>,
@ -71,17 +66,6 @@ pub struct TerrainChanges {
pub modified_blocks: FxHashMap<Vec3<i32>, Block>,
}
impl Default for TerrainChanges {
fn default() -> Self {
Self {
new_chunks: FxHashSet::default(),
modified_chunks: FxHashSet::default(),
removed_chunks: FxHashSet::default(),
modified_blocks: FxHashMap::default(),
}
}
}
impl TerrainChanges {
pub fn clear(&mut self) {
self.new_chunks.clear();
@ -161,7 +145,6 @@ impl State {
ecs.register::<comp::ForceUpdate>();
ecs.register::<comp::InventoryUpdate>();
ecs.register::<comp::Inventory>();
ecs.register::<comp::Events>();
// Controller effects
ecs.register::<comp::MoveDir>();
ecs.register::<comp::OnGround>();
@ -179,6 +162,7 @@ impl State {
ecs.add_resource(TerrainMap::new().unwrap());
ecs.add_resource(BlockChange::default());
ecs.add_resource(TerrainChanges::default());
ecs.add_resource(EventBus::default());
}
/// Register a component with the state's ECS.

View File

@ -1,33 +0,0 @@
use crate::{
comp::{EntityEvent, Events, HealthSource, Stats},
state::DeltaTime,
};
use log::warn;
use specs::{Entities, Join, Read, System, WriteStorage};
/// This system kills players
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
WriteStorage<'a, Events>,
WriteStorage<'a, Stats>,
);
fn run(&mut self, (entities, mut events, mut stats): Self::SystemData) {
for (entity, mut events) in (&entities, &mut events).join() {
for event in events.drain(..) {
match event {
EntityEvent::HitGround { vel } => {
if let Some(stat) = stats.get_mut(entity) {
let falldmg = (vel.z / 1.5 + 6.0) as i32;
if falldmg < 0 {
stat.health.change_by(falldmg, HealthSource::World);
}
}
}
}
}
}
}
}

View File

@ -3,7 +3,6 @@ pub mod agent;
pub mod animation;
pub mod combat;
pub mod controller;
mod event_handler;
pub mod movement;
pub mod phys;
mod stats;
@ -20,7 +19,6 @@ const MOVEMENT_SYS: &str = "movement_sys";
const COMBAT_SYS: &str = "combat_sys";
const ANIMATION_SYS: &str = "animation_sys";
const STATS_SYS: &str = "stats_sys";
const EVENT_HANDLER_SYS: &str = "event_handler_sys";
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
@ -35,9 +33,4 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
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]);
dispatch_builder.add(
event_handler::Sys,
EVENT_HANDLER_SYS,
&[AGENT_SYS, PHYS_SYS, ACTION_STATE_SYS, COMBAT_SYS],
);
}

View File

@ -1,9 +1,10 @@
use crate::{
comp::HealthSource,
comp::{
ActionState, Body, EntityEvent, Events, Jumping, MoveDir, OnGround, Ori, Pos, Rolling,
Scale, Stats, Vel, Wielding,
ActionState, Body, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Scale, Stats, Vel,
Wielding,
},
event::{Event, EventBus},
state::DeltaTime,
terrain::TerrainMap,
vol::{ReadVol, Vox},
@ -35,6 +36,7 @@ impl<'a> System<'a> for Sys {
Entities<'a>,
ReadExpect<'a, TerrainMap>,
Read<'a, DeltaTime>,
Read<'a, EventBus>,
ReadStorage<'a, ActionState>,
ReadStorage<'a, Scale>,
ReadStorage<'a, Body>,
@ -42,7 +44,6 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Pos>,
WriteStorage<'a, Vel>,
WriteStorage<'a, Ori>,
WriteStorage<'a, Events>,
);
fn run(
@ -51,6 +52,7 @@ impl<'a> System<'a> for Sys {
entities,
terrain,
dt,
event_bus,
action_states,
scales,
bodies,
@ -58,9 +60,10 @@ impl<'a> System<'a> for Sys {
mut positions,
mut velocities,
mut orientations,
mut events,
): Self::SystemData,
) {
let mut event_emitter = event_bus.emitter();
// Apply movement inputs
for (entity, a, scale, b, mut pos, mut vel, mut ori) in (
&entities,
@ -211,15 +214,8 @@ impl<'a> System<'a> for Sys {
if resolve_dir.z > 0.0 && vel.0.z <= 0.0 {
on_ground = true;
// Hitting the ground
const COLLISION_VEL: f32 = GRAVITY * 0.75; // Falling for 0.75 seconds
if vel.0.z < -COLLISION_VEL {
if events.get(entity).is_none() {
events.insert(entity, Events::default());
}
events
.get_mut(entity) // TODO: Use get_mut_or_default when updating to SPECS 15
.map(|e| e.push(EntityEvent::HitGround { vel: vel.0 }));
if !was_on_ground {
event_emitter.emit(Event::LandOnGround { entity, vel: vel.0 });
}
}

View File

@ -15,6 +15,7 @@ use crate::{
};
use common::{
comp,
event::{Event as GameEvent, EventBus},
msg::{ClientMsg, ClientState, RequestStateError, ServerError, ServerInfo, ServerMsg},
net::PostOffice,
state::{State, TimeOfDay, Uid},
@ -84,6 +85,7 @@ impl Server {
state
.ecs_mut()
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 380.0)));
state.ecs_mut().add_resource(EventBus::default());
// Set starting time for the server.
state.ecs_mut().write_resource::<TimeOfDay>().0 = settings.start_time;
@ -199,6 +201,24 @@ impl Server {
client.allow_state(ClientState::Character);
}
/// Handle events coming through via the event bus
fn handle_events(&mut self) {
let mut stats = self.state.ecs().write_storage::<comp::Stats>();
for event in self.state.ecs().read_resource::<EventBus>().recv_all() {
match event {
GameEvent::LandOnGround { entity, vel } => {
if let Some(stats) = stats.get_mut(entity) {
let falldmg = (vel.z / 1.5 + 10.0) as i32;
if falldmg < 0 {
stats.health.change_by(falldmg, comp::HealthSource::World);
}
}
}
}
}
}
/// Execute a single server tick, handle input and update the game state by the given duration.
pub fn tick(&mut self, _input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
// This tick function is the centre of the Veloren universe. Most server-side things are
@ -351,6 +371,9 @@ impl Server {
self.state.remove_chunk(key);
}
// Handle events
self.handle_events();
// 6) Synchronise clients with the new state of the world.
self.sync_clients();