-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Fix BRP query failing when specifying missing/invalid components #18871
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Welcome, new contributor! Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proposed change looks fine and seems to be a step in the right direction, however, I think something more is needed.
The expected remote query result for the strict: false query should be to return an empty list of entities, since foo::bar::MyComponent doesn't exist:
{
"jsonrpc": "2.0",
"id": 0,
"result": []
}However with the proposed change, since no error is raised, all the existing entities are returned as a response:
{"jsonrpc":"2.0","id":0,"result":[{"components":{},"entity":4294967297},{"components":{},"entity":4294967298},{"components":{},"entity":4294967299},{"components":{},"entity":4294967300},{"components":{},"entity":4294967317},{"components":{},"entity":4294967302},{"components":{},"entity":4294967303},{"components":{},"entity":4294967304},{"components":{},"entity":4294967305},{"components":{},"entity":4294967306},{"components":{},"entity":4294967307},{"components":{},"entity":4294967308},{"components":{},"entity":4294967309},{"components":{},"entity":4294967312},{"components":{},"entity":4294967313},{"components":{},"entity":4294967314},{"components":{},"entity":4294967315},{"components":{},"entity":4294967316},{"components":{},"entity":4294967310},{"components":{},"entity":4294967311},{"components":{},"entity":4294967318},{"components":{},"entity":4294967320},{"components":{},"entity":4294967319},{"components":{},"entity":4294967321},{"components":{},"entity":4294967322},{"components":{},"entity":4294967323},{"components":{},"entity":4294967324},{"components":{},"entity":4294967325},{"components":{},"entity":4294967301},{"components":{},"entity":4294967296}]}As a result of silently ignoring missing components, it seems like the query being built in process_remote_query_request has no data nor filter configured and as such matches all entities.
|
I agree, it should return an empty array. {
"jsonrpc": "2.0",
"method": "bevy/query",
"id": 0,
"params": {
"data": {
"option": [
"foo::bar::MyComponent"
]
}
}
} |
|
I don't think avoiding the error and returning too many entities is a good fix here. If the correct fix is larger, it can go in the 0.16.1 |
|
Then the following migration guide item should be removed: https://bevyengine.org/learn/migration-guides/0-15-to-0-16/#add-strict-field-to-brp-bevy-query. |
|
I responded in more details in #18869, I feel like the fix needs to be a bit more holistic |
On bevy/query remote requests, when "strict" is false: - Unregistered components in "option" and "without" are ignored. - Unregistered components in "has" are considered absent from the entity. - Unregistered components in "components" and "with" result in an empty response since they specify hard requirements.
|
I fixed the issue following @Henauxg's specs: #18869 (comment).
|
|
I don't think the build failed because of my changes. Maybe I rebased from a bad |
Henauxg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't test all the queries dynamically (just the components one), but the code looks good and does what we expect. If no one else raises their hand to spec it differently, it's fine with me.
| let component_ids = get_component_ids(&type_registry, world, components, true) | ||
| .and_then(|(registered, unregistered)| { | ||
| if unregistered.is_empty() { | ||
| Ok(registered) | ||
| } else { | ||
| Err(anyhow!("Unregistered component types: {:?}", unregistered)) | ||
| } | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, strict is set as true. This means unregistered will always be empty. Should we just simply return Ok(registered) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get_component_ids with strict true returns an empty unregistered vector for now but that may change later? It's just a sanity/robust check. It depends on the definition of get_component_ids ...
I have no strong feeling about this so if you insist, I'll change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, no, I'm fine with robustness 👍
| let (components, unregistered_in_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 option = get_component_ids(&type_registry, world, option, strict) | ||
| .map_err(BrpError::component_error)?; | ||
| let has = | ||
| let (has, unregistered_in_has) = | ||
| get_component_ids(&type_registry, world, has, strict).map_err(BrpError::component_error)?; | ||
| let without = get_component_ids(&type_registry, world, without, strict) | ||
| 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) | ||
| let (with, unregistered_in_with) = get_component_ids(&type_registry, world, with, strict) | ||
| .map_err(BrpError::component_error)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit unfortunate that we retrieve the list of unregistered components type paths for all of them but only ever use the returned values within one of those lists for unregistered_in_has. But I guess doing otherwise would imply having specialized versions of get_component_ids, and I'm not sure that it's worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I prefered code simplicity and clarity than a small optimization. Again, if you insist, I'll change it with a specialized version.
|
(Removing X-Contentious as it's no longer the same proposed fix) |
Trashtalk217
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tested this locally, and I approve of the changes.
|
Please add a test(s) that demonstrates and ensures correct functionality. |
|
I agree tests are much needed for BRP-related stuff. I am not sure what form they should take. Should we make actual HTTP requests? That would probably be the best but I believe that would be much work and should go in its own pull request. If you see a way to make lightweight unit tests, please assist me. |
) # Objective - Fixes #18869. ## Solution The issue was the `?` after a `Result` raising the error, instead of treating it. Instead it is handled with `ok`, `and_then`, `map` ... _Edit: I added the following logic._ On `bevy/query` remote requests, when `strict` is false: - Unregistered components in `option` and `without` are ignored. - Unregistered components in `has` are considered absent from the entity. - Unregistered components in `components` and `with` result in an empty response since they specify hard requirements. I made the `get_component_ids` function return a `AnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)>` instead of the previous `AnyhowResult<Vec<(TypeId, ComponentId)>>`; that is I added the list of unregistered components. ## Testing I tested changes using the same procedure as in the linked issue: ```sh cargo run --example server --features="bevy_remote" ``` In another terminal: ```sh # Not strict: $ curl -X POST http://localhost:15702 -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "bevy/query", "id": 0, "params": { "data": { "components": [ "foo::bar::MyComponent" ] } } }' {"jsonrpc":"2.0","id":0,"result":[]} # Strict: $ curl -X POST http://localhost:15702 -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "bevy/query", "id": 0, "params": { "data": { "components": [ "foo::bar::MyComponent" ] }, "strict": true } }' {"jsonrpc":"2.0","id":0,"error":{"code":-23402,"message":"Component `foo::bar::MyComponent` isn't registered or used in the world"}} ```
Objective
Solution
The issue was the
?after aResultraising the error, instead of treating it.Instead it is handled with
ok,and_then,map...Edit: I added the following logic.
On
bevy/queryremote requests, whenstrictis false:optionandwithoutare ignored.hasare considered absent from the entity.componentsandwithresult in an empty response since they specify hard requirements.I made the
get_component_idsfunction return aAnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)>instead of the previousAnyhowResult<Vec<(TypeId, ComponentId)>>; that is I added the list of unregistered components.Testing
I tested changes using the same procedure as in the linked issue:
cargo run --example server --features="bevy_remote"In another terminal: