Use Last<C> to check if a physics component changed

This commit is contained in:
timokoesters 2019-07-30 07:24:36 +02:00
parent fbb00601fb
commit c6034bf450
6 changed files with 116 additions and 70 deletions

View File

@ -1,6 +1,6 @@
use specs::{Component, FlaggedStorage, VecStorage};
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct ActionState {
pub moving: bool,
pub on_ground: bool,

15
common/src/comp/last.rs Normal file
View File

@ -0,0 +1,15 @@
use specs::{Component, VecStorage};
use std::{fmt::Debug, marker::Send, ops::Deref};
#[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize)]
pub struct Last<C: Component + PartialEq>(pub C);
impl<C: Component + Send + Sync + PartialEq> Component for Last<C> {
type Storage = VecStorage<Self>;
}
impl<C: Component + PartialEq> PartialEq<C> for Last<C> {
fn eq(&self, other: &C) -> bool {
self.0 == *other
}
}

View File

@ -5,6 +5,7 @@ mod body;
mod controller;
mod inputs;
mod inventory;
mod last;
mod phys;
mod player;
mod stats;
@ -20,6 +21,7 @@ pub use inputs::{
Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding,
};
pub use inventory::{item, Inventory};
pub use last::Last;
pub use phys::{ForceUpdate, Ori, Pos, Vel};
pub use player::Player;
pub use stats::{Dying, Exp, HealthSource, Level, Stats};

View File

@ -2,7 +2,7 @@ use specs::{Component, NullStorage, VecStorage};
use vek::*;
// Position
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Pos(pub Vec3<f32>);
impl Component for Pos {
@ -10,7 +10,7 @@ impl Component for Pos {
}
// Velocity
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Vel(pub Vec3<f32>);
impl Component for Vel {
@ -18,7 +18,7 @@ impl Component for Vel {
}
// Orientation
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Ori(pub Vec3<f32>);
impl Component for Ori {
@ -26,7 +26,7 @@ impl Component for Ori {
}
// ForceUpdate
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ForceUpdate;
impl Component for ForceUpdate {

View File

@ -125,36 +125,45 @@ impl State {
// Create a new Sphynx ECS world.
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsCompPacket, EcsResPacket>) {
// Register server->client synced components.
// Register server -> all clients synced components.
ecs.register_synced::<comp::Body>();
ecs.register_synced::<comp::Player>();
ecs.register_synced::<comp::Stats>();
ecs.register_synced::<comp::CanBuild>();
ecs.register_synced::<comp::LightEmitter>();
// Register components synced by other means
// Register components send from clients -> server
ecs.register::<comp::Controller>();
// Register components send directly from server -> all but one client
ecs.register::<comp::ActionState>();
// Register components synced from client -> server -> all other clients
ecs.register::<comp::Pos>();
ecs.register::<comp::Vel>();
ecs.register::<comp::Ori>();
ecs.register::<comp::MoveDir>();
ecs.register::<comp::OnGround>();
ecs.register::<comp::Controller>();
ecs.register::<comp::Attacking>();
ecs.register::<comp::Wielding>();
ecs.register::<comp::Rolling>();
ecs.register::<comp::Gliding>();
ecs.register::<comp::ActionState>();
// Register client-local components
ecs.register::<comp::AnimationInfo>();
ecs.register::<comp::Jumping>();
// Register server-local components
ecs.register::<comp::Last<comp::Pos>>();
ecs.register::<comp::Last<comp::Vel>>();
ecs.register::<comp::Last<comp::Ori>>();
ecs.register::<comp::Last<comp::ActionState>>();
ecs.register::<comp::Agent>();
ecs.register::<comp::Respawning>();
ecs.register::<comp::Dying>();
ecs.register::<comp::ForceUpdate>();
ecs.register::<comp::Inventory>();
// Controller effects
ecs.register::<comp::MoveDir>();
ecs.register::<comp::OnGround>();
ecs.register::<comp::Attacking>();
ecs.register::<comp::Wielding>();
ecs.register::<comp::Rolling>();
ecs.register::<comp::Gliding>();
// Register synced resources used by the ECS.
ecs.add_resource_synced(TimeOfDay(0.0));

View File

@ -71,13 +71,11 @@ pub struct Server {
impl Server {
/// Create a new `Server` bound to the default socket.
#[allow(dead_code)]
pub fn new(settings: ServerSettings) -> Result<Self, Error> {
Self::bind(settings.address, settings)
}
/// Create a new server bound to the given socket.
#[allow(dead_code)]
pub fn bind<A: Into<SocketAddr>>(addrs: A, settings: ServerSettings) -> Result<Self, Error> {
let (chunk_tx, chunk_rx) = mpsc::channel();
@ -114,31 +112,26 @@ impl Server {
Ok(this)
}
#[allow(dead_code)]
pub fn with_thread_pool(mut self, thread_pool: ThreadPool) -> Self {
self.thread_pool = thread_pool;
self
}
/// Get a reference to the server's game state.
#[allow(dead_code)]
pub fn state(&self) -> &State {
&self.state
}
/// Get a mutable reference to the server's game state.
#[allow(dead_code)]
pub fn state_mut(&mut self) -> &mut State {
&mut self.state
}
/// Get a reference to the server's world.
#[allow(dead_code)]
pub fn world(&self) -> &World {
&self.world
}
/// Build a non-player character.
#[allow(dead_code)]
pub fn create_npc(
&mut self,
pos: comp::Pos,
@ -159,7 +152,6 @@ impl Server {
}
/// Build a static object entity
#[allow(dead_code)]
pub fn create_object(
&mut self,
pos: comp::Pos,
@ -207,7 +199,6 @@ impl Server {
}
/// Execute a single server tick, handle input and update the game state by the given duration.
#[allow(dead_code)]
pub fn tick(&mut self, _input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
// This tick function is the centre of the Veloren universe. Most server-side things are
// managed from here, and as such it's important that it stays organised. Please consult
@ -381,7 +372,6 @@ impl Server {
}
/// Clean up the server after a tick.
#[allow(dead_code)]
pub fn cleanup(&mut self) {
// Cleanup the local state
self.state.cleanup();
@ -716,8 +706,9 @@ impl Server {
self.clients
.notify_registered(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
// TODO: Move this into some new method like `handle_sys_outputs` right after ticking the world
// Handle deaths.
let ecs = &self.state.ecs();
let ecs = self.state.ecs_mut();
let clients = &mut self.clients;
let todo_kill = (&ecs.entities(), &ecs.read_storage::<comp::Dying>())
.join()
@ -774,20 +765,17 @@ impl Server {
// Actually kill them
for entity in todo_kill {
if let Some(client) = self.clients.get_mut(&entity) {
self.state.write_component(entity, comp::Vel(Vec3::zero()));
self.state.write_component(entity, comp::ForceUpdate);
let _ = ecs.write_storage().insert(entity, comp::Vel(Vec3::zero()));
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
client.force_state(ClientState::Dead);
} else {
let _ = self.state.ecs_mut().delete_entity_synced(entity);
let _ = ecs.delete_entity_synced(entity);
continue;
}
}
// Handle respawns
let todo_respawn = (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<comp::Respawning>(),
)
let todo_respawn = (&ecs.entities(), &ecs.read_storage::<comp::Respawning>())
.join()
.map(|(entity, _)| entity)
.collect::<Vec<EcsEntity>>();
@ -795,29 +783,25 @@ impl Server {
for entity in todo_respawn {
if let Some(client) = self.clients.get_mut(&entity) {
client.allow_state(ClientState::Character);
self.state
.ecs_mut()
.write_storage::<comp::Stats>()
ecs.write_storage::<comp::Stats>()
.get_mut(entity)
.map(|stats| stats.revive());
self.state
.ecs_mut()
.write_storage::<comp::Pos>()
ecs.write_storage::<comp::Pos>()
.get_mut(entity)
.map(|pos| pos.0.z += 20.0);
self.state.write_component(entity, comp::ForceUpdate);
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
}
}
// Sync physics
for (entity, &uid, &pos, vel, ori, action_state, force_update) in (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<Uid>(),
&self.state.ecs().read_storage::<comp::Pos>(),
self.state.ecs().read_storage::<comp::Vel>().maybe(),
self.state.ecs().read_storage::<comp::Ori>().maybe(),
self.state.ecs().read_storage::<comp::ActionState>().maybe(),
self.state.ecs().read_storage::<comp::ForceUpdate>().maybe(),
&ecs.entities(),
&ecs.read_storage::<Uid>(),
&ecs.read_storage::<comp::Pos>(),
ecs.read_storage::<comp::Vel>().maybe(),
ecs.read_storage::<comp::Ori>().maybe(),
ecs.read_storage::<comp::ActionState>().maybe(),
ecs.read_storage::<comp::ForceUpdate>().maybe(),
)
.join()
{
@ -829,35 +813,71 @@ impl Server {
action_state: action_state.copied(),
};
let state = &self.state;
let clients = &mut self.clients;
let in_vd = |entity| {
// Get client position.
let client_pos = match state.ecs().read_storage::<comp::Pos>().get(entity) {
Some(pos) => pos.0,
None => return false,
};
// Get client view distance
let client_vd = match state.ecs().read_storage::<comp::Player>().get(entity) {
Some(comp::Player {
view_distance: Some(vd),
..
}) => *vd,
_ => return false,
};
let in_vd_and_changed = |entity| {
let mut last_pos = ecs.write_storage::<comp::Last<comp::Pos>>();
let mut last_vel = ecs.write_storage::<comp::Last<comp::Vel>>();
let mut last_ori = ecs.write_storage::<comp::Last<comp::Ori>>();
let mut last_action_state = ecs.write_storage::<comp::Last<comp::ActionState>>();
(pos.0 - client_pos)
.map2(TerrainChunkSize::SIZE, |d, sz| {
(d.abs() as u32 / sz).checked_sub(2).unwrap_or(0)
})
.magnitude_squared()
< client_vd.pow(2)
if let (
Some(client_pos),
Some(client_vel),
Some(client_ori),
Some(client_action_state),
Some(client_vd),
) = (
ecs.read_storage::<comp::Pos>().get(entity),
ecs.read_storage::<comp::Vel>().get(entity),
ecs.read_storage::<comp::Ori>().get(entity),
ecs.read_storage::<comp::ActionState>().get(entity),
ecs.read_storage::<comp::Player>()
.get(entity)
.map(|pl| pl.view_distance)
.and_then(|v| v),
) {
// If nothing changed...
if last_pos
.get(entity)
.map(|&l| l == *client_pos)
.unwrap_or(false)
&& last_vel
.get(entity)
.map(|&l| l == *client_vel)
.unwrap_or(false)
&& last_ori
.get(entity)
.map(|&l| l == *client_ori)
.unwrap_or(false)
&& last_action_state
.get(entity)
.map(|&l| l == *client_action_state)
.unwrap_or(false)
{
return false;
}
let _ = last_pos.insert(entity, comp::Last(*client_pos));
let _ = last_vel.insert(entity, comp::Last(*client_vel));
let _ = last_ori.insert(entity, comp::Last(*client_ori));
let _ = last_action_state.insert(entity, comp::Last(*client_action_state));
// Check if the entity is in the client's range
(pos.0 - client_pos.0)
.map2(TerrainChunkSize::SIZE, |d, sz| {
(d.abs() as u32 / sz).checked_sub(2).unwrap_or(0)
})
.magnitude_squared()
< client_vd.pow(2)
} else {
false
}
};
match force_update {
Some(_) => clients.notify_ingame_if(msg, in_vd),
None => clients.notify_ingame_if_except(entity, msg, in_vd),
Some(_) => clients.notify_ingame_if(msg, in_vd_and_changed),
None => clients.notify_ingame_if_except(entity, msg, in_vd_and_changed),
}
}