From 192f5d355f59ee5b0773b8c4ef35bd1f99de0e65 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Wed, 7 Aug 2019 16:39:16 +0100
Subject: [PATCH] Switched to EventBus system

---
 common/src/comp/event.rs        | 30 -----------------------
 common/src/comp/mod.rs          |  2 --
 common/src/event.rs             | 42 +++++++++++++++++++++++++++++++++
 common/src/lib.rs               |  1 +
 common/src/state.rs             | 24 ++++---------------
 common/src/sys/event_handler.rs | 33 --------------------------
 common/src/sys/mod.rs           |  7 ------
 common/src/sys/phys.rs          | 22 +++++++----------
 server/src/lib.rs               | 23 ++++++++++++++++++
 9 files changed, 79 insertions(+), 105 deletions(-)
 delete mode 100644 common/src/comp/event.rs
 create mode 100644 common/src/event.rs
 delete mode 100644 common/src/sys/event_handler.rs

diff --git a/common/src/comp/event.rs b/common/src/comp/event.rs
deleted file mode 100644
index bed74d4669..0000000000
--- a/common/src/comp/event.rs
+++ /dev/null
@@ -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> },
-}
diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs
index 54ac7dd856..bee5458a2c 100644
--- a/common/src/comp/mod.rs
+++ b/common/src/comp/mod.rs
@@ -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,
 };
diff --git a/common/src/event.rs b/common/src/event.rs
new file mode 100644
index 0000000000..e9cef710d1
--- /dev/null
+++ b/common/src/event.rs
@@ -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);
+    }
+}
diff --git a/common/src/lib.rs b/common/src/lib.rs
index 42f30e0e23..e128100c78 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -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;
diff --git a/common/src/state.rs b/common/src/state.rs
index 81cfc2d7bb..65046bc4dd 100644
--- a/common/src/state.rs
+++ b/common/src/state.rs
@@ -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.
diff --git a/common/src/sys/event_handler.rs b/common/src/sys/event_handler.rs
deleted file mode 100644
index c7f6e72198..0000000000
--- a/common/src/sys/event_handler.rs
+++ /dev/null
@@ -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);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs
index a71a03bdcb..7664b4af8a 100644
--- a/common/src/sys/mod.rs
+++ b/common/src/sys/mod.rs
@@ -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],
-    );
 }
diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs
index 674e7cd5fb..dadc527fd7 100644
--- a/common/src/sys/phys.rs
+++ b/common/src/sys/phys.rs
@@ -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 });
                         }
                     }
 
diff --git a/server/src/lib.rs b/server/src/lib.rs
index f0cbf76fd5..7650108e2f 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -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();