From 472c419dedb457a6bf7f25f7c3fb80e0fb59c4b6 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 19 Feb 2024 10:17:02 +1100 Subject: [PATCH] Update to Bevy 0.13 --- Cargo.toml | 6 +-- examples/box_game/box_game.rs | 24 ++++++------ examples/stress_tests/particles.rs | 4 +- src/lib.rs | 1 + src/rollback.rs | 4 +- src/snapshot/component_map.rs | 58 ++++------------------------- src/snapshot/resource_map.rs | 48 +++--------------------- src/snapshot/rollback_entity_map.rs | 25 +++++++++++-- src/time.rs | 2 +- tests/integration.rs | 21 ++++++----- 10 files changed, 69 insertions(+), 124 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0bd7682..03a01f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ categories = ["network-programming", "game-development"] wasm-bindgen = ["instant/wasm-bindgen", "ggrs/wasm-bindgen"] [dependencies] -bevy = { version = "0.12", default-features = false } +bevy = { version = "0.13", default-features = false } bytemuck = { version = "1.7", features=["derive"]} instant = { version = "0.1", optional = true } log = "0.4" @@ -24,11 +24,11 @@ log = "0.4" ggrs = { git = "https://github.com/gschup/ggrs", features=["sync-send"]} [dev-dependencies] -bevy = { version = "0.12", default-features = true } +bevy = { version = "0.13", default-features = true } clap = { version = "4.4", features = ["derive"] } rand = "0.8.4" rand_xoshiro = "0.6" -serde = "1.0.130" +serde = "1.0.196" serde_json = "1.0" serial_test = "2.0" diff --git a/examples/box_game/box_game.rs b/examples/box_game/box_game.rs index 077a15e..16761f6 100644 --- a/examples/box_game/box_game.rs +++ b/examples/box_game/box_game.rs @@ -1,4 +1,4 @@ -use bevy::{prelude::*, utils::HashMap}; +use bevy::{prelude::*, render::mesh::PlaneMeshBuilder, utils::HashMap}; use bevy_ggrs::{ AddRollbackCommandExtension, GgrsConfig, LocalInputs, LocalPlayers, PlayerInputs, Rollback, Session, @@ -54,7 +54,7 @@ pub struct FrameCount { /// Collects player inputs during [`ReadInputs`](`bevy_ggrs::ReadInputs`) and creates a [`LocalInputs`] resource. pub fn read_local_inputs( mut commands: Commands, - keyboard_input: Res>, + keyboard_input: Res>, local_players: Res, ) { let mut local_inputs = HashMap::new(); @@ -62,16 +62,16 @@ pub fn read_local_inputs( for handle in &local_players.0 { let mut input: u8 = 0; - if keyboard_input.pressed(KeyCode::W) { + if keyboard_input.pressed(KeyCode::KeyW) { input |= INPUT_UP; } - if keyboard_input.pressed(KeyCode::A) { + if keyboard_input.pressed(KeyCode::KeyA) { input |= INPUT_LEFT; } - if keyboard_input.pressed(KeyCode::S) { + if keyboard_input.pressed(KeyCode::KeyS) { input |= INPUT_DOWN; } - if keyboard_input.pressed(KeyCode::D) { + if keyboard_input.pressed(KeyCode::KeyD) { input |= INPUT_RIGHT; } @@ -95,16 +95,16 @@ pub fn setup_system( // A ground plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { - size: PLANE_SIZE, - ..default() + mesh: meshes.add(Mesh::from(PlaneMeshBuilder { + plane: Plane3d::new(Vec3::Y), + half_size: Vec2::splat(PLANE_SIZE / 2.0), })), - material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + material: materials.add(StandardMaterial::from(Color::rgb(0.3, 0.5, 0.3))), ..default() }); let r = PLANE_SIZE / 4.; - let mesh = meshes.add(Mesh::from(shape::Cube { size: CUBE_SIZE })); + let mesh = meshes.add(Mesh::from(Cuboid::from_size(Vec3::splat(CUBE_SIZE)))); for handle in 0..num_players { let rot = handle as f32 / num_players as f32 * 2. * std::f32::consts::PI; @@ -123,7 +123,7 @@ pub fn setup_system( // ...add visual information... PbrBundle { mesh: mesh.clone(), - material: materials.add(color.into()), + material: materials.add(StandardMaterial::from(color)), transform, ..default() }, diff --git a/examples/stress_tests/particles.rs b/examples/stress_tests/particles.rs index 15d7a33..b7525a0 100644 --- a/examples/stress_tests/particles.rs +++ b/examples/stress_tests/particles.rs @@ -74,7 +74,7 @@ const INPUT_NOOP: u8 = 1 << 5; fn read_local_inputs( mut commands: Commands, - keyboard_input: Res>, + keyboard_input: Res>, local_players: Res, ) { let mut local_inputs = HashMap::new(); @@ -88,7 +88,7 @@ fn read_local_inputs( } // n is a no-op key, press to simply trigger a rollback - if keyboard_input.pressed(KeyCode::N) { + if keyboard_input.pressed(KeyCode::KeyN) { input |= INPUT_NOOP; } diff --git a/src/lib.rs b/src/lib.rs index c099b29..af0e732 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ //! //! See [`GgrsPlugin`] for getting started. #![forbid(unsafe_code)] // let us try +#![allow(clippy::type_complexity)] // Suppress warnings around Query use bevy::{ ecs::{ diff --git a/src/rollback.rs b/src/rollback.rs index 75e52de..cebc456 100644 --- a/src/rollback.rs +++ b/src/rollback.rs @@ -44,9 +44,9 @@ pub trait AddRollbackCommandExtension: private::AddRollbackCommandExtensionSeal fn add_rollback(&mut self) -> &mut Self; } -impl<'w, 's, 'a> private::AddRollbackCommandExtensionSeal for EntityCommands<'w, 's, 'a> {} +impl<'a> private::AddRollbackCommandExtensionSeal for EntityCommands<'a> {} -impl<'w, 's, 'a> AddRollbackCommandExtension for EntityCommands<'w, 's, 'a> { +impl<'a> AddRollbackCommandExtension for EntityCommands<'a> { fn add_rollback(&mut self) -> &mut Self { self.add(AddRollbackCommand); self diff --git a/src/snapshot/component_map.rs b/src/snapshot/component_map.rs index 607feff..ed7340f 100644 --- a/src/snapshot/component_map.rs +++ b/src/snapshot/component_map.rs @@ -1,9 +1,6 @@ use std::marker::PhantomData; -use bevy::{ - ecs::entity::{EntityMapper, MapEntities}, - prelude::*, -}; +use bevy::{ecs::entity::MapEntities, prelude::*}; use crate::{LoadWorld, LoadWorldSet, RollbackEntityMap}; @@ -26,8 +23,8 @@ use crate::{LoadWorld, LoadWorldSet, RollbackEntityMap}; /// struct BestFriend(Entity); /// /// impl MapEntities for BestFriend { -/// fn map_entities(&mut self, entity_mapper: &mut EntityMapper) { -/// self.0 = entity_mapper.get_or_reserve(self.0); +/// fn map_entities(&mut self, entity_mapper: &mut M) { +/// self.0 = entity_mapper.map_entity(self.0); /// } /// } /// @@ -72,55 +69,16 @@ fn apply_rollback_map_to_component_inner(world: &mut World, map: Mut); + for (original, _new) in map.iter() { + if let Some(mut component) = world.get_mut::(original) { + component.map_entities(&mut map.as_ref()); + } + } trace!( "Mapped {}", bevy::utils::get_short_name(std::any::type_name::()) ); - - // If the entity map is now larger than the set of rollback entities, then dead entities were created. - // TODO: This workaround is required because the current behavior of `map_all_entities` is to change all entities, - // creating dead entities instead of leaving them with their original value. If `EntityMapper` behavior changes, - // then this workaround may no longer be required. - if applied_entity_map.len() > map.len() { - // Reverse dead-mappings, no-op correct mappings - for original in applied_entity_map.keys().copied().collect::>() { - let mapped = applied_entity_map.remove(&original).unwrap(); - - if map.get(original).is_some() { - // Rollback entity was correctly mapped; no-op - applied_entity_map.insert(mapped, mapped); - } else { - // An untracked bystander was mapped to a dead end; reverse - applied_entity_map.insert(mapped, original); - } - } - - // Map entities a second time, fixing dead entities - EntityMapper::world_scope(&mut applied_entity_map, world, apply_map::); - - trace!( - "Re-Mapped {}", - bevy::utils::get_short_name(std::any::type_name::()) - ); - } -} - -fn apply_map(world: &mut World, entity_mapper: &mut EntityMapper) { - let entities = entity_mapper - .get_map() - .values() - .copied() - .collect::>(); - - for entity in &entities { - if let Some(mut component) = world.get_mut::(*entity) { - component.map_entities(entity_mapper); - } - } } impl Plugin for ComponentMapEntitiesPlugin diff --git a/src/snapshot/resource_map.rs b/src/snapshot/resource_map.rs index 541869b..b00978e 100644 --- a/src/snapshot/resource_map.rs +++ b/src/snapshot/resource_map.rs @@ -1,9 +1,6 @@ use std::marker::PhantomData; -use bevy::{ - ecs::entity::{EntityMapper, MapEntities}, - prelude::*, -}; +use bevy::{ecs::entity::MapEntities, prelude::*}; use crate::{LoadWorld, LoadWorldSet, RollbackEntityMap}; @@ -26,8 +23,8 @@ use crate::{LoadWorld, LoadWorldSet, RollbackEntityMap}; /// struct Player(Entity); /// /// impl MapEntities for Player { -/// fn map_entities(&mut self, entity_mapper: &mut EntityMapper) { -/// self.0 = entity_mapper.get_or_reserve(self.0); +/// fn map_entities(&mut self, entity_mapper: &mut M) { +/// self.0 = entity_mapper.map_entity(self.0); /// } /// } /// @@ -72,47 +69,14 @@ fn apply_rollback_map_to_resource_inner(world: &mut World, map: Mut); + if let Some(mut resource) = world.get_resource_mut::() { + resource.map_entities(&mut map.as_ref()); + } trace!( "Mapped {}", bevy::utils::get_short_name(std::any::type_name::()) ); - - // If the entity map is now larger than the set of rollback entities, then dead entities were created. - // TODO: This workaround is required because the current behavior of `map_all_entities` is to change all entities, - // creating dead entities instead of leaving them with their original value. If `EntityMapper` behavior changes, - // then this workaround may no longer be required. - if applied_entity_map.len() > map.len() { - // Reverse dead-mappings, no-op correct mappings - for original in applied_entity_map.keys().copied().collect::>() { - let mapped = applied_entity_map.remove(&original).unwrap(); - - if map.get(original).is_some() { - // Rollback entity was correctly mapped; no-op - applied_entity_map.insert(mapped, mapped); - } else { - // An untracked bystander was mapped to a dead end; reverse - applied_entity_map.insert(mapped, original); - } - } - - // Map entities a second time, fixing dead entities - EntityMapper::world_scope(&mut applied_entity_map, world, apply_map::); - - trace!( - "Re-Mapped {}", - bevy::utils::get_short_name(std::any::type_name::()) - ); - } -} - -fn apply_map(world: &mut World, entity_mapper: &mut EntityMapper) { - if let Some(mut resource) = world.get_resource_mut::() { - resource.map_entities(entity_mapper); - } } impl Plugin for ResourceMapEntitiesPlugin diff --git a/src/snapshot/rollback_entity_map.rs b/src/snapshot/rollback_entity_map.rs index d008762..b6cafaf 100644 --- a/src/snapshot/rollback_entity_map.rs +++ b/src/snapshot/rollback_entity_map.rs @@ -1,14 +1,26 @@ -use bevy::{prelude::*, utils::HashMap}; +use bevy::{ecs::entity::EntityHashMap, prelude::*, utils::HashMap}; /// A [`Resource`] which provides an [`EntityMap`], describing how [`Entities`](`Entity`) /// changed during a rollback. #[derive(Resource, Default)] -pub struct RollbackEntityMap(HashMap); +pub struct RollbackEntityMap(EntityHashMap); + +impl From> for RollbackEntityMap { + fn from(value: EntityHashMap) -> Self { + Self(value) + } +} + +impl From> for RollbackEntityMap { + fn from(value: HashMap) -> Self { + Self(value.into_iter().collect()) + } +} impl RollbackEntityMap { /// Create a new [`RollbackEntityMap`], which can generate [`EntityMaps`](`EntityMap`) as required. pub fn new(map: HashMap) -> Self { - Self(map) + map.into() } /// Generate an owned [`EntityMap`], which can be used concurrently with other systems. @@ -46,3 +58,10 @@ impl RollbackEntityMap { map.is_empty() } } + +impl<'a> EntityMapper for &'a RollbackEntityMap { + /// Map the provided [`Entity`], or return it unmodified if it does not need to be mapped. + fn map_entity(&mut self, entity: Entity) -> Entity { + self.get(entity).unwrap_or(entity) + } +} diff --git a/src/time.rs b/src/time.rs index b5e1296..e808776 100644 --- a/src/time.rs +++ b/src/time.rs @@ -86,7 +86,7 @@ impl GgrsTimePlugin { impl Plugin for GgrsTimePlugin { fn build(&self, app: &mut App) { - app.insert_resource(Time::new_with(GgrsTime::default())) + app.insert_resource(Time::new_with(GgrsTime)) .add_plugins(ResourceSnapshotPlugin::>>::default()) .add_systems( AdvanceWorld, diff --git a/tests/integration.rs b/tests/integration.rs index 1843154..2b60c7b 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,5 +1,8 @@ use bevy::{ - input::{keyboard::KeyboardInput, ButtonState, Input, InputPlugin}, + input::{ + keyboard::{Key, KeyboardInput}, + ButtonInput, ButtonState, InputPlugin, + }, prelude::*, time::TimeUpdateStrategy, utils::{Duration, HashMap}, @@ -55,7 +58,7 @@ fn it_syncs_rollback_components() -> Result<(), Box> { let mut app2 = create_app::(session2); for _ in 0..50 { - press_key(&mut app1, KeyCode::W); + press_key(&mut app1, KeyCode::KeyW); app1.update(); app2.update(); } @@ -72,7 +75,7 @@ fn it_syncs_rollback_components() -> Result<(), Box> { fn create_app(session: P2PSession) -> App { let mut app = App::new(); app.add_plugins(MinimalPlugins) - .add_plugins(InputPlugin::default()) + .add_plugins(InputPlugin) .add_plugins(GgrsPlugin::::default()) .insert_resource(TimeUpdateStrategy::ManualDuration(Duration::from_secs_f64( 1.0 / 60.0, @@ -103,7 +106,7 @@ fn create_players() -> (TestPlayer, TestPlayer) { const REMOTE_PORT: u16 = 8082; let remote_addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), PLAYER1_PORT); let remote_addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), REMOTE_PORT); - return ( + ( TestPlayer { handle: 0, address: remote_addr1, @@ -112,7 +115,7 @@ fn create_players() -> (TestPlayer, TestPlayer) { handle: 1, address: remote_addr2, }, - ); + ) } fn start_session( @@ -138,14 +141,14 @@ const INPUT_UP: u8 = 1 << 0; pub fn read_local_inputs( mut commands: Commands, - keyboard_input: Res>, + keyboard_input: Res>, local_players: Res, ) { let mut local_inputs = HashMap::new(); for handle in &local_players.0 { let mut input: u8 = 0; - if keyboard_input.pressed(KeyCode::W) { + if keyboard_input.pressed(KeyCode::KeyW) { input |= INPUT_UP; } local_inputs.insert(*handle, BoxInput { inp: input }); @@ -160,8 +163,8 @@ pub fn increase_frame_system(mut frame_count: ResMut) { fn press_key(app: &mut App, key: KeyCode) { app.world.send_event(KeyboardInput { - scan_code: 0, - key_code: Option::from(key), + logical_key: Key::Character("w".into()), + key_code: key, state: ButtonState::Pressed, window: Entity::PLACEHOLDER, });