diff --git a/client/src/lib.rs b/client/src/lib.rs index 632f7205c4..2e4101f924 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -33,6 +33,7 @@ use common::{ grid::Grid, outcome::Outcome, recipe::RecipeBook, + resources::PlayerEntity, terrain::{block::Block, neighbors, BiomeKind, SitesKind, TerrainChunk, TerrainChunkSize}, trade::{PendingTrade, TradeAction, TradeId, TradeResult}, uid::{Uid, UidAllocator}, @@ -281,6 +282,7 @@ impl Client { let entity = state.ecs_mut().apply_entity_package(entity_package); *state.ecs_mut().write_resource() = time_of_day; + *state.ecs_mut().write_resource() = PlayerEntity(Some(entity)); state.ecs_mut().insert(material_stats); state.ecs_mut().insert(ability_map); diff --git a/common/net/src/msg/ecs_packet.rs b/common/net/src/msg/ecs_packet.rs index 804d1ed1f1..86049f7103 100644 --- a/common/net/src/msg/ecs_packet.rs +++ b/common/net/src/msg/ecs_packet.rs @@ -101,8 +101,8 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Pos(comp) => sync::handle_interp_insert(comp, entity, world), - EcsCompPacket::Vel(comp) => sync::handle_insert(comp, entity, world), - EcsCompPacket::Ori(comp) => sync::handle_insert(comp, entity, world), + EcsCompPacket::Vel(comp) => sync::handle_interp_insert(comp, entity, world), + EcsCompPacket::Ori(comp) => sync::handle_interp_insert(comp, entity, world), EcsCompPacket::Shockwave(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::BeamSegment(comp) => sync::handle_insert(comp, entity, world), } @@ -133,8 +133,8 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Pos(comp) => sync::handle_interp_modify(comp, entity, world), - EcsCompPacket::Vel(comp) => sync::handle_modify(comp, entity, world), - EcsCompPacket::Ori(comp) => sync::handle_modify(comp, entity, world), + EcsCompPacket::Vel(comp) => sync::handle_interp_modify(comp, entity, world), + EcsCompPacket::Ori(comp) => sync::handle_interp_modify(comp, entity, world), EcsCompPacket::Shockwave(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::BeamSegment(comp) => sync::handle_modify(comp, entity, world), } @@ -169,8 +169,8 @@ impl sync::CompPacket for EcsCompPacket { sync::handle_remove::(entity, world) }, EcsCompPhantom::Pos(_) => sync::handle_interp_remove::(entity, world), - EcsCompPhantom::Vel(_) => sync::handle_remove::(entity, world), - EcsCompPhantom::Ori(_) => sync::handle_remove::(entity, world), + EcsCompPhantom::Vel(_) => sync::handle_interp_remove::(entity, world), + EcsCompPhantom::Ori(_) => sync::handle_interp_remove::(entity, world), EcsCompPhantom::Shockwave(_) => sync::handle_remove::(entity, world), EcsCompPhantom::BeamSegment(_) => sync::handle_remove::(entity, world), } diff --git a/common/net/src/sync/interpolation.rs b/common/net/src/sync/interpolation.rs index 6c321eb0a7..17b9d4ef1c 100644 --- a/common/net/src/sync/interpolation.rs +++ b/common/net/src/sync/interpolation.rs @@ -1,26 +1,125 @@ -// impls of `InterpolatableComponent` on things defined in `common`, since `common_net` is -// downstream of `common` -use common::comp::{Pos, Vel}; +// impls of `InterpolatableComponent` on things defined in `common`, since +// `common_net` is downstream of `common`, and an `InterpolationSystem` that +// applies them use super::InterpolatableComponent; -use specs::{Component, Entity, World}; +use common::comp::{Ori, Pos, Vel}; +use specs::Component; use specs_idvs::IdvStorage; -use vek::Vec3; +use tracing::warn; +use vek::ops::{Lerp, Slerp}; -#[derive(Default)] -pub struct PosBuffer(pub [Vec3; 4]); +#[derive(Debug, Default)] +pub struct InterpBuffer { + pub buf: [(f64, T); 4], + pub i: usize, +} -impl Component for PosBuffer { +impl Component for InterpBuffer { type Storage = IdvStorage; } impl InterpolatableComponent for Pos { - type InterpData = PosBuffer; + type InterpData = InterpBuffer; + type ReadData = Vel; - fn interpolate(self, data: &mut Self::InterpData, entity: Entity, world: &World) -> Self { - for i in 0..data.0.len()-1 { - data.0[i] = data.0[i+1]; + fn update_component(&self, interp_data: &mut Self::InterpData, time: f64) { + let InterpBuffer { + ref mut buf, + ref mut i, + } = interp_data; + *i += 1; + *i %= buf.len(); + buf[*i] = (time, *self); + } + + fn interpolate(self, interp_data: &Self::InterpData, t2: f64, _vel: &Vel) -> Self { + // lerp to test interface, do hermite spline later + let InterpBuffer { ref buf, ref i } = interp_data; + let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()]; + let (t1, p1) = buf[i % buf.len()]; + if (t1 - t0).abs() < f64::EPSILON { + return self; } - data.0[data.0.len()-1] = self.0; - self + let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32; + let mut out = Lerp::lerp_unclamped(p0.0, p1.0, lerp_factor); + if out.map(|x| x.is_nan()).reduce_or() { + warn!( + "interpolation output is nan: {}, {}, {:?}", + t2, lerp_factor, buf + ); + out = p1.0; + } + + Pos(out) + } +} + +impl InterpolatableComponent for Vel { + type InterpData = InterpBuffer; + type ReadData = (); + + fn update_component(&self, interp_data: &mut Self::InterpData, time: f64) { + let InterpBuffer { + ref mut buf, + ref mut i, + } = interp_data; + *i += 1; + *i %= buf.len(); + buf[*i] = (time, *self); + } + + fn interpolate(self, interp_data: &Self::InterpData, t2: f64, _: &()) -> Self { + let InterpBuffer { ref buf, ref i } = interp_data; + let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()]; + let (t1, p1) = buf[i % buf.len()]; + if (t1 - t0).abs() < f64::EPSILON { + return self; + } + let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32; + let mut out = Lerp::lerp_unclamped(p0.0, p1.0, lerp_factor); + if out.map(|x| x.is_nan()).reduce_or() { + warn!( + "interpolation output is nan: {}, {}, {:?}", + t2, lerp_factor, buf + ); + out = p1.0; + } + + Vel(out) + } +} + +impl InterpolatableComponent for Ori { + type InterpData = InterpBuffer; + type ReadData = (); + + fn update_component(&self, interp_data: &mut Self::InterpData, time: f64) { + let InterpBuffer { + ref mut buf, + ref mut i, + } = interp_data; + *i += 1; + *i %= buf.len(); + buf[*i] = (time, *self); + } + + fn interpolate(self, interp_data: &Self::InterpData, t2: f64, _: &()) -> Self { + let InterpBuffer { ref buf, ref i } = interp_data; + let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()]; + let (t1, p1) = buf[i % buf.len()]; + if (t1 - t0).abs() < f64::EPSILON { + return self; + } + let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32; + let mut out = Slerp::slerp_unclamped(p0.0, p1.0, lerp_factor); + if out.into_vec4().map(|x| x.is_nan()).reduce_or() { + warn!( + "interpolation output is nan: {}, {}, {:?}", + t2, lerp_factor, buf + ); + out = p1.0; + } + + Ori(out.normalized()) } } diff --git a/common/net/src/sync/packet.rs b/common/net/src/sync/packet.rs index d9d3e67d0d..152b7946d6 100644 --- a/common/net/src/sync/packet.rs +++ b/common/net/src/sync/packet.rs @@ -1,5 +1,5 @@ use super::track::UpdateTracker; -use common::uid::Uid; +use common::{resources::Time, uid::Uid}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use specs::{Component, Entity, Join, ReadStorage, World, WorldExt}; use std::{ @@ -9,6 +9,10 @@ use std::{ }; use tracing::error; +// TODO: apply_{insert,modify,remove} all take the entity and call +// `write_storage` once per entity per component, instead of once per update +// batch(e.g. in a system-like memory access pattern); if sync ends up being a +// bottleneck, try optimizing this /// Implemented by type that carries component data for insertion and /// modification The assocatied `Phantom` type only carries information about /// which component type is of interest and is used to transmit deletion events @@ -44,13 +48,16 @@ pub fn handle_remove(entity: Entity, world: &World) { pub trait InterpolatableComponent: Component { type InterpData: Component + Default; + type ReadData; - fn interpolate(self, data: &mut Self::InterpData, entity: Entity, world: &World) -> Self; + fn update_component(&self, data: &mut Self::InterpData, time: f64); + fn interpolate(self, data: &Self::InterpData, time: f64, read_data: &Self::ReadData) -> Self; } pub fn handle_interp_insert(comp: C, entity: Entity, world: &World) { let mut interp_data = C::InterpData::default(); - let comp = comp.interpolate(&mut interp_data, entity, world); + let time = world.read_resource::