Component sync + UID system

Former-commit-id: 5ecddc0e1f9c1a15f99dd167b825178c972da062
This commit is contained in:
Joshua Barretto 2019-03-04 19:50:26 +00:00
parent ef333877ae
commit 2e613178a0
11 changed files with 181 additions and 40 deletions

View File

@ -14,8 +14,9 @@ use std::{
}; };
use vek::*; use vek::*;
use threadpool; use threadpool;
use specs::Builder;
use common::{ use common::{
comp::phys::Vel, comp,
state::State, state::State,
terrain::TerrainChunk, terrain::TerrainChunk,
net::PostBox, net::PostBox,
@ -96,6 +97,13 @@ impl Client {
#[allow(dead_code)] #[allow(dead_code)]
pub fn state_mut(&mut self) -> &mut State { &mut self.state } 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 /// Get the player entity
#[allow(dead_code)] #[allow(dead_code)]
pub fn player(&self) -> Option<EcsEntity> { pub fn player(&self) -> Option<EcsEntity> {
@ -141,7 +149,7 @@ impl Client {
const PLAYER_VELOCITY: f32 = 100.0; const PLAYER_VELOCITY: f32 = 100.0;
// TODO: Set acceleration instead // 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) // Tick the client's LocalState (step 3)
@ -170,11 +178,20 @@ impl Client {
self.last_ping = self.state.get_time(); self.last_ping = self.state.get_time();
for msg in new_msgs { for msg in new_msgs {
println!("Received message");
match msg { match msg {
ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
ServerMsg::Shutdown => return Err(Error::ServerShutdown), 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) Ok(frontend_events)

View File

@ -7,7 +7,7 @@ edition = "2018"
[dependencies] [dependencies]
specs = { version = "0.14", features = ["serde"] } specs = { version = "0.14", features = ["serde"] }
shred = "0.7" shred = "0.7"
vek = "0.9" vek = { version = "0.9", features = ["serde"] }
dot_vox = "1.0" dot_vox = "1.0"
threadpool = "1.7" threadpool = "1.7"
mio = "0.6" mio = "0.6"

View File

@ -1,11 +1,13 @@
pub mod phys; pub mod phys;
pub mod uid; pub mod uid;
// External // Reexports
pub use uid::{Uid, UidAllocator};
use specs::World as EcsWorld; use specs::World as EcsWorld;
pub fn register_local_components(ecs_world: &mut 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::Pos>();
ecs_world.register::<phys::Vel>(); ecs_world.register::<phys::Vel>();

View File

@ -4,7 +4,7 @@ use vek::*;
// Pos // Pos
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Pos(pub Vec3<f32>); pub struct Pos(pub Vec3<f32>);
impl Component for Pos { impl Component for Pos {
@ -13,7 +13,7 @@ impl Component for Pos {
// Vel // Vel
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Vel(pub Vec3<f32>); pub struct Vel(pub Vec3<f32>);
impl Component for Vel { impl Component for Vel {
@ -22,7 +22,7 @@ impl Component for Vel {
// Dir // Dir
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Dir(pub Vec3<f32>); pub struct Dir(pub Vec3<f32>);
impl Component for Dir { impl Component for Dir {

View File

@ -1,6 +1,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
ops::Range, ops::Range,
u64,
}; };
use specs::{ use specs::{
saveload::{Marker, MarkerAllocator}, saveload::{Marker, MarkerAllocator},
@ -13,9 +14,12 @@ use specs::{
}; };
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Uid { pub struct Uid(pub u64);
id: u64,
seq: u64, impl Into<u64> for Uid {
fn into(self) -> u64 {
self.0
}
} }
impl Component for Uid { impl Component for Uid {
@ -24,22 +28,30 @@ impl Component for Uid {
impl Marker for Uid { impl Marker for Uid {
type Identifier = u64; 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) { fn update(&mut self, update: Self) {
assert_eq!(self.id, update.id); assert_eq!(self.0, update.0);
self.seq = update.seq;
} }
} }
pub struct UidNode { pub struct UidAllocator {
pub(crate) range: Range<u64>, pub(crate) range: Range<u64>,
pub(crate) mapping: HashMap<u64, Entity>, 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 { fn allocate(&mut self, entity: Entity, id: Option<u64>) -> Uid {
let id = id.unwrap_or_else(|| { let id = id.unwrap_or_else(|| {
self.range.next().expect(" self.range.next().expect("
@ -49,7 +61,7 @@ impl MarkerAllocator<Uid> for UidNode {
") ")
}); });
self.mapping.insert(id, entity); self.mapping.insert(id, entity);
Uid { id, seq: 0 } Uid(id)
} }
fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> { fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> {

View File

@ -1,4 +1,4 @@
#[derive(Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ClientMsg { pub enum ClientMsg {
Chat(String), Chat(String),
Disconnect, Disconnect,

View File

@ -1,5 +1,13 @@
#[derive(Debug, Serialize, Deserialize)] use crate::comp::phys;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ServerMsg { pub enum ServerMsg {
Chat(String),
Shutdown, Shutdown,
Chat(String),
EntityPhysics {
uid: u64,
pos: phys::Pos,
vel: phys::Vel,
dir: phys::Dir,
},
} }

View File

@ -3,7 +3,17 @@ use std::time::Duration;
// Library // Library
use shred::{Fetch, FetchMut}; 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::*; use vek::*;
// Crate // Crate
@ -95,9 +105,19 @@ impl State {
.build() .build()
} }
/// Write a component /// Write a component attributed to a particular entity
pub fn write_component<C: Component>(&mut self, e: EcsEntity, c: C) { pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) {
let _ = self.ecs_world.write_storage().insert(e, 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 /// Get a reference to the internal ECS world
@ -105,6 +125,11 @@ impl State {
&self.ecs_world &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 /// Get a reference to the `Changes` structure of the state. This contains
/// information about state that has changed since the last game tick. /// information about state that has changed since the last game tick.
pub fn changes(&self) -> &Changes { pub fn changes(&self) -> &Changes {

View File

@ -9,3 +9,4 @@ common = { package = "veloren-common", path = "../common" }
world = { package = "veloren-world", path = "../world" } world = { package = "veloren-world", path = "../world" }
specs = "0.14" specs = "0.14"
vek = "0.9"

View File

@ -3,9 +3,38 @@ use common::{
msg::{ServerMsg, ClientMsg}, msg::{ServerMsg, ClientMsg},
net::PostBox, net::PostBox,
}; };
use crate::Error;
pub struct Client { pub struct Client {
pub ecs_entity: EcsEntity, pub ecs_entity: EcsEntity,
pub postbox: PostBox<ServerMsg, ClientMsg>, pub postbox: PostBox<ServerMsg, ClientMsg>,
pub last_ping: f64, 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...");
}
}
}

View File

@ -14,14 +14,25 @@ use std::{
time::Duration, time::Duration,
net::SocketAddr, 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::{ use common::{
comp,
state::State, state::State,
net::PostOffice, net::PostOffice,
msg::{ServerMsg, ClientMsg}, msg::{ServerMsg, ClientMsg},
}; };
use world::World; use world::World;
use crate::client::Client; use crate::client::{
Client,
Clients,
};
const CLIENT_TIMEOUT: f64 = 5.0; // Seconds const CLIENT_TIMEOUT: f64 = 5.0; // Seconds
@ -43,19 +54,23 @@ pub struct Server {
world: World, world: World,
postoffice: PostOffice<ServerMsg, ClientMsg>, postoffice: PostOffice<ServerMsg, ClientMsg>,
clients: Vec<Client>, clients: Clients,
} }
impl Server { impl Server {
/// Create a new `Server`. /// Create a new `Server`.
#[allow(dead_code)] #[allow(dead_code)]
pub fn new() -> Result<Self, Error> { pub fn new() -> Result<Self, Error> {
let mut state = State::new();
state.ecs_world_mut().add_resource(comp::UidAllocator::new());
Ok(Self { Ok(Self {
state: State::new(), state,
world: World::new(), world: World::new(),
postoffice: PostOffice::new(SocketAddr::from(([0; 4], 59003)))?, postoffice: PostOffice::new(SocketAddr::from(([0; 4], 59003)))?,
clients: Vec::new(), clients: Clients::empty(),
}) })
} }
@ -66,6 +81,20 @@ impl Server {
#[allow(dead_code)] #[allow(dead_code)]
pub fn state_mut(&mut self) -> &mut State { &mut self.state } 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. /// Get a reference to the server's world.
#[allow(dead_code)] #[allow(dead_code)]
pub fn world(&self) -> &World { &self.world } pub fn world(&self) -> &World { &self.world }
@ -107,6 +136,9 @@ impl Server {
// Tick the client's LocalState (step 3) // Tick the client's LocalState (step 3)
self.state.tick(dt); 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) // Finish the tick, pass control back to the frontend (step 6)
Ok(frontend_events) Ok(frontend_events)
} }
@ -123,14 +155,14 @@ impl Server {
let mut frontend_events = Vec::new(); let mut frontend_events = Vec::new();
for postbox in self.postoffice.new_connections() { for postbox in self.postoffice.new_connections() {
// TODO: Don't use this method let ecs_entity = self.build_player()
let ecs_entity = self.state.new_test_player(); .build();
frontend_events.push(Event::ClientConnected { frontend_events.push(Event::ClientConnected {
ecs_entity, ecs_entity,
}); });
self.clients.push(Client { self.clients.add(Client {
ecs_entity, ecs_entity,
postbox, postbox,
last_ping: self.state.get_time(), last_ping: self.state.get_time(),
@ -147,7 +179,7 @@ impl Server {
let state = &mut self.state; let state = &mut self.state;
let mut new_chat_msgs = Vec::new(); let mut new_chat_msgs = Vec::new();
self.clients.drain_filter(|client| { self.clients.remove_if(|client| {
let mut disconnected = false; let mut disconnected = false;
let new_msgs = client.postbox.new_messages(); let new_msgs = client.postbox.new_messages();
@ -163,8 +195,8 @@ impl Server {
} }
} }
} else if } else if
state.get_time() - client.last_ping > CLIENT_TIMEOUT || state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
client.postbox.status().is_some() client.postbox.status().is_some() // Postbox eror
{ {
disconnected = true; disconnected = true;
} }
@ -182,9 +214,7 @@ impl Server {
// Handle new chat messages // Handle new chat messages
for (ecs_entity, msg) in new_chat_msgs { for (ecs_entity, msg) in new_chat_msgs {
for client in &mut self.clients { self.clients.notify_all(ServerMsg::Chat(msg.clone()));
let _ = client.postbox.send(ServerMsg::Chat(msg.clone()));
}
frontend_events.push(Event::Chat { frontend_events.push(Event::Chat {
ecs_entity, ecs_entity,
@ -194,4 +224,21 @@ impl Server {
Ok(frontend_events) 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,
});
}
}
} }