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 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)

View File

@ -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"

View File

@ -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>();

View File

@ -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 {

View File

@ -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> {

View File

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

View File

@ -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,
},
}

View File

@ -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 {

View File

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

View File

@ -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...");
}
}
}

View File

@ -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,
});
}
}
}