From 62b91eb01b2d44ccd9f1b57dc61bd3dbc92dd653 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 10 Apr 2019 18:23:27 +0100 Subject: [PATCH 01/16] Integrated Sphynx Former-commit-id: 5d96983a385bb77a2876aa7439158252b7e2f0fc --- chat-cli/src/main.rs | 7 +- client/src/error.rs | 1 + client/src/lib.rs | 55 ++++++++------- common/Cargo.toml | 2 + common/src/comp/character.rs | 26 +++++-- common/src/comp/mod.rs | 21 ++---- common/src/comp/phys.rs | 21 ++---- common/src/comp/player.rs | 18 +++++ common/src/comp/uid.rs | 84 ---------------------- common/src/comp/util.rs | 12 ---- common/src/lib.rs | 2 +- common/src/msg/client.rs | 15 ++-- common/src/msg/ecs_packet.rs | 29 ++++++++ common/src/msg/mod.rs | 6 +- common/src/msg/server.rs | 19 ++--- common/src/net/post.rs | 39 ++++++++++ common/src/state.rs | 126 +++++++++++++-------------------- server-cli/src/main.rs | 6 +- server/src/client.rs | 25 +++++-- server/src/lib.rs | 133 +++++++++++++++++------------------ voxygen/src/menu/main/mod.rs | 9 ++- voxygen/src/scene/mod.rs | 3 +- voxygen/src/session.rs | 2 + 23 files changed, 317 insertions(+), 344 deletions(-) create mode 100644 common/src/comp/player.rs delete mode 100644 common/src/comp/uid.rs delete mode 100644 common/src/comp/util.rs create mode 100644 common/src/msg/ecs_packet.rs diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs index fa93a3df36..deb5cb126c 100644 --- a/chat-cli/src/main.rs +++ b/chat-cli/src/main.rs @@ -1,7 +1,10 @@ use std::time::Duration; use log::info; use client::{Input, Client, Event}; -use common::clock::Clock; +use common::{ + comp, + clock::Clock, +}; const FPS: u64 = 60; @@ -15,7 +18,7 @@ fn main() { let mut clock = Clock::new(); // Create client - let mut client = Client::new(([127, 0, 0, 1], 59003)) + let mut client = Client::new(([127, 0, 0, 1], 59003), comp::Player::new("test".to_string()), None) .expect("Failed to create client instance"); client.send_chat("Hello!".to_string()); diff --git a/client/src/error.rs b/client/src/error.rs index e702a67b0f..b4b3ce4742 100644 --- a/client/src/error.rs +++ b/client/src/error.rs @@ -3,6 +3,7 @@ use common::net::PostError; #[derive(Debug)] pub enum Error { Network(PostError), + ServerWentMad, ServerTimeout, ServerShutdown, Other(String), diff --git a/client/src/lib.rs b/client/src/lib.rs index 0e364d9701..0fcccc1e27 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -50,11 +50,31 @@ pub struct Client { impl Client { /// Create a new `Client`. #[allow(dead_code)] - pub fn new>(addr: A) -> Result { - let state = State::new(); + pub fn new>( + addr: A, + player: comp::Player, + character: Option, + ) -> Result { let mut postbox = PostBox::to_server(addr)?; + // Send connection request + postbox.send(ClientMsg::Connect { + player, + character, + }); + + // Wait for handshake from server + let (state, player) = match postbox.next_message() { + Some(ServerMsg::Handshake { ecs_state, player_entity }) => { + println!("STATE PACKAGE! {:?}", ecs_state); + let mut state = State::from_state_package(ecs_state); + let player_entity = state.ecs().entity_from_uid(player_entity); + (state, player_entity) + }, + _ => return Err(Error::ServerWentMad), + }; + Ok(Self { thread_pool: threadpool::Builder::new() .thread_name("veloren-worker".into()) @@ -65,7 +85,7 @@ impl Client { tick: 0, state, - player: None, + player, // Testing world: World::new(), @@ -85,12 +105,6 @@ impl Client { self } - // TODO: Get rid of this - pub fn load_chunk(&mut self, pos: Vec3) { - self.state.terrain_mut().insert(pos, self.world.generate_chunk(pos)); - self.state.changes_mut().new_chunks.push(pos); - } - /// Get a reference to the client's game state. #[allow(dead_code)] pub fn state(&self) -> &State { &self.state } @@ -187,28 +201,15 @@ impl Client { for msg in new_msgs { match msg { + ServerMsg::Handshake { .. } => return Err(Error::ServerWentMad), ServerMsg::Shutdown => return Err(Error::ServerShutdown), ServerMsg::Ping => self.postbox.send(ClientMsg::Pong), ServerMsg::Pong => {}, ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)), - ServerMsg::SetPlayerEntity(uid) => { - let ecs_entity = self.state - .get_entity(uid) - .unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build()); - - self.player = Some(ecs_entity); - }, - ServerMsg::EntityPhysics { uid, pos, vel, dir } => { - let ecs_entity = self.state - .get_entity(uid) - .unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build()); - - self.state.write_component(ecs_entity, pos); - self.state.write_component(ecs_entity, vel); - self.state.write_component(ecs_entity, dir); - }, - ServerMsg::EntityDeleted(uid) => { - self.state.delete_entity(uid); + ServerMsg::SetPlayerEntity(uid) => self.player = Some(self.state.ecs().entity_from_uid(uid).unwrap()), // TODO: Don't unwrap here! + ServerMsg::EcsSync(sync_package) => { + println!("SYNC PACKAGE! {:?}", sync_package); + self.state.ecs_mut().sync_with_package(sync_package) }, } } diff --git a/common/Cargo.toml b/common/Cargo.toml index 3fa8fd33a3..75c032bf18 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -5,6 +5,8 @@ authors = ["Joshua Barretto ", "Maciej Ćwięka ; +impl Character { + // TODO: Remove this + pub fn test() -> Self { + Self { + race: Race::Human, + gender: Gender::Unspecified, + head: (), + chest: (), + belt: (), + arms: (), + feet: (), + } + } +} + +impl Component for Character { + type Storage = FlaggedStorage>; } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 776a67485c..f5fa9254b7 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -1,20 +1,7 @@ +pub mod character; +pub mod player; pub mod phys; -pub mod uid; -pub mod util; // Reexports -pub use uid::{Uid, UidAllocator}; - -use specs::World as EcsWorld; - -pub fn register_local_components(ecs_world: &mut EcsWorld) { - ecs_world.register::(); - ecs_world.add_resource(UidAllocator::new()); - - ecs_world.register::(); - - ecs_world.register::(); - ecs_world.register::(); - ecs_world.register::(); - ecs_world.register::(); -} +pub use character::Character; +pub use player::Player; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 3db813276d..20e2b59192 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -1,5 +1,4 @@ -// Library -use specs::{Component, VecStorage}; +use specs::{Component, VecStorage, FlaggedStorage}; use vek::*; // Pos @@ -8,7 +7,7 @@ use vek::*; pub struct Pos(pub Vec3); impl Component for Pos { - type Storage = VecStorage; + type Storage = FlaggedStorage>; } // Vel @@ -17,7 +16,7 @@ impl Component for Pos { pub struct Vel(pub Vec3); impl Component for Vel { - type Storage = VecStorage; + type Storage = FlaggedStorage>; } // Dir @@ -26,17 +25,5 @@ impl Component for Vel { pub struct Dir(pub Vec3); impl Component for Dir { - type Storage = VecStorage; -} - -// UpdateKind - -#[derive(Copy, Clone, Debug)] -pub enum UpdateKind { - Passive, - Force, -} - -impl Component for UpdateKind { - type Storage = VecStorage; + type Storage = FlaggedStorage>; } diff --git a/common/src/comp/player.rs b/common/src/comp/player.rs new file mode 100644 index 0000000000..46f1a048a4 --- /dev/null +++ b/common/src/comp/player.rs @@ -0,0 +1,18 @@ +use specs::{Component, VecStorage, FlaggedStorage}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Player { + pub alias: String, +} + +impl Player { + pub fn new(alias: String) -> Self { + Self { + alias, + } + } +} + +impl Component for Player { + type Storage = FlaggedStorage>; +} diff --git a/common/src/comp/uid.rs b/common/src/comp/uid.rs deleted file mode 100644 index 6ea657dada..0000000000 --- a/common/src/comp/uid.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::{ - collections::HashMap, - ops::Range, - u64, - fmt, -}; -use specs::{ - saveload::{Marker, MarkerAllocator}, - world::EntitiesRes, - Component, - VecStorage, - Entity, - Join, - ReadStorage, -}; - -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct Uid(pub u64); - -impl Into for Uid { - fn into(self) -> u64 { - self.0 - } -} - -impl fmt::Display for Uid { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Component for Uid { - type Storage = VecStorage; -} - -impl Marker for Uid { - type Identifier = u64; - type Allocator = UidAllocator; - - fn id(&self) -> u64 { self.0 } - - fn update(&mut self, update: Self) { - assert_eq!(self.0, update.0); - } -} - -pub struct UidAllocator { - pub(crate) range: Range, - pub(crate) mapping: HashMap, -} - -impl UidAllocator { - pub fn new() -> Self { - Self { - range: 0..u64::MAX, - mapping: HashMap::new(), - } - } -} - -impl MarkerAllocator for UidAllocator { - fn allocate(&mut self, entity: Entity, id: Option) -> Uid { - let id = id.unwrap_or_else(|| { - self.range.next().expect(" - Id range must be effectively endless. - Somehow, you ran this program for longer than the lifetime of the universe. - It's probably time to stop playing and prepare for your imminent extinction. - ") - }); - self.mapping.insert(id, entity); - Uid(id) - } - - fn retrieve_entity_internal(&self, id: u64) -> Option { - self.mapping.get(&id).cloned() - } - - fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage) { - self.mapping = (&*entities, storage) - .join() - .map(|(e, m)| (m.id(), e)) - .collect(); - } -} diff --git a/common/src/comp/util.rs b/common/src/comp/util.rs deleted file mode 100644 index 0ea2d35ebc..0000000000 --- a/common/src/comp/util.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Library -use specs::{Component, NullStorage}; -use vek::*; - -// Pos - -#[derive(Copy, Clone, Debug, Default)] -pub struct New; - -impl Component for New { - type Storage = NullStorage; -} diff --git a/common/src/lib.rs b/common/src/lib.rs index 9fc68777e4..72bcade3f8 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(euclidean_division, duration_float, try_from, trait_alias)] +#![feature(euclidean_division, duration_float, trait_alias)] #[macro_use] extern crate serde_derive; diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index 149cd07ad4..e8b59414ed 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -1,17 +1,18 @@ -use crate::comp::{ - Uid, - phys, -}; +use crate::comp; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ClientMsg { + Connect { + player: comp::Player, + character: Option, + }, Ping, Pong, Chat(String), PlayerPhysics { - pos: phys::Pos, - vel: phys::Vel, - dir: phys::Dir, + pos: comp::phys::Pos, + vel: comp::phys::Vel, + dir: comp::phys::Dir, }, Disconnect, } diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs new file mode 100644 index 0000000000..a3b4a9e463 --- /dev/null +++ b/common/src/msg/ecs_packet.rs @@ -0,0 +1,29 @@ +use std::marker::PhantomData; +use serde_derive::{Serialize, Deserialize}; +use crate::comp; + +// Automatically derive From for Packet for each variant Packet::T(T) +sphynx::sum_type! { + #[derive(Clone, Debug, Serialize, Deserialize)] + pub enum EcsPacket { + Pos(comp::phys::Pos), + Vel(comp::phys::Vel), + Dir(comp::phys::Dir), + Character(comp::Character), + Player(comp::Player), + } +} +// Automatically derive From for Phantom for each variant Phantom::T(PhantomData) +sphynx::sum_type! { + #[derive(Clone, Debug, Serialize, Deserialize)] + pub enum EcsPhantom { + Pos(PhantomData), + Vel(PhantomData), + Dir(PhantomData), + Character(PhantomData), + Player(PhantomData), + } +} +impl sphynx::Packet for EcsPacket { + type Phantom = EcsPhantom; +} diff --git a/common/src/msg/mod.rs b/common/src/msg/mod.rs index ee18da8f4d..b38939eeb2 100644 --- a/common/src/msg/mod.rs +++ b/common/src/msg/mod.rs @@ -1,6 +1,8 @@ +pub mod ecs_packet; pub mod server; pub mod client; // Reexports -pub use server::ServerMsg; -pub use client::ClientMsg; +pub use self::server::ServerMsg; +pub use self::client::ClientMsg; +pub use self::ecs_packet::EcsPacket; diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 871215138e..21a21f506e 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -1,20 +1,15 @@ -use crate::comp::{ - Uid, - phys, -}; +use super::EcsPacket; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ServerMsg { + Handshake { + ecs_state: sphynx::StatePackage, + player_entity: u64, + }, Shutdown, Ping, Pong, Chat(String), - SetPlayerEntity(Uid), - EntityPhysics { - uid: Uid, - pos: phys::Pos, - vel: phys::Vel, - dir: phys::Dir, - }, - EntityDeleted(Uid), + SetPlayerEntity(u64), + EcsSync(sphynx::SyncPackage), } diff --git a/common/src/net/post.rs b/common/src/net/post.rs index b9160ec35b..afd3d8956d 100644 --- a/common/src/net/post.rs +++ b/common/src/net/post.rs @@ -251,6 +251,45 @@ impl PostBox { let _ = self.send_tx.send(data); } + // TODO: This method is super messy + pub fn next_message(&mut self) -> Option { + if self.err.is_some() { + return None; + } + + loop { + let mut events = Events::with_capacity(10); + if let Err(err) = self.poll.poll( + &mut events, + Some(Duration::new(0, 0)), + ) { + self.err = Some(err.into()); + return None; + } + + for event in events { + match event.token() { + // Keep reading new messages from the channel + RECV_TOK => loop { + match self.recv_rx.try_recv() { + Ok(Ok(msg)) => return Some(msg), + Err(TryRecvError::Empty) => break, + Err(err) => { + self.err = Some(err.into()); + return None; + }, + Ok(Err(err)) => { + self.err = Some(err); + return None; + }, + } + }, + tok => panic!("Unexpected event token '{:?}'", tok), + } + } + } + } + pub fn new_messages(&mut self) -> impl ExactSizeIterator { let mut msgs = VecDeque::new(); diff --git a/common/src/state.rs b/common/src/state.rs index b395dd35de..1e7f9a076d 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -6,18 +6,19 @@ use specs::{ DispatcherBuilder, EntityBuilder as EcsEntityBuilder, Entity as EcsEntity, - World as EcsWorld, storage::{ Storage as EcsStorage, MaskedStorage as EcsMaskedStorage, }, saveload::{MarkedBuilder, MarkerAllocator}, }; +use sphynx; use vek::*; use crate::{ comp, sys, terrain::TerrainMap, + msg::EcsPacket, }; /// How much faster should an in-game day be compared to a real day? @@ -59,95 +60,74 @@ impl Changes { /// A type used to represent game state stored on both the client and the server. This includes /// things like entity components, terrain data, and global state like weather, time of day, etc. pub struct State { - ecs_world: EcsWorld, + ecs: sphynx::World, changes: Changes, } impl State { /// Create a new `State`. pub fn new() -> Self { - let mut ecs_world = EcsWorld::new(); - - // Register resources used by the ECS - ecs_world.add_resource(TimeOfDay(0.0)); - ecs_world.add_resource(Time(0.0)); - ecs_world.add_resource(DeltaTime(0.0)); - ecs_world.add_resource(TerrainMap::new()); - - // Register common components with the state - comp::register_local_components(&mut ecs_world); - Self { - ecs_world, + ecs: sphynx::World::new(specs::World::new(), Self::setup_sphynx_world), changes: Changes::default(), } } + /// Create a new `State` from an ECS state package + pub fn from_state_package(state_package: sphynx::StatePackage) -> Self { + Self { + ecs: sphynx::World::from_state_package(specs::World::new(), Self::setup_sphynx_world, state_package), + changes: Changes::default(), + } + } + + // Create a new Sphynx ECS world + fn setup_sphynx_world(ecs: &mut sphynx::World) { + // Register synced components + ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); + + // Register resources used by the ECS + ecs.internal_mut().add_resource(TimeOfDay(0.0)); + ecs.internal_mut().add_resource(Time(0.0)); + ecs.internal_mut().add_resource(DeltaTime(0.0)); + ecs.internal_mut().add_resource(TerrainMap::new()); + } + /// Register a component with the state's ECS pub fn with_component(mut self) -> Self where ::Storage: Default { - self.ecs_world.register::(); + self.ecs.internal_mut().register::(); self } - /// Build a new entity with a generated UID - pub fn build_uid_entity(&mut self) -> EcsEntityBuilder { - self.ecs_world.create_entity() - .with(comp::util::New) - .marked::() - } - - /// Build an entity with a specific UID - pub fn build_uid_entity_with_uid(&mut self, uid: comp::Uid) -> EcsEntityBuilder { - let builder = self.build_uid_entity(); - - builder.world - .write_resource::() - .allocate(builder.entity, Some(uid.into())); - - builder - } - - /// Get an entity from its UID, if it exists - pub fn get_entity(&self, uid: comp::Uid) -> Option { - // Find the ECS entity from its UID - self.ecs_world - .read_resource::() - .retrieve_entity_internal(uid.into()) - } - - /// Delete an entity from the state's ECS, if it exists - pub fn delete_entity(&mut self, uid: comp::Uid) { - // Find the ECS entity from its UID - let ecs_entity = self.ecs_world - .read_resource::() - .retrieve_entity_internal(uid.into()); - - // Delete the ECS entity, if it exists - if let Some(ecs_entity) = ecs_entity { - let _ = self.ecs_world.delete_entity(ecs_entity); - } - } - /// Write a component attributed to a particular entity pub fn write_component(&mut self, entity: EcsEntity, comp: C) { - let _ = self.ecs_world.write_storage().insert(entity, comp); + let _ = self.ecs.internal_mut().write_storage().insert(entity, comp); + } + + /// Read a component attributed to a particular entity + pub fn read_component_cloned(&self, entity: EcsEntity) -> Option { + self.ecs.internal().read_storage().get(entity).cloned() } /// Get a read-only reference to the storage of a particular component type pub fn read_storage(&self) -> EcsStorage>> { - self.ecs_world.read_storage::() + self.ecs.internal().read_storage::() } /// Get a reference to the internal ECS world - pub fn ecs_world(&self) -> &EcsWorld { - &self.ecs_world + pub fn ecs(&self) -> &sphynx::World { + &self.ecs } /// Get a mutable reference to the internal ECS world - pub fn ecs_world_mut(&mut self) -> &mut EcsWorld { - &mut self.ecs_world + pub fn ecs_mut(&mut self) -> &mut sphynx::World { + &mut self.ecs } /// Get a reference to the `Changes` structure of the state. This contains @@ -156,54 +136,46 @@ impl State { &self.changes } - // TODO: Get rid of this since it shouldn't be needed - pub fn changes_mut(&mut self) -> &mut Changes { - &mut self.changes - } - /// Get the current in-game time of day. /// /// Note that this should not be used for physics, animations or other such localised timings. pub fn get_time_of_day(&self) -> f64 { - self.ecs_world.read_resource::().0 + self.ecs.internal().read_resource::().0 } /// Get the current in-game time. /// /// Note that this does not correspond to the time of day. pub fn get_time(&self) -> f64 { - self.ecs_world.read_resource::