mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Component sync + UID system
Former-commit-id: 5ecddc0e1f9c1a15f99dd167b825178c972da062
This commit is contained in:
parent
ef333877ae
commit
2e613178a0
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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>();
|
||||
|
@ -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 {
|
||||
|
@ -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> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ClientMsg {
|
||||
Chat(String),
|
||||
Disconnect,
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -9,3 +9,4 @@ common = { package = "veloren-common", path = "../common" }
|
||||
world = { package = "veloren-world", path = "../world" }
|
||||
|
||||
specs = "0.14"
|
||||
vek = "0.9"
|
||||
|
@ -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...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user