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

Commit

Permalink
component births implied by recording a value at a frame
Browse files Browse the repository at this point in the history
  • Loading branch information
RJ committed Oct 20, 2023
1 parent bb0115d commit 22b0ccd
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 119 deletions.
34 changes: 0 additions & 34 deletions src/systems/postfix_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,37 +132,3 @@ pub(crate) fn add_timewarp_components<T: TimewarpComponent, const CORRECTION_LOG
));
}
}

/// record component lifetimes
/// won't be called first time comp is added, since it won't have a ComponentHistory yet.
/// only for comp removed ... then readded birth
/// TODO not sure if we need this birth tracking at all?
pub(crate) fn record_component_birth<T: TimewarpComponent>(
mut q: Query<(Entity, &mut ComponentHistory<T>), (Added<T>, Without<NoRollback>)>,
game_clock: Res<GameClock>,
rb: Option<Res<Rollback>>,
) {
return;
// no. implied by inserting values to CH!

// during rollback, components are removed and readded.
// but we don't want to log the same as outside of rollback, we want to ignore.
// however this system still runs, so that the Added<T> filters update their markers
// otherwise things added during rollback would all show as Added the first frame back.
if rb.is_some() {
return;
}

for (entity, mut ch) in q.iter_mut() {
trace!(
"{entity:?} Component birth @ {:?} {:?}",
game_clock.frame(),
std::any::type_name::<T>()
);
ch.report_birth_at_frame(**game_clock);
assert!(
ch.at_frame(**game_clock).is_some(),
"Reported birth, but no CH value stored"
);
}
}
6 changes: 0 additions & 6 deletions src/systems/prefix_not_in_rollback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,6 @@ pub(crate) fn apply_snapshots_and_maybe_rollback<T: TimewarpComponent>(
}
}

// if !comp_hist.alive_at_frame(snap_frame) {
// info!("Setting liveness for {snap_frame} {entity:?} {comp_from_snapshot:?} ");
// comp_hist.report_birth_at_frame(snap_frame);
// assert!(comp_hist.at_frame(snap_frame).is_some());
// }

if snap_frame < **game_clock {
debug!(
"Triggering rollback due to snapshot. {entity:?} snap_frame: {snap_frame} {}",
Expand Down
109 changes: 44 additions & 65 deletions src/systems/prefix_start_rollback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,27 +67,39 @@ enum Provenance {
pub(crate) fn rollback_component<T: TimewarpComponent>(
rb: Res<Rollback>,
// T is None in case where component removed but ComponentHistory persists
mut q: Query<(Entity, Option<&mut T>, &ComponentHistory<T>), Without<NoRollback>>,
mut q: Query<
(
Entity,
Option<&mut T>,
&ComponentHistory<T>,
&ServerSnapshot<T>,
),
Without<NoRollback>,
>,
mut commands: Commands,
game_clock: Res<GameClock>,
) {
for (entity, opt_comp, comp_hist) in q.iter_mut() {
for (entity, opt_comp, ch, ss) in q.iter_mut() {
let rollback_frame = **game_clock;
let end_frame = rb.range.end;

let prefix = if rollback_frame != **game_clock {
warn!(
"😬 rollback_component {entity:?} {game_clock:?} rollback_frame:{rollback_frame} {}",
comp_hist.type_name()
);
"😬"
} else {
""
trace!("rollback_component {entity:?} {} rollback-frame:{rollback_frame} {game_clock:?} end_frame={end_frame} {rb:?}", ch.type_name());

// comp@frame is cloned once here
//
// in cases where just-in-time component values are delivered by replicon, inserted,
// and a rollback is triggered, the value can end up being in the SS but not ever written
// to the CH, because we never reached the TW postfix sets that frame.
//
// we always prefer the SS value if available, otherwise our own record from the CH.
let comp_at_rollback_frame = match ss.at_frame(rollback_frame) {
Some(val) => Some(val.clone()),
None => ch.at_frame(rollback_frame).cloned(),
};
trace!("rollback_component {entity:?} {} rollback-frame:{rollback_frame} {game_clock:?} end_frame={end_frame} {rb:?}", comp_hist.type_name());

let provenance = match (
comp_hist.alive_at_frame(rollback_frame),
comp_hist.alive_at_frame(end_frame),
comp_at_rollback_frame.is_some(),
ch.alive_at_frame(end_frame),
) {
(true, true) => Provenance::AliveThenAlive,
(true, false) => Provenance::AliveThenDead,
Expand All @@ -96,81 +108,48 @@ pub(crate) fn rollback_component<T: TimewarpComponent>(
};

trace!(
"⛳️ {prefix} {entity:?} {} CH alive_ranges: {:?}",
comp_hist.type_name(),
comp_hist.alive_ranges
"⛳️ {entity:?} {} CH alive_ranges: {:?}",
ch.type_name(),
ch.alive_ranges
);

match provenance {
Provenance::DeadThenDead => {
trace!(
"{prefix} {game_clock:?} rollback component {entity:?} {} {provenance:?} - NOOP {:?}",
comp_hist.type_name(),
comp_hist.alive_ranges
"{game_clock:?} rollback component {entity:?} {} {provenance:?} - NOOP {:?}",
ch.type_name(),
ch.alive_ranges
);
}
Provenance::DeadThenAlive => {
trace!(
"{prefix} {game_clock:?} rollback component {entity:?} {} {provenance:?} - REMOVE<T>",
comp_hist.type_name()
"{game_clock:?} rollback component {entity:?} {} {provenance:?} - REMOVE<T>",
ch.type_name()
);
commands.entity(entity).remove::<T>();
}
Provenance::AliveThenAlive => {
// TODO we might want a general way to check the oldest frame for this comp,
// and if we dont have the requested frame, use the oldest instead?
// assuming a request OLDER than the requested can't be serviced.
let comp_at_frame = comp_hist.at_frame(rollback_frame);

// debugging
if comp_at_frame.is_none() {
let oldest_frame = comp_hist.values.oldest_frame();

error!(
"HMMMM {entity:?} f @ oldest_frame ({oldest_frame}) comp_val = {:?}",
comp_hist.at_frame(oldest_frame)
);
error!("HMMMM {entity:?} {game_clock:?} OPT_COMP = {opt_comp:?}");
for f in (rollback_frame - 2)..=(rollback_frame + 2) {
error!(
"HMMMM {entity:?} f={f} comp_val = {:?}",
comp_hist.at_frame(f)
);
}

panic!("{prefix} {game_clock:?} {entity:?} {provenance:?} {} rollback_frame: {rollback_frame} alive_ranges:{:?} rb:{rb:?} oldest value in comp_hist: {oldest_frame} occ:{:?}\n",
comp_hist.type_name(), comp_hist.alive_ranges, comp_hist.values.frame_occupancy());
}
//
let comp_val = comp_at_frame.unwrap().clone();
trace!(
"{prefix} {game_clock:?} rollback component {entity:?} {} {provenance:?} - REPLACE WITH {comp_val:?}",
comp_hist.type_name()
"{game_clock:?} rollback component {entity:?} {} {provenance:?} - REPLACE WITH {comp_at_rollback_frame:?}",
ch.type_name()
);
if let Some(mut comp) = opt_comp {
*comp = comp_val;
*comp = comp_at_rollback_frame.expect("Component should be alive here!");
} else {
// during new spawns this happens. not a bug.
trace!(
"{prefix} {entity:?} Actually having to insert for {comp_val:?} doesn't exist yet"
);
commands.entity(entity).insert(comp_val);
commands
.entity(entity)
.insert(comp_at_rollback_frame.expect("Component should be alive here"));
}
}
Provenance::AliveThenDead => {
let comp_at_frame = comp_hist.at_frame(rollback_frame);
// debugging
if comp_at_frame.is_none() {
panic!("{game_clock:?} {entity:?} {provenance:?} {} rollback_frame: {rollback_frame} alive_ranges:{:?} rb:{rb:?}",
comp_hist.type_name(), comp_hist.alive_ranges);
}
//
let comp_val = comp_at_frame.unwrap().clone();
trace!(
"{prefix} {game_clock:?} rollback component {entity:?} {} {provenance:?} - INSERT {comp_val:?}",
comp_hist.type_name()
"{game_clock:?} rollback component {entity:?} {} {provenance:?} - INSERT {comp_at_rollback_frame:?}",
ch.type_name()
);
commands.entity(entity).insert(comp_val);
commands
.entity(entity)
.insert(comp_at_rollback_frame.expect("Component should be alive here!!"));
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl TimewarpTraits for App {
);
}
self.add_systems(
schedule.clone(), // TODO RJRJ MOVE FILE
schedule.clone(),
prefix_first::record_component_death::<T>
.run_if(not(resource_exists::<Rollback>()))
.in_set(TimewarpPrefixSet::First),
Expand Down Expand Up @@ -120,7 +120,6 @@ impl TimewarpTraits for App {
postfix_components::remove_components_from_despawning_entities::<T>,
postfix_components::record_component_history::<T>,
postfix_components::add_timewarp_components::<T, CORRECTION_LOGGING>,
postfix_components::record_component_birth::<T>,
)
.in_set(TimewarpPostfixSet::Components),
);
Expand Down
12 changes: 0 additions & 12 deletions tests/spawning_in_the_past.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,6 @@ fn log_all(game_clock: Res<GameClock>, q: Query<(Entity, &Enemy, &EntName)>) {
}
}

/*
error i noticed during game dev:
client is at frame 10.
messages arrive from server causing client to want to:
* insert an entity in the past, at frame 7.
* update server snapshot for something at frame 8
the update SS thing will do insert_resource and replace the rb to 7 with 8
then the initial value of the past-entity comps won't exist at f8, since we
birthed it at f7 --> boom.
*/

#[test]
fn spawning_in_the_past() {
let mut app = setup_test_app();
Expand Down

0 comments on commit 22b0ccd

Please sign in to comment.