Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions crates/core/core/src/history_buffer.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::tick::Tick;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
use bevy_ecs::reflect::{ReflectComponent, ReflectResource};
use bevy_ecs::resource::Resource;
use bevy_reflect::Reflect;
use core::fmt::Debug;
use core::iter::FilterMap;
Expand Down Expand Up @@ -69,15 +67,14 @@ impl<R> HistoryState<R> {
}
}

/// HistoryBuffer stores past values (usually of a Component or Resource) in a buffer, to allow for rollback
/// HistoryBuffer stores past values in a buffer, to allow for rollback
/// The values must always remain ordered from oldest (front) to most recent (back)
#[derive(Resource, Debug, Reflect)]
#[reflect(Component, Resource)]
#[derive(Debug, Reflect)]
pub struct HistoryBuffer<R> {
// Queue containing the history of the resource.
// Queue containing the history of the value.
// The front contains old elements, the back contains the more recent elements.
// We will only store the history for the ticks where the resource got updated
// (if the resource doesn't change, we don't store it)
// We will only store the history for the ticks where the value got updated
// (if the value doesn't change, we don't store it)
//
// The ticks might become invalid in case of a TickEvent (the client tick is changed).
// In that case we simply handle the TickEvent and update all the ticks inside this buffer.
Expand Down Expand Up @@ -167,7 +164,7 @@ impl<R> HistoryBuffer<R> {
self.buffer.get(partition - 1).map(|(_, state)| state)
}

/// Reset the history for this resource
/// Reset the history for this value
pub fn clear(&mut self) {
self.buffer.clear();
}
Expand All @@ -191,7 +188,7 @@ impl<R> HistoryBuffer<R> {
}
}

/// Add to the buffer that we received an update for the resource at the given tick
/// Add to the buffer that we received an update for the value at the given tick
/// The tick must be more recent than the most recent update in the buffer
pub fn add_update(&mut self, tick: Tick, value: R) {
self.add(tick, Some(value));
Expand Down Expand Up @@ -400,7 +397,7 @@ mod tests {
#[derive(Clone, PartialEq, Debug)]
struct TestValue(f32);

/// Test adding and removing updates to the resource history
/// Test adding and removing updates to the value history
#[test]
fn test_add_remove_history() {
let mut history = HistoryBuffer::<TestValue>::default();
Expand Down
37 changes: 34 additions & 3 deletions crates/integration/avian/src/lag_compensation/history.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
//! This plugin maintains a history buffer of the Position, Rotation and ColliderAabb of server entities
//! so that they can be used for lag compensation.

use core::ops::{Deref, DerefMut};

#[cfg(all(feature = "2d", not(feature = "3d")))]
use avian2d::{math::Vector, prelude::*};
#[cfg(all(feature = "3d", not(feature = "2d")))]
use avian3d::{math::Vector, prelude::*};
use bevy_app::prelude::*;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::prelude::*;
use bevy_ecs::{
hierarchy::{ChildOf, Children},
schedule::{IntoScheduleConfigs, SystemSet},
};
use lightyear_core::history_buffer::HistoryBuffer;
use lightyear_core::prelude::LocalTimeline;
use lightyear_core::prelude::{LocalTimeline, Tick};
use lightyear_link::prelude::Server;
#[allow(unused_imports)]
use tracing::{debug, info, trace};
Expand Down Expand Up @@ -63,7 +66,18 @@ pub struct AabbEnvelopeHolder;
/// Component that will store the Position, Rotation, ColliderAabb in a history buffer
/// in order to perform lag compensation for client-predicted entities interacting with
/// this entity
pub type LagCompensationHistory = HistoryBuffer<(Position, Rotation, ColliderAabb)>;
#[derive(Component, Debug, Default, Deref, DerefMut)]
pub struct LagCompensationHistory(HistoryBuffer<(Position, Rotation, ColliderAabb)>);

impl<'a> IntoIterator for &'a LagCompensationHistory {
type Item = (Tick, &'a (Position, Rotation, ColliderAabb));
type IntoIter =
<&'a HistoryBuffer<(Position, Rotation, ColliderAabb)> as IntoIterator>::IntoIter;

fn into_iter(self) -> Self::IntoIter {
(&self.0).into_iter()
}
}

impl Plugin for LagCompensationPlugin {
fn build(&self, app: &mut App) {
Expand Down Expand Up @@ -191,7 +205,7 @@ fn update_collider_history(

// step 2. update the child's Position, Rotation, Collider so that the avian spatial query
// can use the collider's aabb envelope for broad-phase collision detection
let (min, max) = history.into_iter().fold(
let (min, max) = (&*history).into_iter().fold(
(Vector::MAX, Vector::MIN),
|(min, max), (_, (_, _, aabb))| (min.min(aabb.min), max.max(aabb.max)),
);
Expand All @@ -214,3 +228,20 @@ fn update_collider_history(
);
});
}

#[cfg(test)]
mod tests {
use super::*;
use bevy_ecs::world::World;

#[test]
fn lag_compensation_history_can_be_inserted_on_multiple_entities() {
let mut world = World::new();

let first = world.spawn(LagCompensationHistory::default()).id();
let second = world.spawn(LagCompensationHistory::default()).id();

assert!(world.entity(first).contains::<LagCompensationHistory>());
assert!(world.entity(second).contains::<LagCompensationHistory>());
}
}
1 change: 1 addition & 0 deletions crates/integration/avian2d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ tracing.workspace = true

# bevy
bevy_app.workspace = true
bevy_derive.workspace = true
bevy_ecs.workspace = true
bevy_math.workspace = true
bevy_time.workspace = true
Expand Down
1 change: 1 addition & 0 deletions crates/integration/avian3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ avian3d.workspace = true

# bevy
bevy_app.workspace = true
bevy_derive.workspace = true
bevy_ecs.workspace = true
bevy_math.workspace = true
bevy_time.workspace = true
Expand Down
Loading