diff --git a/src/systems/postfix_components.rs b/src/systems/postfix_components.rs index 48c1b2e..dafe26a 100644 --- a/src/systems/postfix_components.rs +++ b/src/systems/postfix_components.rs @@ -132,37 +132,3 @@ pub(crate) fn add_timewarp_components( - mut q: Query<(Entity, &mut ComponentHistory), (Added, Without)>, - game_clock: Res, - rb: Option>, -) { - 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 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::() - ); - ch.report_birth_at_frame(**game_clock); - assert!( - ch.at_frame(**game_clock).is_some(), - "Reported birth, but no CH value stored" - ); - } -} diff --git a/src/systems/prefix_not_in_rollback.rs b/src/systems/prefix_not_in_rollback.rs index 2d1569d..b2dbd07 100644 --- a/src/systems/prefix_not_in_rollback.rs +++ b/src/systems/prefix_not_in_rollback.rs @@ -77,12 +77,6 @@ pub(crate) fn apply_snapshots_and_maybe_rollback( } } - // 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} {}", diff --git a/src/systems/prefix_start_rollback.rs b/src/systems/prefix_start_rollback.rs index 34e8242..d391d0c 100644 --- a/src/systems/prefix_start_rollback.rs +++ b/src/systems/prefix_start_rollback.rs @@ -67,27 +67,39 @@ enum Provenance { pub(crate) fn rollback_component( rb: Res, // T is None in case where component removed but ComponentHistory persists - mut q: Query<(Entity, Option<&mut T>, &ComponentHistory), Without>, + mut q: Query< + ( + Entity, + Option<&mut T>, + &ComponentHistory, + &ServerSnapshot, + ), + Without, + >, mut commands: Commands, game_clock: Res, ) { - 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, @@ -96,81 +108,48 @@ pub(crate) fn rollback_component( }; 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", - comp_hist.type_name() + "{game_clock:?} rollback component {entity:?} {} {provenance:?} - REMOVE", + ch.type_name() ); commands.entity(entity).remove::(); } 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!!")); } } } diff --git a/src/traits.rs b/src/traits.rs index c0b4ec2..4a229e7 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -83,7 +83,7 @@ impl TimewarpTraits for App { ); } self.add_systems( - schedule.clone(), // TODO RJRJ MOVE FILE + schedule.clone(), prefix_first::record_component_death:: .run_if(not(resource_exists::())) .in_set(TimewarpPrefixSet::First), @@ -120,7 +120,6 @@ impl TimewarpTraits for App { postfix_components::remove_components_from_despawning_entities::, postfix_components::record_component_history::, postfix_components::add_timewarp_components::, - postfix_components::record_component_birth::, ) .in_set(TimewarpPostfixSet::Components), ); diff --git a/tests/spawning_in_the_past.rs b/tests/spawning_in_the_past.rs index 399583d..5d14108 100644 --- a/tests/spawning_in_the_past.rs +++ b/tests/spawning_in_the_past.rs @@ -22,18 +22,6 @@ fn log_all(game_clock: Res, 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();