Skip to content

Commit

Permalink
BRP strict field in query (#16725)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
Leinnan authored Dec 14, 2024
1 parent 30bd641 commit 897ffad
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 14 deletions.
40 changes: 26 additions & 14 deletions crates/bevy_remote/src/builtin_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -527,19 +532,22 @@ pub fn process_remote_query_request(In(params): In<Option<Value>>, world: &mut W
has,
},
filter: BrpQueryFilter { without, with },
strict,
} = parse_some(params)?;

let app_type_registry = world.resource::<AppTypeRegistry>().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::<FilteredEntityRef>::new(world);
for (_, component) in &components {
Expand Down Expand Up @@ -659,8 +667,8 @@ pub fn process_remote_remove_request(
let app_type_registry = world.resource::<AppTypeRegistry>().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)?;
Expand Down Expand Up @@ -818,16 +826,20 @@ fn get_component_ids(
type_registry: &TypeRegistry,
world: &World,
component_paths: Vec<String>,
strict: bool,
) -> AnyhowResult<Vec<(TypeId, ComponentId)>> {
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));
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_remote/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions examples/remote/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down

0 comments on commit 897ffad

Please sign in to comment.