From 897ffad8af979de5cb2e6f79e46b6d5773486ede Mon Sep 17 00:00:00 2001 From: MevLyshkin Date: Sat, 14 Dec 2024 06:22:19 +0100 Subject: [PATCH] BRP strict field in query (#16725) # Objective - Allow skiping components that don't have ComponentId yet instead of failing `bevy/query` request. ## Solution - Describe the solution used to achieve the objective above. ## Testing My naive approach boils down to: - bevy/list to get list of all components. - bevy/query with empty components and has fields and a option that contains result of the bevy/list. Before that change I end up with bunch of `Component xxx isn't used in the world` because some of the components wasn't spawned at any moment yet in the game. Now it should work. ## Migration Guide - `BrpQueryParams` now has `strict` boolean field. It serfs as a flag to fail when encountering an invalid component rather than skipping it. Defaults to false. --- crates/bevy_remote/src/builtin_methods.rs | 40 +++++++++++++++-------- crates/bevy_remote/src/lib.rs | 2 ++ examples/remote/client.rs | 1 + 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/crates/bevy_remote/src/builtin_methods.rs b/crates/bevy_remote/src/builtin_methods.rs index 7cf59c825b194..840cc102eabea 100644 --- a/crates/bevy_remote/src/builtin_methods.rs +++ b/crates/bevy_remote/src/builtin_methods.rs @@ -92,6 +92,11 @@ pub struct BrpQueryParams { /// exclude from the results. #[serde(default)] pub filter: BrpQueryFilter, + + /// An optional flag to fail when encountering an invalid component rather + /// than skipping it. Defaults to false. + #[serde(default)] + pub strict: bool, } /// `bevy/spawn`: Creates a new entity with the given components and responds @@ -527,19 +532,22 @@ pub fn process_remote_query_request(In(params): In>, world: &mut W has, }, filter: BrpQueryFilter { without, with }, + strict, } = parse_some(params)?; let app_type_registry = world.resource::().clone(); let type_registry = app_type_registry.read(); - let components = - get_component_ids(&type_registry, world, components).map_err(BrpError::component_error)?; - let option = - get_component_ids(&type_registry, world, option).map_err(BrpError::component_error)?; - let has = get_component_ids(&type_registry, world, has).map_err(BrpError::component_error)?; - let without = - get_component_ids(&type_registry, world, without).map_err(BrpError::component_error)?; - let with = get_component_ids(&type_registry, world, with).map_err(BrpError::component_error)?; + let components = get_component_ids(&type_registry, world, components, strict) + .map_err(BrpError::component_error)?; + let option = get_component_ids(&type_registry, world, option, strict) + .map_err(BrpError::component_error)?; + let has = + get_component_ids(&type_registry, world, has, strict).map_err(BrpError::component_error)?; + let without = get_component_ids(&type_registry, world, without, strict) + .map_err(BrpError::component_error)?; + let with = get_component_ids(&type_registry, world, with, strict) + .map_err(BrpError::component_error)?; let mut query = QueryBuilder::::new(world); for (_, component) in &components { @@ -659,8 +667,8 @@ pub fn process_remote_remove_request( let app_type_registry = world.resource::().clone(); let type_registry = app_type_registry.read(); - let component_ids = - get_component_ids(&type_registry, world, components).map_err(BrpError::component_error)?; + let component_ids = get_component_ids(&type_registry, world, components, true) + .map_err(BrpError::component_error)?; // Remove the components. let mut entity_world_mut = get_entity_mut(world, entity)?; @@ -818,16 +826,20 @@ fn get_component_ids( type_registry: &TypeRegistry, world: &World, component_paths: Vec, + strict: bool, ) -> AnyhowResult> { let mut component_ids = vec![]; for component_path in component_paths { let type_id = get_component_type_registration(type_registry, &component_path)?.type_id(); let Some(component_id) = world.components().get_id(type_id) else { - return Err(anyhow!( - "Component `{}` isn't used in the world", - component_path - )); + if strict { + return Err(anyhow!( + "Component `{}` isn't used in the world", + component_path + )); + } + continue; }; component_ids.push((type_id, component_id)); diff --git a/crates/bevy_remote/src/lib.rs b/crates/bevy_remote/src/lib.rs index 103afb0327174..3d6781444148d 100644 --- a/crates/bevy_remote/src/lib.rs +++ b/crates/bevy_remote/src/lib.rs @@ -142,6 +142,8 @@ //! on entities in order for them to be included in results. //! - `without` (optional): An array of fully-qualified type names of components that must *not* be //! present on entities in order for them to be included in results. +//! - `strict` (optional): A flag to enable strict mode which will fail if any one of the +//! components is not present or can not be reflected. Defaults to false. //! //! `result`: An array, each of which is an object containing: //! - `entity`: The ID of a query-matching entity. diff --git a/examples/remote/client.rs b/examples/remote/client.rs index 7794aec0525f1..1188dd0876634 100644 --- a/examples/remote/client.rs +++ b/examples/remote/client.rs @@ -57,6 +57,7 @@ fn main() -> AnyhowResult<()> { option: Vec::default(), has: Vec::default(), }, + strict: false, filter: BrpQueryFilter::default(), }) .expect("Unable to convert query parameters to a valid JSON value"),