From 8b347f77a3924d58b54d24f38173d03a811b7a80 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 4 Mar 2019 19:50:26 +0000
Subject: [PATCH] Component sync + UID system

Former-commit-id: 5ecddc0e1f9c1a15f99dd167b825178c972da062
---
 client/src/lib.rs        | 23 ++++++++++--
 common/Cargo.toml        |  2 +-
 common/src/comp/mod.rs   |  6 ++--
 common/src/comp/phys.rs  |  6 ++--
 common/src/comp/uid.rs   | 32 +++++++++++------
 common/src/msg/client.rs |  2 +-
 common/src/msg/server.rs | 12 +++++--
 common/src/state.rs      | 33 +++++++++++++++---
 server/Cargo.toml        |  1 +
 server/src/client.rs     | 29 ++++++++++++++++
 server/src/lib.rs        | 75 ++++++++++++++++++++++++++++++++--------
 11 files changed, 181 insertions(+), 40 deletions(-)

diff --git a/client/src/lib.rs b/client/src/lib.rs
index af2f41d397..d1496d6a15 100644
--- a/client/src/lib.rs
+++ b/client/src/lib.rs
@@ -14,8 +14,9 @@ use std::{
 };
 use vek::*;
 use threadpool;
+use specs::Builder;
 use common::{
-    comp::phys::Vel,
+    comp,
     state::State,
     terrain::TerrainChunk,
     net::PostBox,
@@ -96,6 +97,13 @@ impl Client {
     #[allow(dead_code)]
     pub fn state_mut(&mut self) -> &mut State { &mut self.state }
 
+    /// Get an entity from its UID, creating it if it does not exists
+    pub fn get_or_create_entity(&mut self, uid: u64) -> EcsEntity {
+        self.state.ecs_world_mut().create_entity()
+            .with(comp::Uid(uid))
+            .build()
+    }
+
     /// Get the player entity
     #[allow(dead_code)]
     pub fn player(&self) -> Option<EcsEntity> {
@@ -141,7 +149,7 @@ impl Client {
             const PLAYER_VELOCITY: f32 = 100.0;
 
             // TODO: Set acceleration instead
-            self.state.write_component(p, Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY)));
+            self.state.write_component(p, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY)));
         }
 
         // Tick the client's LocalState (step 3)
@@ -170,11 +178,20 @@ impl Client {
             self.last_ping = self.state.get_time();
 
             for msg in new_msgs {
+                println!("Received message");
                 match msg {
-                    ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
                     ServerMsg::Shutdown => return Err(Error::ServerShutdown),
+                    ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
+                    ServerMsg::EntityPhysics { uid, pos, vel, dir } => {
+                        let ecs_entity = self.get_or_create_entity(uid);
+                        self.state.write_component(ecs_entity, pos);
+                        self.state.write_component(ecs_entity, vel);
+                        self.state.write_component(ecs_entity, dir);
+                    },
                 }
             }
+        } else if let Some(err) = self.postbox.status() {
+            return Err(err.into());
         }
 
         Ok(frontend_events)
diff --git a/common/Cargo.toml b/common/Cargo.toml
index b8db18d3b3..ca660532d1 100644
--- a/common/Cargo.toml
+++ b/common/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2018"
 [dependencies]
 specs = { version = "0.14", features = ["serde"] }
 shred = "0.7"
-vek = "0.9"
+vek = { version = "0.9", features = ["serde"] }
 dot_vox = "1.0"
 threadpool = "1.7"
 mio = "0.6"
diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs
index d822941454..528f1a3468 100644
--- a/common/src/comp/mod.rs
+++ b/common/src/comp/mod.rs
@@ -1,11 +1,13 @@
 pub mod phys;
 pub mod uid;
 
-// External
+// Reexports
+pub use uid::{Uid, UidAllocator};
+
 use specs::World as EcsWorld;
 
 pub fn register_local_components(ecs_world: &mut EcsWorld) {
-    ecs_world.register::<uid::Uid>();
+    ecs_world.register::<Uid>();
 
     ecs_world.register::<phys::Pos>();
     ecs_world.register::<phys::Vel>();
diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs
index 50809815ba..81eb09a249 100644
--- a/common/src/comp/phys.rs
+++ b/common/src/comp/phys.rs
@@ -4,7 +4,7 @@ use vek::*;
 
 // Pos
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
 pub struct Pos(pub Vec3<f32>);
 
 impl Component for Pos {
@@ -13,7 +13,7 @@ impl Component for Pos {
 
 // Vel
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
 pub struct Vel(pub Vec3<f32>);
 
 impl Component for Vel {
@@ -22,7 +22,7 @@ impl Component for Vel {
 
 // Dir
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
 pub struct Dir(pub Vec3<f32>);
 
 impl Component for Dir {
diff --git a/common/src/comp/uid.rs b/common/src/comp/uid.rs
index 3f46979036..b28c1cb136 100644
--- a/common/src/comp/uid.rs
+++ b/common/src/comp/uid.rs
@@ -1,6 +1,7 @@
 use std::{
     collections::HashMap,
     ops::Range,
+    u64,
 };
 use specs::{
     saveload::{Marker, MarkerAllocator},
@@ -13,9 +14,12 @@ use specs::{
 };
 
 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
-pub struct Uid {
-    id: u64,
-    seq: u64,
+pub struct Uid(pub u64);
+
+impl Into<u64> for Uid {
+    fn into(self) -> u64 {
+        self.0
+    }
 }
 
 impl Component for Uid {
@@ -24,22 +28,30 @@ impl Component for Uid {
 
 impl Marker for Uid {
     type Identifier = u64;
-    type Allocator = UidNode;
+    type Allocator = UidAllocator;
 
-    fn id(&self) -> u64 { self.id }
+    fn id(&self) -> u64 { self.0 }
 
     fn update(&mut self, update: Self) {
-        assert_eq!(self.id, update.id);
-        self.seq = update.seq;
+        assert_eq!(self.0, update.0);
     }
 }
 
-pub struct UidNode {
+pub struct UidAllocator {
     pub(crate) range: Range<u64>,
     pub(crate) mapping: HashMap<u64, Entity>,
 }
 
-impl MarkerAllocator<Uid> for UidNode {
+impl UidAllocator {
+    pub fn new() -> Self {
+        Self {
+            range: 0..u64::MAX,
+            mapping: HashMap::new(),
+        }
+    }
+}
+
+impl MarkerAllocator<Uid> for UidAllocator {
     fn allocate(&mut self, entity: Entity, id: Option<u64>) -> Uid {
         let id = id.unwrap_or_else(|| {
             self.range.next().expect("
@@ -49,7 +61,7 @@ impl MarkerAllocator<Uid> for UidNode {
             ")
         });
         self.mapping.insert(id, entity);
-        Uid { id, seq: 0 }
+        Uid(id)
     }
 
     fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> {
diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs
index 11d7155c2e..2e304e20bd 100644
--- a/common/src/msg/client.rs
+++ b/common/src/msg/client.rs
@@ -1,4 +1,4 @@
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Clone, Debug, Serialize, Deserialize)]
 pub enum ClientMsg {
     Chat(String),
     Disconnect,
diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs
index def24a97a4..ea80e1038f 100644
--- a/common/src/msg/server.rs
+++ b/common/src/msg/server.rs
@@ -1,5 +1,13 @@
-#[derive(Debug, Serialize, Deserialize)]
+use crate::comp::phys;
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
 pub enum ServerMsg {
-    Chat(String),
     Shutdown,
+    Chat(String),
+    EntityPhysics {
+        uid: u64,
+        pos: phys::Pos,
+        vel: phys::Vel,
+        dir: phys::Dir,
+    },
 }
diff --git a/common/src/state.rs b/common/src/state.rs
index 1d557cc846..2e516759e8 100644
--- a/common/src/state.rs
+++ b/common/src/state.rs
@@ -3,7 +3,17 @@ use std::time::Duration;
 
 // Library
 use shred::{Fetch, FetchMut};
-use specs::{Builder, Component, DispatcherBuilder, Entity as EcsEntity, World as EcsWorld};
+use specs::{
+    Builder,
+    Component,
+    DispatcherBuilder,
+    Entity as EcsEntity,
+    World as EcsWorld,
+    storage::{
+        Storage as EcsStorage,
+        MaskedStorage as EcsMaskedStorage,
+    },
+};
 use vek::*;
 
 // Crate
@@ -95,9 +105,19 @@ impl State {
             .build()
     }
 
-    /// Write a component
-    pub fn write_component<C: Component>(&mut self, e: EcsEntity, c: C) {
-        let _ = self.ecs_world.write_storage().insert(e, c);
+    /// Write a component attributed to a particular entity
+    pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) {
+        let _ = self.ecs_world.write_storage().insert(entity, comp);
+    }
+
+    /// Read a clone of a component attributed to a particular entity
+    pub fn read_component<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
+        self.ecs_world.read_storage::<C>().get(entity).cloned()
+    }
+
+    /// Get a read-only reference to the storage of a particular component type
+    pub fn read_storage<C: Component>(&self) -> EcsStorage<C, Fetch<EcsMaskedStorage<C>>> {
+        self.ecs_world.read_storage::<C>()
     }
 
     /// Get a reference to the internal ECS world
@@ -105,6 +125,11 @@ impl State {
         &self.ecs_world
     }
 
+    /// Get a mutable reference to the internal ECS world
+    pub fn ecs_world_mut(&mut self) -> &mut EcsWorld {
+        &mut self.ecs_world
+    }
+
     /// Get a reference to the `Changes` structure of the state. This contains
     /// information about state that has changed since the last game tick.
     pub fn changes(&self) -> &Changes {
diff --git a/server/Cargo.toml b/server/Cargo.toml
index a5950b0420..f7f9f31dcb 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -9,3 +9,4 @@ common = { package = "veloren-common", path = "../common" }
 world = { package = "veloren-world", path = "../world" }
 
 specs = "0.14"
+vek = "0.9"
diff --git a/server/src/client.rs b/server/src/client.rs
index 45d42dda73..6d7fd6d660 100644
--- a/server/src/client.rs
+++ b/server/src/client.rs
@@ -3,9 +3,38 @@ use common::{
     msg::{ServerMsg, ClientMsg},
     net::PostBox,
 };
+use crate::Error;
 
 pub struct Client {
     pub ecs_entity: EcsEntity,
     pub postbox: PostBox<ServerMsg, ClientMsg>,
     pub last_ping: f64,
 }
+
+pub struct Clients {
+    clients: Vec<Client>,
+}
+
+impl Clients {
+    pub fn empty() -> Self {
+        Self {
+            clients: Vec::new(),
+        }
+    }
+
+    pub fn add(&mut self, client: Client) {
+        self.clients.push(client);
+    }
+
+    pub fn remove_if<F: FnMut(&mut Client) -> bool>(&mut self, f: F) {
+        self.clients.drain_filter(f);
+    }
+
+    pub fn notify_all(&mut self, msg: ServerMsg) {
+        for client in &mut self.clients {
+            // Consume any errors, deal with them later
+            let _ = client.postbox.send(msg.clone());
+            println!("Sending message...");
+        }
+    }
+}
diff --git a/server/src/lib.rs b/server/src/lib.rs
index fa5eb309ed..d81a40cb0a 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -14,14 +14,25 @@ use std::{
     time::Duration,
     net::SocketAddr,
 };
-use specs::Entity as EcsEntity;
+use specs::{
+    Entity as EcsEntity,
+    world::EntityBuilder as EcsEntityBuilder,
+    Builder,
+    join::Join,
+    saveload::MarkedBuilder,
+};
+use vek::*;
 use common::{
+    comp,
     state::State,
     net::PostOffice,
     msg::{ServerMsg, ClientMsg},
 };
 use world::World;
-use crate::client::Client;
+use crate::client::{
+    Client,
+    Clients,
+};
 
 const CLIENT_TIMEOUT: f64 = 5.0; // Seconds
 
@@ -43,19 +54,23 @@ pub struct Server {
     world: World,
 
     postoffice: PostOffice<ServerMsg, ClientMsg>,
-    clients: Vec<Client>,
+    clients: Clients,
 }
 
 impl Server {
     /// Create a new `Server`.
     #[allow(dead_code)]
     pub fn new() -> Result<Self, Error> {
+        let mut state = State::new();
+
+        state.ecs_world_mut().add_resource(comp::UidAllocator::new());
+
         Ok(Self {
-            state: State::new(),
+            state,
             world: World::new(),
 
             postoffice: PostOffice::new(SocketAddr::from(([0; 4], 59003)))?,
-            clients: Vec::new(),
+            clients: Clients::empty(),
         })
     }
 
@@ -66,6 +81,20 @@ impl Server {
     #[allow(dead_code)]
     pub fn state_mut(&mut self) -> &mut State { &mut self.state }
 
+    /// Build a new entity with a generated UID
+    pub fn build_entity(&mut self) -> EcsEntityBuilder {
+        self.state.ecs_world_mut().create_entity()
+            .marked::<comp::Uid>()
+    }
+
+    /// Build a new player with a generated UID
+    pub fn build_player(&mut self) -> EcsEntityBuilder {
+        self.build_entity()
+            .with(comp::phys::Pos(Vec3::zero()))
+            .with(comp::phys::Vel(Vec3::zero()))
+            .with(comp::phys::Dir(Vec3::unit_y()))
+    }
+
     /// Get a reference to the server's world.
     #[allow(dead_code)]
     pub fn world(&self) -> &World { &self.world }
@@ -107,6 +136,9 @@ impl Server {
         // Tick the client's LocalState (step 3)
         self.state.tick(dt);
 
+        // Synchronise clients with the new state of the world
+        self.sync_clients();
+
         // Finish the tick, pass control back to the frontend (step 6)
         Ok(frontend_events)
     }
@@ -123,14 +155,14 @@ impl Server {
         let mut frontend_events = Vec::new();
 
         for postbox in self.postoffice.new_connections() {
-            // TODO: Don't use this method
-            let ecs_entity = self.state.new_test_player();
+            let ecs_entity = self.build_player()
+                .build();
 
             frontend_events.push(Event::ClientConnected {
                 ecs_entity,
             });
 
-            self.clients.push(Client {
+            self.clients.add(Client {
                 ecs_entity,
                 postbox,
                 last_ping: self.state.get_time(),
@@ -147,7 +179,7 @@ impl Server {
         let state = &mut self.state;
         let mut new_chat_msgs = Vec::new();
 
-        self.clients.drain_filter(|client| {
+        self.clients.remove_if(|client| {
             let mut disconnected = false;
             let new_msgs = client.postbox.new_messages();
 
@@ -163,8 +195,8 @@ impl Server {
                     }
                 }
             } else if
-                state.get_time() - client.last_ping > CLIENT_TIMEOUT ||
-                client.postbox.status().is_some()
+                state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
+                client.postbox.status().is_some() // Postbox eror
             {
                 disconnected = true;
             }
@@ -182,9 +214,7 @@ impl Server {
 
         // Handle new chat messages
         for (ecs_entity, msg) in new_chat_msgs {
-            for client in &mut self.clients {
-                let _ = client.postbox.send(ServerMsg::Chat(msg.clone()));
-            }
+            self.clients.notify_all(ServerMsg::Chat(msg.clone()));
 
             frontend_events.push(Event::Chat {
                 ecs_entity,
@@ -194,4 +224,21 @@ impl Server {
 
         Ok(frontend_events)
     }
+
+    /// Sync client states with the most up to date information
+    fn sync_clients(&mut self) {
+        for (&uid, &pos, &vel, &dir) in (
+            &self.state.ecs_world().read_storage::<comp::Uid>(),
+            &self.state.ecs_world().read_storage::<comp::phys::Pos>(),
+            &self.state.ecs_world().read_storage::<comp::phys::Vel>(),
+            &self.state.ecs_world().read_storage::<comp::phys::Dir>(),
+        ).join() {
+            self.clients.notify_all(ServerMsg::EntityPhysics {
+                uid: uid.into(),
+                pos,
+                vel,
+                dir,
+            });
+        }
+    }
 }