From f05f47a11fb8b3c8f6f055b1a26000e9ab5c7e7c Mon Sep 17 00:00:00 2001 From: Zicklag Date: Thu, 1 Dec 2022 17:51:51 -0600 Subject: [PATCH] Make System Execution Order More Deterministic This fixes a network desync issue with grabbing overlapping items. --- Cargo.lock | 11 +++++++++ Cargo.toml | 3 ++- src/animation.rs | 39 +++++++++--------------------- src/camera.rs | 4 +-- src/damage.rs | 5 +--- src/lifetime.rs | 4 +-- src/main.rs | 29 +++++++++++++--------- src/map.rs | 7 +----- src/map/elements/decoration.rs | 4 +-- src/map/elements/grenade.rs | 22 ++++++----------- src/map/elements/player_spawner.rs | 4 +-- src/map/elements/sproinger.rs | 9 +++---- src/map/elements/sword.rs | 9 +++---- src/player.rs | 10 +++----- src/player/input.rs | 4 +-- src/schedule.rs | 39 ++++++++++++++++++++++++++++++ src/session.rs | 11 +++------ src/utils/event.rs | 4 +-- 18 files changed, 110 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 752ed5592d..5ea33c1a6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -867,6 +867,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bevy_system_graph" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01982006dc7d468bc38c6362066299bcffeb742066483b131cd1acc3648ecb5f" +dependencies = [ + "bevy_ecs", + "bevy_utils", +] + [[package]] name = "bevy_tasks" version = "0.8.1" @@ -2490,6 +2500,7 @@ dependencies = [ "bevy_ggrs", "bevy_kira_audio", "bevy_prototype_lyon", + "bevy_system_graph", "bevy_tweening", "bitfield", "blocking", diff --git a/Cargo.toml b/Cargo.toml index 59118d7c3d..104fae0c26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ bevy_ggrs = { git = "https://github.com/zicklag/bevy_ggrs.git", branch = "jumpy" bevy_kira_audio = { version = "0.12.0", features = ["ogg"], default-features = false } # bevy_mod_js_scripting = { git = "https://github.com/zicklag/bevy_mod_js_scripting.git", branch = "jumpy" } bevy_prototype_lyon = "0.6.0" +bevy_system_graph = "0.3.0" bevy_tweening = { version = "0.5", default-features = false } bitfield = "0.14.0" blocking = "1.2.0" @@ -63,13 +64,13 @@ postcard = { git = "https://github.com/zicklag/postcard.git", branch = "custom-e rand = "0.8.5" rustls = { version = "0.20.7", features = ["dangerous_configuration", "quic"] } serde = { version = "1.0.137", features = ["derive"] } +serde_json = "1.0.89" serde_yaml = "0.9.2" sys-locale = "0.2.1" thiserror = "1.0.31" tracing = { version = "0.1.37", features = ["release_max_level_debug"] } turborand = { version = "0.8.0", features = ["atomic", "serialize"] } unic-langid = "0.9.0" -serde_json = "1.0.89" [dependencies.bevy] version = "0.8" diff --git a/src/animation.rs b/src/animation.rs index 93c5acddd2..1519f1b43f 100644 --- a/src/animation.rs +++ b/src/animation.rs @@ -26,34 +26,17 @@ impl Plugin for AnimationPlugin { .register_rollback_type::() .register_rollback_type::() }) - .extend_rollback_schedule(|schedule| { - schedule - .add_stage_after( - RollbackStage::PostUpdate, - AnimationStage::Hydrate, - SystemStage::single_threaded() - .with_system(hydrate_animation_bank_sprites) - .with_system( - hydrate_animated_sprites.after(hydrate_animation_bank_sprites), - ), - ) - .add_stage_after( - AnimationStage::Hydrate, - AnimationStage::Animate, - SystemStage::single_threaded() - .with_system(update_animation_bank_sprites) - .with_system( - update_animated_sprite_components - .after(update_animation_bank_sprites), - ) - .with_system( - animate_sprites - .run_in_state(GameState::InGame) - .run_not_in_state(InGameState::Paused) - .after(update_animated_sprite_components.as_system_label()), - ), - ); - }); + .add_rollback_system(RollbackStage::PostUpdate, hydrate_animation_bank_sprites) + .add_rollback_system(RollbackStage::PostUpdate, hydrate_animated_sprites) + .add_rollback_system(RollbackStage::PostUpdate, update_animation_bank_sprites) + .add_rollback_system(RollbackStage::PostUpdate, update_animated_sprite_components) + .add_rollback_system( + RollbackStage::PostUpdate, + animate_sprites + .run_in_state(GameState::InGame) + .run_not_in_state(InGameState::Paused) + .after(update_animated_sprite_components.as_system_label()), + ); } } diff --git a/src/camera.rs b/src/camera.rs index 10335a5745..1073f91f62 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -13,9 +13,7 @@ impl Plugin for CameraPlugin { fn build(&self, app: &mut App) { app.register_type::() .register_type::() - .extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage(RollbackStage::Update, camera_controller); - }); + .add_rollback_system(RollbackStage::Update, camera_controller); app.add_plugin(bevy_parallax::ParallaxPlugin); } diff --git a/src/damage.rs b/src/damage.rs index 19b404f321..c3c594ba40 100644 --- a/src/damage.rs +++ b/src/damage.rs @@ -17,10 +17,7 @@ impl Plugin for DamagePlugin { .register_rollback_type::() .register_rollback_type::() }) - .extend_rollback_schedule(|schedule| { - schedule - .add_system_to_stage(RollbackStage::PostUpdate, kill_players_in_damage_region); - }); + .add_rollback_system(RollbackStage::PostUpdate, kill_players_in_damage_region); } } diff --git a/src/lifetime.rs b/src/lifetime.rs index 1e20188506..f8026737fc 100644 --- a/src/lifetime.rs +++ b/src/lifetime.rs @@ -8,9 +8,7 @@ impl Plugin for LifetimePlugin { fn build(&self, app: &mut App) { app.register_type::() .extend_rollback_plugin(|plugin| plugin.register_rollback_type::()) - .extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage(RollbackStage::PostUpdate, lifetime_system); - }); + .add_rollback_system(RollbackStage::PostUpdate, lifetime_system); } } diff --git a/src/main.rs b/src/main.rs index 5d6548bd76..c55c10d8e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,6 @@ pub mod prelude; pub mod random; pub mod run_criteria; pub mod schedule; -// pub mod scripting; pub mod session; pub mod ui; pub mod utils; @@ -74,6 +73,7 @@ use crate::{ player::PlayerPlugin, prelude::*, random::RandomPlugin, + schedule::RollbackSystems, session::SessionPlugin, ui::UiPlugin, utils::{run_criteria_game_not_paused, UtilsPlugin}, @@ -103,7 +103,7 @@ pub enum GameEditorState { Visible, } -#[derive(StageLabel)] +#[derive(StageLabel, Eq, PartialEq, Hash)] pub enum RollbackStage { Input, First, @@ -161,42 +161,43 @@ pub fn main() { .add_loopless_state(InGameState::Playing) .add_loopless_state(GameEditorState::Hidden); - // Create the GGRS rollback schedule and plugin + // Create the GGRS rollback schedule, systems, and plugin let mut rollback_schedule = Schedule::default(); + let rollback_systems = RollbackSystems::default(); let rollback_plugin = GGRSPlugin::::new(); - // Add fixed update stagesrefs/branchless/2fd80952e26d905aa258ebb7e6175a7cfc4cb76f rollback_schedule .add_stage(RollbackStage::Input, SystemStage::parallel()) .add_stage_after( RollbackStage::Input, RollbackStage::First, - SystemStage::parallel().with_run_criteria(run_criteria_game_not_paused), + SystemStage::single_threaded().with_run_criteria(run_criteria_game_not_paused), ) .add_stage_after( RollbackStage::First, RollbackStage::PreUpdate, - SystemStage::parallel().with_run_criteria(run_criteria_game_not_paused), + SystemStage::single_threaded().with_run_criteria(run_criteria_game_not_paused), ) .add_stage_after( RollbackStage::PreUpdate, RollbackStage::Update, - SystemStage::parallel().with_run_criteria(run_criteria_game_not_paused), + SystemStage::single_threaded().with_run_criteria(run_criteria_game_not_paused), ) .add_stage_after( RollbackStage::Update, RollbackStage::PostUpdate, - SystemStage::parallel().with_run_criteria(run_criteria_game_not_paused), + SystemStage::single_threaded().with_run_criteria(run_criteria_game_not_paused), ) .add_stage_after( RollbackStage::PostUpdate, RollbackStage::Last, - SystemStage::parallel().with_run_criteria(run_criteria_game_not_paused), + SystemStage::single_threaded().with_run_criteria(run_criteria_game_not_paused), ); // Add the rollback schedule and plugin as resources, temporarily. // This allows plugins to modify them using `crate::schedule::RollbackScheduleAppExt`. app.insert_resource(rollback_schedule); + app.insert_non_send_resource(rollback_systems); app.insert_resource(rollback_plugin); // Install game plugins @@ -235,10 +236,16 @@ pub fn main() { .add_plugin(NetworkingPlugin) .add_plugin(SessionPlugin); - // Pull the schedule back out of the world - let rollback_schedule: Schedule = app.world.remove_resource().unwrap(); + // Pull the rollback systems back out of the world + let mut rollback_schedule: Schedule = app.world.remove_resource().unwrap(); + let rollback_systems: RollbackSystems = app.world.remove_non_send_resource().unwrap(); let ggrs_plugin: GGRSPlugin = app.world.remove_resource().unwrap(); + // Add the rollback systems to the schedule + for (stage, set) in rollback_systems { + rollback_schedule.add_system_set_to_stage(stage, set.graph.into()); + } + // Build the GGRS plugin ggrs_plugin .with_input_system(player::input::input_system) diff --git a/src/map.rs b/src/map.rs index eec84e11be..0f6dc2222d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -30,12 +30,7 @@ impl Plugin for MapPlugin { .register_rollback_type::() .register_rollback_type::>() }) - .extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage( - RollbackStage::Last, - handle_out_of_bounds_players_and_items, - ); - }) + .add_rollback_system(RollbackStage::Last, handle_out_of_bounds_players_and_items) .add_plugin(elements::MapElementsPlugin); } } diff --git a/src/map/elements/decoration.rs b/src/map/elements/decoration.rs index c7f93b83eb..3834dbbe94 100644 --- a/src/map/elements/decoration.rs +++ b/src/map/elements/decoration.rs @@ -4,9 +4,7 @@ pub struct DecorationPlugin; impl Plugin for DecorationPlugin { fn build(&self, app: &mut App) { - app.extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage(RollbackStage::PreUpdate, hydrate_decorations); - }); + app.add_rollback_system(RollbackStage::PreUpdate, hydrate_decorations); } } diff --git a/src/map/elements/grenade.rs b/src/map/elements/grenade.rs index 437f68efe1..4ec646f53a 100644 --- a/src/map/elements/grenade.rs +++ b/src/map/elements/grenade.rs @@ -40,20 +40,14 @@ impl Default for LitGrenade { impl Plugin for GrenadePlugin { fn build(&self, app: &mut App) { - app.extend_rollback_schedule(|schedule| { - schedule - .add_system_to_stage(RollbackStage::PreUpdate, pre_update_in_game) - .add_system_to_stage( - RollbackStage::Update, - update_lit_grenades.before(update_idle_grenades), - ) - .add_system_to_stage(RollbackStage::Update, update_idle_grenades); - }) - .extend_rollback_plugin(|plugin| { - plugin - .register_rollback_type::() - .register_rollback_type::() - }); + app.add_rollback_system(RollbackStage::PreUpdate, pre_update_in_game) + .add_rollback_system(RollbackStage::Update, update_lit_grenades) + .add_rollback_system(RollbackStage::Update, update_idle_grenades) + .extend_rollback_plugin(|plugin| { + plugin + .register_rollback_type::() + .register_rollback_type::() + }); } } diff --git a/src/map/elements/player_spawner.rs b/src/map/elements/player_spawner.rs index 00df729f1b..d130c826e6 100644 --- a/src/map/elements/player_spawner.rs +++ b/src/map/elements/player_spawner.rs @@ -6,9 +6,7 @@ pub struct PlayerSpawnerPlugin; impl Plugin for PlayerSpawnerPlugin { fn build(&self, app: &mut App) { app.init_resource::() - .extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage(RollbackStage::PreUpdate, pre_update_in_game); - }) + .add_rollback_system(RollbackStage::PreUpdate, pre_update_in_game) .extend_rollback_plugin(|plugin| { plugin .register_rollback_type::() diff --git a/src/map/elements/sproinger.rs b/src/map/elements/sproinger.rs index 38a5385e0e..f713dc3f36 100644 --- a/src/map/elements/sproinger.rs +++ b/src/map/elements/sproinger.rs @@ -5,12 +5,9 @@ const FORCE: f32 = 30.0; pub struct SproingerPlugin; impl Plugin for SproingerPlugin { fn build(&self, app: &mut App) { - app.extend_rollback_schedule(|schedule| { - schedule - .add_system_to_stage(RollbackStage::PreUpdate, pre_update_in_game) - .add_system_to_stage(RollbackStage::Update, update_in_game); - }) - .extend_rollback_plugin(|plugin| plugin.register_rollback_type::()); + app.add_rollback_system(RollbackStage::PreUpdate, pre_update_in_game) + .add_rollback_system(RollbackStage::Update, update_in_game) + .extend_rollback_plugin(|plugin| plugin.register_rollback_type::()); } } diff --git a/src/map/elements/sword.rs b/src/map/elements/sword.rs index 035004609c..ff837faedf 100644 --- a/src/map/elements/sword.rs +++ b/src/map/elements/sword.rs @@ -3,12 +3,9 @@ use super::*; pub struct SwordPlugin; impl Plugin for SwordPlugin { fn build(&self, app: &mut App) { - app.extend_rollback_schedule(|schedule| { - schedule - .add_system_to_stage(RollbackStage::PreUpdate, pre_update_in_game) - .add_system_to_stage(RollbackStage::Update, update_in_game); - }) - .extend_rollback_plugin(|plugin| plugin.register_rollback_type::()); + app.add_rollback_system(RollbackStage::PreUpdate, pre_update_in_game) + .add_rollback_system(RollbackStage::Update, update_in_game) + .extend_rollback_plugin(|plugin| plugin.register_rollback_type::()); } } diff --git a/src/player.rs b/src/player.rs index c856adce5c..29916d9a95 100644 --- a/src/player.rs +++ b/src/player.rs @@ -32,12 +32,10 @@ impl Plugin for PlayerPlugin { .register_rollback_type::() .register_rollback_type::() }) - .extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage( - RollbackStage::PreUpdate, - hydrate_players.run_if_resource_exists::(), - ); - }); + .add_rollback_system( + RollbackStage::PreUpdate, + hydrate_players.run_if_resource_exists::(), + ); } } diff --git a/src/player/input.rs b/src/player/input.rs index 4e96402eae..b7196e9af1 100644 --- a/src/player/input.rs +++ b/src/player/input.rs @@ -24,9 +24,7 @@ impl Plugin for PlayerInputPlugin { ) .add_system_to_stage(CoreStage::Last, clear_input_buffer) .extend_rollback_plugin(|plugin| plugin.register_rollback_type::()) - .extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage(RollbackStage::Input, update_user_input); - }); + .add_rollback_system(RollbackStage::Input, update_user_input); } } diff --git a/src/schedule.rs b/src/schedule.rs index 0e686fa471..7e1b8c3471 100644 --- a/src/schedule.rs +++ b/src/schedule.rs @@ -1,13 +1,27 @@ //! Utilities related to system scheduling and, in particular, the netcode rollback schedule. +use bevy::{ecs::schedule::IntoSystemDescriptor, utils::HashMap}; use bevy_ggrs::GGRSPlugin; +use bevy_system_graph::{SystemGraph, SystemGraphNode}; use crate::prelude::*; +pub type RollbackSystems = HashMap; + +pub struct RollbackSystemSet { + pub graph: SystemGraph, + pub last_system: SystemGraphNode, +} + pub trait RollbackScheduleAppExt { fn extend_rollback_schedule(&mut self, f: F) -> &mut Self where F: FnOnce(&mut Schedule); + fn add_rollback_system( + &mut self, + stage: RollbackStage, + system: impl IntoSystemDescriptor, + ) -> &mut Self; fn extend_rollback_plugin(&mut self, f: F) -> &mut Self where F: FnOnce(GGRSPlugin) -> GGRSPlugin; @@ -22,6 +36,31 @@ impl RollbackScheduleAppExt for App { f(&mut schedule); self } + + fn add_rollback_system( + &mut self, + stage: RollbackStage, + system: impl IntoSystemDescriptor, + ) -> &mut Self { + let mut systems = self.world.non_send_resource_mut::(); + match systems.get_mut(&stage) { + Some(set) => { + set.last_system = set.last_system.then(system); + } + None => { + let graph = SystemGraph::new(); + systems.insert( + stage, + RollbackSystemSet { + last_system: graph.root(system), + graph, + }, + ); + } + } + self + } + fn extend_rollback_plugin(&mut self, f: F) -> &mut Self where F: FnOnce(GGRSPlugin) -> GGRSPlugin, diff --git a/src/session.rs b/src/session.rs index e98ee4c890..a4d951cf25 100644 --- a/src/session.rs +++ b/src/session.rs @@ -24,14 +24,9 @@ impl Plugin for SessionPlugin { fn build(&self, app: &mut App) { app.init_resource::() .extend_rollback_plugin(|plugin| plugin.register_rollback_type::()) - .extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage( - RollbackStage::Last, - |mut frame_idx: ResMut| { - frame_idx.0 = frame_idx.0.wrapping_add(1); - trace!("End of simulation frame {}", frame_idx.0); - }, - ); + .add_rollback_system(RollbackStage::Last, |mut frame_idx: ResMut| { + frame_idx.0 = frame_idx.0.wrapping_add(1); + trace!("End of simulation frame {}", frame_idx.0); }); } } diff --git a/src/utils/event.rs b/src/utils/event.rs index e2d23bc1b7..aff481ce95 100644 --- a/src/utils/event.rs +++ b/src/utils/event.rs @@ -9,9 +9,7 @@ pub trait FixedUpdateEventAppExt { impl FixedUpdateEventAppExt for bevy::app::App { fn add_fixed_update_event(&mut self) -> &mut Self { self.init_resource::>() - .extend_rollback_schedule(|schedule| { - schedule.add_system_to_stage(RollbackStage::First, Events::::update_system); - }); + .add_rollback_system(RollbackStage::First, Events::::update_system); self }