From 05980def09e4adc76776787e960f6b3b4b7e94ae Mon Sep 17 00:00:00 2001 From: timokoesters Date: Tue, 30 Jul 2019 07:24:36 +0200 Subject: [PATCH] Use Last to check if a physics component changed --- common/src/comp/action_state.rs | 2 +- common/src/comp/last.rs | 15 ++++ common/src/comp/mod.rs | 2 + common/src/comp/phys.rs | 8 +- common/src/state.rs | 29 ++++--- server/src/lib.rs | 130 ++++++++++++++++++-------------- 6 files changed, 116 insertions(+), 70 deletions(-) create mode 100644 common/src/comp/last.rs diff --git a/common/src/comp/action_state.rs b/common/src/comp/action_state.rs index 546f551ef7..18626300a4 100644 --- a/common/src/comp/action_state.rs +++ b/common/src/comp/action_state.rs @@ -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, diff --git a/common/src/comp/last.rs b/common/src/comp/last.rs new file mode 100644 index 0000000000..2ec18b6712 --- /dev/null +++ b/common/src/comp/last.rs @@ -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(pub C); + +impl Component for Last { + type Storage = VecStorage; +} + +impl PartialEq for Last { + fn eq(&self, other: &C) -> bool { + self.0 == *other + } +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 91d0b380a7..86a948dd21 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -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}; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 6fe1e603bb..0c674d63ec 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -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); 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); 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); 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 { diff --git a/common/src/state.rs b/common/src/state.rs index 06c6d49fe4..9269d6e101 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -125,36 +125,45 @@ impl State { // Create a new Sphynx ECS world. fn setup_sphynx_world(ecs: &mut sphynx::World) { - // Register server->client synced components. + // Register server -> all clients synced components. ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); - // Register components synced by other means + // Register components send from clients -> server + ecs.register::(); + + // Register components send directly from server -> all but one client + ecs.register::(); + + // Register components synced from client -> server -> all other clients ecs.register::(); ecs.register::(); ecs.register::(); - ecs.register::(); - ecs.register::(); - ecs.register::(); - ecs.register::(); - ecs.register::(); - ecs.register::(); - ecs.register::(); - ecs.register::(); // Register client-local components ecs.register::(); ecs.register::(); // Register server-local components + ecs.register::>(); + ecs.register::>(); + ecs.register::>(); + ecs.register::>(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); + // Controller effects + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); // Register synced resources used by the ECS. ecs.add_resource_synced(TimeOfDay(0.0)); diff --git a/server/src/lib.rs b/server/src/lib.rs index fd66034b79..662c7f928c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -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::bind(settings.address, settings) } /// Create a new server bound to the given socket. - #[allow(dead_code)] pub fn bind>(addrs: A, settings: ServerSettings) -> Result { 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, 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::()) .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::(), - ) + let todo_respawn = (&ecs.entities(), &ecs.read_storage::()) .join() .map(|(entity, _)| entity) .collect::>(); @@ -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::() + ecs.write_storage::() .get_mut(entity) .map(|stats| stats.revive()); - self.state - .ecs_mut() - .write_storage::() + ecs.write_storage::() .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::(), - &self.state.ecs().read_storage::(), - self.state.ecs().read_storage::().maybe(), - self.state.ecs().read_storage::().maybe(), - self.state.ecs().read_storage::().maybe(), - self.state.ecs().read_storage::().maybe(), + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), + ecs.read_storage::().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::().get(entity) { - Some(pos) => pos.0, - None => return false, - }; - // Get client view distance - let client_vd = match state.ecs().read_storage::().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::>(); + let mut last_vel = ecs.write_storage::>(); + let mut last_ori = ecs.write_storage::>(); + let mut last_action_state = ecs.write_storage::>(); - (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::().get(entity), + ecs.read_storage::().get(entity), + ecs.read_storage::().get(entity), + ecs.read_storage::().get(entity), + ecs.read_storage::() + .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), } }