#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
pub mod prelude {
pub use crate::{FrameInterpolate, FrameInterpolationPlugin, FrameInterpolationSystems};
}
use bevy_app::prelude::*;
use bevy_ecs::component::Mutable;
use bevy_ecs::prelude::*;
use bevy_ecs::schedule::common_conditions::not;
use bevy_reflect::Reflect;
use bevy_time::{Fixed, Time};
use bevy_utils::prelude::DebugName;
use core::fmt::Debug;
use lightyear_core::prelude::LocalTimeline;
use lightyear_core::timeline::is_in_rollback;
use lightyear_interpolation::prelude::InterpolationRegistry;
use lightyear_replication::prelude::ReplicationBufferSystems;
use serde::{Deserialize, Serialize};
use tracing::trace;
#[deprecated(note = "Use FrameInterpolationSystems instead")]
pub type FrameInterpolationSet = FrameInterpolationSystems;
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum FrameInterpolationSystems {
Restore,
Update,
Interpolate,
}
pub struct FrameInterpolationPlugin<C> {
_marker: core::marker::PhantomData<C>,
}
impl<C> Default for FrameInterpolationPlugin<C> {
fn default() -> Self {
Self {
_marker: core::marker::PhantomData,
}
}
}
impl<C: Component<Mutability = Mutable> + Clone + Debug> Plugin for FrameInterpolationPlugin<C> {
fn build(&self, app: &mut App) {
app.configure_sets(
RunFixedMainLoop,
FrameInterpolationSystems::Restore.in_set(RunFixedMainLoopSystems::BeforeFixedMainLoop),
);
app.configure_sets(
FixedLast,
FrameInterpolationSystems::Update.run_if(not(is_in_rollback)),
);
app.configure_sets(
PostUpdate,
FrameInterpolationSystems::Interpolate
.before(bevy_transform::TransformSystems::Propagate)
.after(ReplicationBufferSystems::Buffer),
);
app.add_systems(
RunFixedMainLoop,
restore_from_visual_interpolation::<C>.in_set(FrameInterpolationSystems::Restore),
);
app.add_systems(
FixedPostUpdate,
update_visual_interpolation_status::<C>.in_set(FrameInterpolationSystems::Update),
);
app.add_systems(
PostUpdate,
visual_interpolation::<C>.in_set(FrameInterpolationSystems::Interpolate),
);
}
}
#[derive(Component, PartialEq, Debug)]
pub struct FrameInterpolate<C: Component> {
pub trigger_change_detection: bool,
pub previous_value: Option<C>,
pub current_value: Option<C>,
}
impl<C: Component> Default for FrameInterpolate<C> {
fn default() -> Self {
Self {
trigger_change_detection: true,
previous_value: None,
current_value: None,
}
}
}
#[derive(Component, PartialEq, Serialize, Deserialize, Clone, Debug, Reflect)]
pub struct SkipFrameInterpolation;
pub(crate) fn visual_interpolation<C: Component<Mutability = Mutable> + Clone + Debug>(
time: Res<Time<Fixed>>,
timeline: Res<LocalTimeline>,
registry: Res<InterpolationRegistry>,
mut query: Query<(
&mut C,
&mut FrameInterpolate<C>,
Option<&SkipFrameInterpolation>,
)>,
) {
let kind = DebugName::type_name::<C>();
let tick = timeline.tick();
let overstep = time.overstep_fraction();
for (mut component, mut interpolate_status, skip_interpolation) in query.iter_mut() {
if skip_interpolation.is_some() && interpolate_status.current_value.is_some() {
*component = interpolate_status.current_value.clone().unwrap();
interpolate_status.previous_value = interpolate_status.current_value.clone();
continue;
}
let Some(previous_value) = &interpolate_status.previous_value else {
trace!(?kind, "No previous value, skipping visual interpolation");
continue;
};
let Some(current_value) = &interpolate_status.current_value else {
trace!(?kind, "No current value, skipping visual interpolation");
continue;
};
let interpolated =
registry.interpolate(previous_value.clone(), current_value.clone(), overstep);
trace!(
?kind,
?tick,
?previous_value,
?current_value,
?overstep,
?interpolated,
"Visual interpolation applied"
);
if !interpolate_status.trigger_change_detection {
*component.bypass_change_detection() = interpolated;
} else {
*component = interpolated;
}
}
}
pub(crate) fn update_visual_interpolation_status<
C: Component<Mutability = Mutable> + Clone + Debug,
>(
mut query: Query<(Ref<C>, &mut FrameInterpolate<C>)>,
) {
for (component, mut interpolate_status) in query.iter_mut() {
if let Some(current_value) = interpolate_status.current_value.take() {
interpolate_status.previous_value = Some(current_value);
}
interpolate_status.current_value = Some(component.clone());
trace!(
?interpolate_status,
"updating interpolate status current_value"
);
}
}
pub(crate) fn restore_from_visual_interpolation<
C: Component<Mutability = Mutable> + Clone + Debug,
>(
mut query: Query<(&mut C, &mut FrameInterpolate<C>)>,
) {
let kind = DebugName::type_name::<C>();
for (mut component, interpolate_status) in query.iter_mut() {
if let Some(current_value) = &interpolate_status.current_value {
trace!(
?kind,
visual = ?component,
correct = ?current_value,
"Restoring correct tick value before FixedMainLoop"
);
*component.bypass_change_detection() = current_value.clone();
}
}
}