mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Use Last<C> to check if a physics component changed
This commit is contained in:
parent
fbb00601fb
commit
c6034bf450
@ -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
15
common/src/comp/last.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user