Skip to content
This repository has been archived by the owner on Nov 10, 2024. It is now read-only.

Commit

Permalink
Rejig into prefix and postfix timewarp sets
Browse files Browse the repository at this point in the history
Blueprints, postfix/prefix sets, rb consolidation strategy, NOTES.
  • Loading branch information
RJ authored Oct 19, 2023
1 parent ddd8efe commit 995e529
Show file tree
Hide file tree
Showing 23 changed files with 1,906 additions and 859 deletions.
481 changes: 433 additions & 48 deletions NOTES.md

Large diffs are not rendered by default.

83 changes: 70 additions & 13 deletions src/components.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{FrameBuffer, FrameNumber, TimewarpComponent};
use crate::{prelude::TimewarpError, FrameBuffer, FrameNumber, TimewarpComponent};
use bevy::prelude::*;

/// entities with NotRollbackable are ignored, even if they have components which
Expand Down Expand Up @@ -47,6 +47,27 @@ impl<T: TimewarpComponent> InsertComponentAtFrame<T> {
}
}

/// For assembling a blueprint in the past - testing.
#[derive(Component, Debug)]
pub struct AssembleBlueprintAtFrame<T: TimewarpComponent> {
pub component: T,
pub frame: FrameNumber,
}
impl<T: TimewarpComponent> AssembleBlueprintAtFrame<T> {
pub fn new(frame: FrameNumber, component: T) -> Self {
Self { component, frame }
}
pub fn type_name(&self) -> &str {
std::any::type_name::<T>()
}
}

/// presence on an entity during rollback means there will be no older values available,
/// since the entity is being assembled from blueprint this frame.
/// so we load in component values matching the origin frame (not one frame prior, like usual)
#[derive(Component, Debug, Clone)]
pub struct OriginFrame(pub FrameNumber);

/// entities with components that were registered with error correction logging will receive
/// one of these components, updated with before/after values when a simulation correction
/// resulting from a rollback and resimulate causes a snap.
Expand All @@ -67,14 +88,25 @@ pub struct ServerSnapshot<T: TimewarpComponent> {
impl<T: TimewarpComponent> ServerSnapshot<T> {
pub fn with_capacity(len: usize) -> Self {
Self {
values: FrameBuffer::with_capacity(len),
values: FrameBuffer::with_capacity(len, "SS"),
}
}
pub fn at_frame(&self, frame: FrameNumber) -> Option<&T> {
self.values.get(frame)
}
pub fn insert(&mut self, frame: FrameNumber, val: T) {
self.values.insert(frame, val);
pub fn insert(&mut self, frame: FrameNumber, val: T) -> Result<(), TimewarpError> {
self.values.insert(frame, val)
}
pub fn type_name(&self) -> &str {
std::any::type_name::<T>()
}
pub fn newest_snap_frame(&self) -> Option<FrameNumber> {
let nf = self.values.newest_frame();
if nf == 0 {
None
} else {
Some(nf)
}
}
}

Expand All @@ -93,15 +125,26 @@ pub struct ComponentHistory<T: TimewarpComponent> {
// lazy first version - don't need a clone each frame if value hasn't changed!
// just store once and reference from each unchanged frame number.
impl<T: TimewarpComponent> ComponentHistory<T> {
pub fn with_capacity(len: usize, birth_frame: FrameNumber) -> Self {
/// The entity param is just for logging.
pub fn with_capacity(
len: usize,
birth_frame: FrameNumber,
component: T,
entity: &Entity,
) -> Self {
let mut this = Self {
values: FrameBuffer::with_capacity(len),
alive_ranges: Vec::new(),
values: FrameBuffer::with_capacity(len, "CH"),
alive_ranges: vec![(birth_frame, None)],
correction_logging_enabled: false,
};
this.report_birth_at_frame(birth_frame);
trace!("CH.new {entity:?} {birth_frame} = {component:?}");
// can't error on a brand new buffer:
_ = this.values.insert(birth_frame, component);
this
}
pub fn type_name(&self) -> &str {
std::any::type_name::<T>()
}
/// will compute and insert `TimewarpCorrection`s when snapping
pub fn enable_correction_logging(&mut self) {
self.correction_logging_enabled = true;
Expand All @@ -110,16 +153,24 @@ impl<T: TimewarpComponent> ComponentHistory<T> {
self.values.get(frame)
}
// adding entity just for debugging print outs.
pub fn insert(&mut self, frame: FrameNumber, val: T, entity: &Entity) {
pub fn insert(
&mut self,
frame: FrameNumber,
val: T,
entity: &Entity,
) -> Result<(), TimewarpError> {
trace!("CH.Insert {entity:?} {frame} = {val:?}");
self.values.insert(frame, val);
self.values.insert(frame, val)
}

/// removes values buffered for this frame, and greater frames.
#[allow(dead_code)]
pub fn remove_frame_and_beyond(&mut self, frame: FrameNumber) {
self.values
.remove_entries_newer_than(frame.saturating_sub(1));
}
pub fn alive_at_frame(&self, frame: FrameNumber) -> bool {
// self.values.get(frame).is_some()
for (start, maybe_end) in &self.alive_ranges {
if *start <= frame && (maybe_end.is_none() || maybe_end.unwrap() > frame) {
return true;
Expand All @@ -129,9 +180,13 @@ impl<T: TimewarpComponent> ComponentHistory<T> {
}
pub fn report_birth_at_frame(&mut self, frame: FrameNumber) {
debug!("component birth @ {frame} {:?}", std::any::type_name::<T>());
if self.alive_at_frame(frame) {
warn!("Can't report birth of component already alive");
return;
}
assert!(
!self.alive_at_frame(frame),
"Can't report birth of component already alive"
self.values.get(frame).is_some(),
"No stored component value when reporting birth @ {frame}"
);
self.alive_ranges.push((frame, None));
}
Expand All @@ -144,11 +199,13 @@ impl<T: TimewarpComponent> ComponentHistory<T> {
if !self.alive_at_frame(frame) {
return;
}
debug!(

trace!(
"component death @ {frame} {:?} {:?}",
std::any::type_name::<T>(),
self.alive_ranges
);

assert!(
self.alive_at_frame(frame),
"Can't report death of component not alive"
Expand Down
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#[derive(Debug)]
pub enum TimewarpError {
Io(std::io::Error),
SequenceBufferFull,
FrameTooOld,
}

impl std::fmt::Display for TimewarpError {
Expand Down
123 changes: 75 additions & 48 deletions src/frame_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,50 @@
///
use crate::*;
use bevy::prelude::*;
use std::{collections::VecDeque, ops::Range};
use std::{collections::VecDeque, fmt, ops::Range};

/// values for new frames are push_front'ed onto the vecdeque
#[derive(Debug, Resource, Clone)]
#[derive(Resource, Clone)]
pub struct FrameBuffer<T>
where
T: Clone + Send + Sync + std::fmt::Debug,
T: Clone + Send + Sync + PartialEq + std::fmt::Debug,
{
/// Contains Option<T> because there can be gaps
/// and we want to be able to store 'None' as a normal value in here.
entries: VecDeque<Option<T>>,
/// frame number of the first elem of vecdeque ie newest value. 0 = empty.
front_frame: FrameNumber,
capacity: usize,
pub name: String,
}

// impl<T> fmt::Debug for FrameBuffer<T>
// where
// T: Clone + Send + Sync + std::fmt::Debug,
// {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// write!(
// f,
// "FrameBuffer{{front_frame:{:?}, capacity:{:?} entries:[{:?},...]}}",
// self.front_frame,
// self.capacity,
// self.get(self.newest_frame()),
// )
// }
// }
impl<T> fmt::Debug for FrameBuffer<T>
where
T: Clone + Send + Sync + PartialEq + std::fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"FrameBuffer[{}]<{}>{{front_frame:{:?}, capacity:{:?} entries:[{:?},...]}}",
self.name,
std::any::type_name::<T>(),
self.front_frame,
self.capacity,
self.get(self.newest_frame()),
)
}
}

impl<T> FrameBuffer<T>
where
T: Clone + Send + Sync + std::fmt::Debug,
T: Clone + Send + Sync + PartialEq + std::fmt::Debug,
{
pub fn with_capacity(len: usize) -> Self {
pub fn with_capacity(len: usize, name: &str) -> Self {
Self {
entries: VecDeque::with_capacity(len),
capacity: len,
front_frame: 0,
name: name.into(),
}
}

Expand Down Expand Up @@ -75,9 +79,11 @@ where
return;
}
if let Some(index) = self.index(frame) {
self.entries.drain(0..index);
self.entries.drain(0..index.min(self.entries.len()));
self.front_frame = frame;
} else {
error!("remove_entries_newer_than {frame} failed, no index.");
}
self.front_frame = frame;
}

/// value at frame, or None if out of the range of values currently stored in the buffer
Expand Down Expand Up @@ -116,44 +122,65 @@ where
}
}

pub fn insert_blanks(&mut self, num_blanks: usize) {
for _ in 0..num_blanks {
self.entries.push_front(None);
}
}

// which frames have values?
pub fn frame_occupancy(&self) -> Vec<bool> {
self.entries.iter().map(|e| e.is_some()).collect::<Vec<_>>()
}

/// insert value at given frame.
/// It is permitted to insert at old frames that are still in the range, but
/// not allowed to insert at a frame older than the oldest existing frame.
///
/// Is is permitted to insert at any future frame, any gaps will be make None.
/// Is is permitted to insert at any future frame, any gaps will be made None.
/// so if you insert at newest_frame() + a gazillion, you gets a buffer containing your
/// one new value and a bunch of Nones after it.
pub fn insert(&mut self, frame: FrameNumber, value: T) {
pub fn insert(&mut self, frame: FrameNumber, value: T) -> Result<(), TimewarpError> {
// is this frame too old to be accepted?
if frame < self.oldest_frame() {
// probably outrageous lag or network desync or something? pretty bad.
error!(
"Frame too old! range: {:?} attempt: {frame} = {value:?}",
(
self.front_frame,
self.front_frame
.saturating_sub(self.capacity as FrameNumber)
)
);
return;
// error!(
// "Frame too old! range: {:?} attempt: {frame} = {value:?}",
// (
// self.front_frame,
// self.front_frame
// .saturating_sub(self.capacity as FrameNumber)
// )
// );
return Err(TimewarpError::FrameTooOld);
}
// are we replacing a potential existing value, ie no change in buffer range
if let Some(index) = self.index(frame) {
if let Some(val) = self.entries.get_mut(index) {
// TODO should we test if we are we replacing with same-val that already exists,
// and bail out here? would still need to avoid mutably derefing the SS somehow.
*val = Some(value);
}
return;
return Ok(());
}
// so we are inserting a frame greater than front_frame.
// any gaps between current `front_frame` and `frame` need to be created as None
for _ in (self.front_frame + 1)..frame {
// print!("{self:?} ... Inserting a None val @ {f}\n");
self.entries.push_front(None);
if self.front_frame == 0 || frame == self.front_frame + 1 {
// no gaps.
} else {
// so we are inserting a frame greater than front_frame.
// any gaps between current `front_frame` and `frame` need to be created as None
// warn!(
// "inserting f {frame}, front_frame currently: {} {self:?}",
// self.front_frame
// );
for _f in (self.front_frame + 1)..frame {
self.entries.push_front(None);
}
}

self.entries.push_front(Some(value));
self.front_frame = frame;
self.entries.truncate(self.capacity);
Ok(())
}

/// gets index into vecdeq for frame number, or None if out of range.
Expand Down Expand Up @@ -185,34 +212,34 @@ mod tests {

#[test]
fn test_frame_buffer() {
let mut fb = FrameBuffer::<u32>::with_capacity(5);
fb.insert(1, 1);
let mut fb = FrameBuffer::<u32>::with_capacity(5, "");
fb.insert(1, 1).unwrap();
assert_eq!(fb.get(1), Some(&1));

fb.insert(2, 2);
fb.insert(2, 2).unwrap();
// print!("{fb:?}");
fb.insert(3, 3);
fb.insert(4, 4);
fb.insert(5, 5);
fb.insert(3, 3).unwrap();
fb.insert(4, 4).unwrap();
fb.insert(5, 5).unwrap();
assert_eq!(fb.get(1), Some(&1));
assert_eq!(fb.get(3), Some(&3));
assert_eq!(fb.get(5), Some(&5));
assert_eq!(fb.get(6), None);
fb.insert(6, 6);
fb.insert(6, 6).unwrap();
assert_eq!(fb.get(6), Some(&6));
// 1 should be dropped now
assert_eq!(fb.get(1), None);
// now test modifying a val by inserting over
assert_eq!(fb.get(3), Some(&3));
fb.insert(3, 33);
fb.insert(3, 33).unwrap();
assert_eq!(fb.get(3), Some(&33));
// test modifying by get_mut
let v2 = fb.get_mut(2).unwrap();
*v2 = 22;
fb.insert(2, 22);
fb.insert(2, 22).unwrap();
assert_eq!(fb.newest_frame(), 6);
// inserting with a gap should fill with nones
fb.insert(8, 8);
fb.insert(8, 8).unwrap();
assert_eq!(fb.get(7), None);
assert_eq!(fb.get(8), Some(&8));
assert_eq!(fb.newest_frame(), 8);
Expand Down
Loading

0 comments on commit 995e529

Please sign in to comment.