mirror of
https://github.com/bevyengine/bevy.git
synced 2026-07-04 01:33:14 -04:00
create-pull-request/patch
86 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
96e198e1e4 |
Fix Deserialize derive for BrpResponse (#24305)
# Objective
Bevy remote's `BrpResponse` derives `serde::Deserialize`, but the static
lifetime prevents it from actually being so:
```
let response: BrpResponse = serde_json::from_str(&response)?;
---------------------^^^^^^^^^-
| |
| borrowed value does not live long enough
argument requires that `response` is borrowed for `'static`
```
No migration guide needed, since the interface for `BrpResponse::new`
used by downstream implementers remains unchanged.
## Solution
Remove the `jsonrpc` field by writing a custom `impl` for `Serialize` &
`Deserialize`, which is the same solution as done previously in #23175.
## Testing
This change has a very low impact. Tests pass.
## Alternatives
1. Replace jsonrpc with `String`
2. Replace jsonrpc with `Cow`
---------
Co-authored-by: Mira <specificprotagonist@posteo.org>
|
||
|
|
d38a00ff8c |
bevy_reflect: Allow parameters to be passed to type data (#13723)
# Objective Addresses [comments](https://github.com/bevyengine/bevy/pull/7317#issuecomment-2143075852) regarding #7317 (note that this doesn't replace #7317, there are still some great improvements there besides this syntactical problem). There currently exist some "special" type data registrations that can be registered like other type data (e.g. `#[reflect(Hash)]`) or can use a "special" syntax to allow specifying custom implementations (e.g. `#[reflect(Hash(custom_hash_fn))]`). And there may be more to follow (#13432). What's interesting is that most of these special cased registrations don't actually come with any type data type. Instead, they simply modify methods on `Reflect` (e.g. `Reflect::reflect_hash`). #7317 sought to distinguish between these "special" registrations by making them lowercase and use a more conventional attribute style: `#[reflect(hash = "custom_hash_fn")]`. However, while this did help distinguish these registrations and make them a bit prettier, they now require the user to actually know which traits are "special" and which are not (as pointed out [here](https://github.com/bevyengine/bevy/pull/7317#issuecomment-2143075852)). Ideally, users shouldn't have to know which traits are "special" until they need to. For most users, they should just know that they need to register their trait in order for certain things to work. And the special-casing may be easier to follow if we open up the configuration abilities to _all_ type data. ## Solution This PR introduces `CreateTypeData` which replaces `FromType`. This was done for two reasons. Firstly, `FromType` isn't very descriptive as to what it should be used for. We are creating type data from a type, but it's not immediately clear this is even for type data. Renaming to `CreateTypeData` should hopefully make this much clearer. Secondly, in order to support type data with parameters like the `custom_hash_fn` in `reflect(Hash(custom_hash_fn))`, an additional `Input` type parameter had to be added. This makes the new signature `CreateTypeData<T, Input = ()>`. We can now create type data that accepts input! ```rust trait Combine { fn combine(a: f32, b: f32) -> f32; } #[derive(Clone)] struct ReflectCombine { multiplier: f32, additional: f32, combine: fn(f32, f32) -> f32, } impl ReflectCombine { pub fn combine(&self, a: f32, b: f32) -> f32 { let combined = (self.combine)(a, b); let multiplied = self.multiplier * combined; multiplied + self.additional } } impl<T: Combine + Reflect> CreateTypeData<T, (f32, f32)> for ReflectCombine { fn create_type_data(input: (f32, f32)) -> Self { Self { multiplier: input.0, additional: input.1, combine: T::combine, } } } ``` And then register them with the special function-like syntax: ```rust #[derive(Reflect)] #[reflect(Combine(2.0, 4.0))] struct Foo; ``` The above code will compile into the following registration: ```rust registration.insert(<ReflectCombine as CreateTypeData<Self, _>>::create_type_data((2.0, 4.0))) ``` Notice how the macro automatically generates the tuple for us, so we don't have to add an additional layer of parentheses. ### Multiple Input Types You might be wondering why we're using a type parameter instead of an associated type to specify the input type. An associated type would limit us to a single implementation. This means that if we want to support the type data with optional parameters (e.g. support both `Hash` and `Hash(custom_hash_fn)`), then all type data must take in `Option<Self::Input>`, regardless of whether or not a `None` case is supported. This is important because the macro has to be pass in _something_, whether that be `()` or `None`. By using a type parameter we open the door to type data with required input: ```rust // `ReflectMyTrait` must be registered with input impl<T> CreateTypeData<T, u32> for ReflectMyTrait { fn create_type_data(input: u32) -> Self { Self { value: input, } } } // And we can support all different input types impl<T> CreateTypeData<T, i32> for ReflectMyTrait { fn create_type_data(input: i32) -> Self { Self { value: input.abs() as u32, } } } ``` However, this may be something we don't necessarily care about since users could also get away with this using custom input enums. And the required-input case could be deferred until runtime (i.e. maybe a panic in the `None` case). ### Adding `ReflectPartialEq` and `ReflectHash` I had originally considered adding `ReflectPartialEq` and `ReflectHash` type data to further decrease the differences between the "special" registrations and the regular ones. However, I chose not to do that to (1) reduce the complexity of this PR and (2) we may end up removing these entirely due to #8695. ### What else is this good for? Another question you might have is what else this is good for beyond just making things a bit more consistent. I'm not sure exactly how the community will use it, but I can see it being used for things like feature gating certain functionality: ```rust #[derive(Reflect)] #[cfg_attr(feature = "debug", reflect(MyTrait(true)))] #[cfg_attr(not(feature = "debug"), reflect(MyTrait(false)))] struct Foo; ``` Or to emulate specialization via reflection: ```rust impl<T> DoSomething for T { fn do_something(&self) { println!("Doing the same old stuff."); } } #[derive(Reflect)] #[reflect(ReflectDoSomething(|_| { println!("Doing something special!"); }))] struct Foo; ``` Note that all of the above could always be done with manual registration. However, due to them requiring input, some cases could _only_ be done with manual registration. This PR mainly opens the door to doing more of this interesting stuff with type data via the macro registration. It not only unifies "special" and regular registrations, but also manual and automatic registrations. ## Testing The tests for this feature are split into doctests (for the docs on `CreateTypeData`) and in the compile-fail tests. These will both be verified automatically by CI. --- ## Changelog - Replaced `FromType<T>` with `CreateTypeData<T, Input = ()>` - Type data may now opt-in to accepting input during creation using the `#[reflect(MyTrait(...))]` syntax - Added `TypeRegistry::register_type_data_with` method ## Migration Guide `FromType<T>` has been replaced by `CreateTypeData<T, Input = ()>`. Implementors of `FromType<T>` will need to update their implementation: ```rust // BEFORE impl<T> FromType<T> for ReflectMyTrait { fn from_type() -> Self { // ... } } // AFTER impl<T> CreateTypeData<T> for ReflectMyTrait { fn create_type_data(input: ()) -> Self { // ... } } ``` Additionally, any calls made to `FromType::from_type` will need to be updated as well: ```rust // BEFORE <ReflectMyTrait as FromType<Foo>>::from_type() // AFTER <ReflectMyTrait as CreateTypeData<Foo>>::create_type_data(()) ``` |
||
|
|
ec2b9dba62 |
can have observers through BRP (#23800)
# Objective - Observe events through BRP ## Solution - Add a observe + watch method to observe events - remote observer systems are limited to only the event as a parameter, processing it will have to happen on the client side - depends on #23797 ## Testing - CI with new test --------- Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com> |
||
|
|
dc9fad28c4 |
Allow BRP schedule.graph to initialize the schedules it is collecting. (#23743)
# Objective - In #23733, we started using the `ScheduleData` API for the BRP `schedule.graph` endpoint. - This had a consequence that only schedules that were initialized (i.e., previously ran) could be returned. - In addition, the schedule data was missing edges for automatically inserted `apply_deferred` systems. ## Solution - Cache the `ScheduleBuildMetadata` whenever a schedule is built (the `ScheduleBuilt` event). - Lookup this metadata when BRP `schedule.graph` runs, and use it to extract the schedule data. - This metadata includes the edges for automatically inserted systems, making the resulting ScheduleData more accurate to the running schedule. - If the schedule needs to be built (has changed/hasn't run before), build the schedule which automatically caches its metadata. ## Testing - Added a test for this. |
||
|
|
9129cb1004 |
Replace the bespoke BRP schedule.graph implementation with ScheduleData. (#23733)
# Objective - A possible step towards #10981. - Followup to #22520. - We accidentally have two implementations of the same thing! We created a way to collect schedule data in `bevy_dev_tools` and in `bevy_remote`. `ScheduleData` is a more complete implementation of collecting schedule data, and is less tied to the internals of BRP (e.g., it supports serializing to disk instead of only through the BRP API), so we switch to that. ## Solution - Replace the implementation of `schedule.graph` with the `ScheduleData` API. A disadvantage is that we now need to wait for the schedules to be initialized before we can read them. Since users have to connect with BRP though, it is almost certain that the schedules will be initialized by the time they request the schedules. This may not be true of schedules like state transitions though - since these only run rarely. In a future PR, we can build the schedules on-demand instead. ## Testing - Updated the test for this. - Ran the same test as #23452 - Terminal 1: `cargo r --example server --features=bevy_remote` - Terminal 2: `curl -d'{"jsonrpc":"2.0","method":"schedule.graph","id":1,"params":{"schedule_label":"First"}}' -X POST -H "Accept: applcation/json" -H "Content-Type: application/json" http://127.0.0.1:15702` - It dumped out a whole load of JSON that looked expected! - I don't have a visualization of the schedule data yet, so I can't validate that it's correct, but there's no reason to believe its wrong given the existing `ScheduleData` tests. |
||
|
|
6a5ef7388f |
Change ResourceEntities from SparseSet to SparseArray to speed up resource lookups (#23616)
# Objective Reduce memory usage for resources, and maybe improve performance of resource lookups. Related to #23039, but not a solution. ## Solution Change `ResourceEntities` from a `SparseSet` to a `SparseArray`. `SparseArray` is `pub (crate)`, so simply exposing it through `Deref` would cause privacy errors. Instead, remove the `Deref` impl and add wrapper methods for `iter`, `get`, and `remove`. Change the return types from `&Entity` to `Entity` now that they aren't generic. As background: A `SparseArray` is a simple `Vec<Option<V>>`, while a `SparseSet` is a `SparseArray` that maps keys to dense indexes, combined with dense arrays of keys and values. That requires a second array operation to find the actual value, but can be much better for memory usage when the values are large, since missing items only take up space for a single index instead of an entire value. But the values in `ResourceEntities` are `Entity`, which are already small! A `SparseArray` will always be smaller on 64-bit systems, since an `Entity` is the same size as a `usize`, and we don't need to store the additional `dense` and `indices` arrays. So switching to `SparseArray` will save a lookup *and* save memory. One drawback is that we can no longer use the dense lists to iterate all resources, so methods like `iter_resources` now need to scan all component ids. I don't expect this to be a problem in practice, though. `iter_resources` is rarely used, and O(components) isn't all that much worse than O(resources). If it turns out to be an issue, it's also possible to recover this data by querying the `IsResource` component. ## Testing Inconclusive. I attempted to run benchmarks, both `bevymark` as in the linked issue and `cargo bench -p benches --bench ecs`, but the results were too noisy on my machine to reach any conclusions. And now that I look more closely, we don't have many benches that even use resources! --------- Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com> |
||
|
|
9f90ff38b9 |
fix(brp): allow params and id to be undefined when deserializing (#23661)
# Objective - Fixes #23643 - Some methods for BRP have `params` as an `Option`. `id` itself also seems to be an `Option`. If these are not provided in the request object, it should not panic on the deserialization level. Individual methods should handle it, and they do already when its necesssary. ## Solution - during deserialization, `params` and `id` should just flatten the nested `Option` instead of `unwrap` and assuming something is there ## Testing - Wrote a regression test - Tested executing a curl while the `server` example without params, and it works `curl -d'{"jsonrpc":"2.0","method":"world.list_components","id":1}' -X POST -H "Accept: applcation/json" -H "Content-Type: application/json" http://127.0.0.1:15702` - Tested `schedule.graph` to ensure that it panics without params provided on its own (it does) and that params are still read correctly when provided (it does) |
||
|
|
3c98d531d8 |
Add BRP to render app (#23446)
# Objective - BRP on the `RenderApp` ## Solution - Add type registry to `RenderApp` - Add BRP systems to render app - Add HTTP listener to extra port I suggest hiding whitespace for reviewing: <img width="422" height="360" alt="image" src="https://github.com/user-attachments/assets/3d76eafb-40f5-42f6-8752-c9d94f81d378" /> ## Related: - https://github.com/bevyengine/bevy/pull/23513 - https://github.com/bevyengine/bevy/pull/23447 - https://github.com/bevyengine/bevy/pull/23452 ## Testing - CI - in terminal 1 run `cargo run --example client --features="bevy_remote"` - in terminal 2 run `curl -d'{"jsonrpc":"2.0","method":"world.list_resources","id":1,"params":{}}' -X POST -H "Accept: applcation/json" -H "Content-Type: application/json" http://127.0.0.1:15702` - in terminal 2 run `curl -d'{"jsonrpc":"2.0","method":"world.list_resources","id":1,"params":{}}' -X POST -H "Accept: applcation/json" -H "Content-Type: application/json" http://127.0.0.1:15703` --------- Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com> |
||
|
|
56f3ef608b |
BRP Add schedule.graph endpoint (#23452)
# Objective - Describe the graph for a given schedule ## Solution - Add endpoint `schedule.graph` ## Related: - https://github.com/bevyengine/bevy/pull/23447 - https://github.com/bevyengine/bevy/pull/23446 ## Testing - in terminal 1 run `cargo run --example server --features="bevy_remote"` - in terminal 2 run `curl -d'{"jsonrpc":"2.0","method":"schedule.graph","id":1,"params":{"schedule_name":"First"}}' -X POST -H "Accept: applcation/json" -H "Content-Type: application/json" http://127.0.0.1:15702 ` <details><summary>Response</summary> <p> ```json { "jsonrpc": "2.0", "id": 1, "result": { "systemsets": [ { "key": "SystemSetKey(1v1)", "method": "SystemTypeSet:bevy_ecs::message::update::message_update_system" }, { "key": "SystemSetKey(2v1)", "method": "MessageUpdateSystems" }, { "key": "SystemSetKey(3v1)", "method": "SystemTypeSet:bevy_time::time_system" }, { "key": "SystemSetKey(4v1)", "method": "TimeSystems" }, { "key": "SystemSetKey(5v1)", "method": "SystemTypeSet:bevy_render::view::window::screenshot::clear_screenshots" }, { "key": "SystemSetKey(6v1)", "method": "SystemTypeSet:bevy_ecs::schedule::executor::ApplyDeferred" }, { "key": "SystemSetKey(7v1)", "method": "SystemTypeSet:bevy_ui::widget::viewport::viewport_picking" }, { "key": "SystemSetKey(8v1)", "method": "PostInput" }, { "key": "SystemSetKey(9v1)", "method": "SystemTypeSet:bevy_picking::input::mouse_pick_events" }, { "key": "SystemSetKey(10v1)", "method": "Input" }, { "key": "SystemSetKey(11v1)", "method": "SystemTypeSet:bevy_picking::input::touch_pick_events" } ], "hierarchy_nodes": [ "Set(SystemSetKey(11v1))", "Set(SystemSetKey(10v1))", "System(SystemKey(6v1))", "Set(SystemSetKey(9v1))", "System(SystemKey(5v1))", "Set(SystemSetKey(8v1))", "Set(SystemSetKey(7v1))", "System(SystemKey(4v1))", "Set(SystemSetKey(6v1))", "Set(SystemSetKey(5v1))", "System(SystemKey(3v1))", "Set(SystemSetKey(4v1))", "Set(SystemSetKey(3v1))", "System(SystemKey(2v1))", "Set(SystemSetKey(2v1))", "Set(SystemSetKey(1v1))", "System(SystemKey(1v1))" ], "hierarchy_edges": [ [ "Set(SystemSetKey(1v1))", "System(SystemKey(1v1))" ], [ "Set(SystemSetKey(3v1))", "System(SystemKey(2v1))" ], [ "Set(SystemSetKey(8v1))", "System(SystemKey(4v1))" ], [ "Set(SystemSetKey(2v1))", "System(SystemKey(1v1))" ], [ "Set(SystemSetKey(7v1))", "System(SystemKey(4v1))" ], [ "Set(SystemSetKey(4v1))", "System(SystemKey(2v1))" ], [ "Set(SystemSetKey(9v1))", "System(SystemKey(5v1))" ], [ "Set(SystemSetKey(10v1))", "System(SystemKey(6v1))" ], [ "Set(SystemSetKey(5v1))", "System(SystemKey(3v1))" ], [ "Set(SystemSetKey(10v1))", "System(SystemKey(5v1))" ], [ "Set(SystemSetKey(11v1))", "System(SystemKey(6v1))" ] ], "dependency_nodes": [ "System(SystemKey(1v1))", "Set(SystemSetKey(1v1))", "Set(SystemSetKey(2v1))", "System(SystemKey(2v1))", "Set(SystemSetKey(3v1))", "Set(SystemSetKey(4v1))", "System(SystemKey(3v1))", "Set(SystemSetKey(5v1))", "Set(SystemSetKey(6v1))", "System(SystemKey(4v1))", "Set(SystemSetKey(7v1))", "Set(SystemSetKey(8v1))", "System(SystemKey(5v1))", "Set(SystemSetKey(9v1))", "Set(SystemSetKey(10v1))", "System(SystemKey(6v1))", "Set(SystemSetKey(11v1))" ], "dependency_edges": [ [ "Set(SystemSetKey(2v1))", "Set(SystemSetKey(10v1))" ], [ "Set(SystemSetKey(10v1))", "Set(SystemSetKey(8v1))" ], [ "System(SystemKey(3v1))", "Set(SystemSetKey(6v1))" ], [ "System(SystemKey(5v1))", "System(SystemKey(6v1))" ], [ "Set(SystemSetKey(4v1))", "Set(SystemSetKey(8v1))" ], [ "Set(SystemSetKey(4v1))", "Set(SystemSetKey(10v1))" ], [ "Set(SystemSetKey(2v1))", "Set(SystemSetKey(8v1))" ], [ "Set(SystemSetKey(1v1))", "System(SystemKey(3v1))" ] ] } } ``` </p> </details> From the above response, the below graph can be generated. Hierarchy edges are blue, dependency edges are red. System nodes are implied by hierarchy from Set to System. <img width="1543" height="347" alt="first" src="https://github.com/user-attachments/assets/80ac7e80-2a28-4246-80cc-29337bad4383" /> <details><summary>GraphViz Source</summary> <p> ``` digraph { "Set(SystemSetKey(1v1))" [label="message_update_system"] "Set(SystemSetKey(2v1))" [label="MessageUpdateSystems"] "Set(SystemSetKey(3v1))" [label="time_system"] "Set(SystemSetKey(4v1))" [label="TimeSystems"] "Set(SystemSetKey(5v1))" [label="clear_screenshots"] "Set(SystemSetKey(6v1))" [label="ApplyDeferred"] "Set(SystemSetKey(7v1))" [label="viewport_picking"] "Set(SystemSetKey(8v1))" [label="PostInput"] "Set(SystemSetKey(9v1))" [label="mouse_pick_events"] "Set(SystemSetKey(10v1))" [label="Input"] "Set(SystemSetKey(11v1))" [label="touch_pick_events"] // implied nodes "System(SystemKey(1v1))" [label="implied message_update_system", shape=rectangle] "System(SystemKey(2v1))" [label="implied time_system", shape=rectangle] "System(SystemKey(3v1))" [label="implied clear_screenshots", shape=rectangle] "System(SystemKey(4v1))" [label="implied viewport_picking", shape=rectangle] "System(SystemKey(5v1))" [label="implied mouse_pick_events", shape=rectangle] "System(SystemKey(6v1))" [label="implied touch_pick_events", shape=rectangle] // hierarchy "Set(SystemSetKey(1v1))" -> "System(SystemKey(1v1))" [color=blue] "Set(SystemSetKey(3v1))" -> "System(SystemKey(2v1))" [color=blue] "Set(SystemSetKey(8v1))" -> "System(SystemKey(4v1))" [color=blue] "Set(SystemSetKey(2v1))" -> "System(SystemKey(1v1))" [color=blue] "Set(SystemSetKey(7v1))" -> "System(SystemKey(4v1))" [color=blue] "Set(SystemSetKey(4v1))" -> "System(SystemKey(2v1))" [color=blue] "Set(SystemSetKey(9v1))" -> "System(SystemKey(5v1))" [color=blue] "Set(SystemSetKey(10v1))" -> "System(SystemKey(6v1))" [color=blue] "Set(SystemSetKey(5v1))" -> "System(SystemKey(3v1))" [color=blue] "Set(SystemSetKey(10v1))" -> "System(SystemKey(5v1))" [color=blue] "Set(SystemSetKey(11v1))" -> "System(SystemKey(6v1))" [color=blue] // dependencies "Set(SystemSetKey(2v1))" -> "Set(SystemSetKey(10v1))" [color=red] "Set(SystemSetKey(10v1))" -> "Set(SystemSetKey(8v1))" [color=red] "System(SystemKey(3v1))" -> "Set(SystemSetKey(6v1))" [color=red] "System(SystemKey(5v1))" -> "System(SystemKey(6v1))" [color=red] "Set(SystemSetKey(4v1))" -> "Set(SystemSetKey(8v1))" [color=red] "Set(SystemSetKey(4v1))" -> "Set(SystemSetKey(10v1))" [color=red] "Set(SystemSetKey(2v1))" -> "Set(SystemSetKey(8v1))" [color=red] "Set(SystemSetKey(1v1))" -> "System(SystemKey(3v1))" [color=red] } ``` </p> </details> --------- Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
1a5fdf96db |
BRP Add schedule.list endpoint (#23447)
# Objective - List schedules in BRP ## Solution - Add the endpoint - Add to `Schedules` some tracking of `temporarily_removed` (schedule removed to execute) and `empty_labels` (no `Schedule` for that label) ## Related: - https://github.com/bevyengine/bevy/pull/23452 - https://github.com/bevyengine/bevy/pull/23446 ## Testing - in terminal 1 run `cargo run --example server --features="bevy_remote"` - in terminal 2 run `curl -d'{"jsonrpc":"2.0","method":"schedule.list","id":1,"params":{}}' -X POST -H "Accept: applcation/json" -H "Content-Type: application/json" http://127.0.0.1:15702` <details><summary>Response</summary> <p> ```json { "jsonrpc": "2.0", "id": 1, "result": { "empty_schedule_labels": [ "FixedPreUpdate", "FixedUpdate" ], "schedule_labels": [ "Startup", "First", "RunFixedMainLoop", "PostStartup", "FixedFirst", "StateTransition", "PreUpdate", "PostUpdate", "Update", "PreStartup", "SpawnScene", "Last", "FixedLast", "FixedPostUpdate", "FixedMain" ], "unavailable_schedule_labels": [ "Main", "RemoteLast" ] } } ``` </p> </details> --------- Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com> |
||
|
|
27fe7af0c7 |
Write Messages by Bevy Remote Protocol (#23391)
# Objective - Be able to write a message through BRP ## Solution - Add an endpoint to bevy_remote that creates a message from a payload and writes it to the world - Make `BrpTriggerEventParams` pub like all the other params for easier use ## Testing - New test in CI --------- Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com> |
||
|
|
0b0f65377b |
Address some nightly clippy lints (#23314)
# Objective Just some code cleanup opportunities that clippy detected. ## Solution - Apply the suggestions from clippy ## Testing Don't think I need to test this (other than compiling which I did) |
||
|
|
5f8435b89d |
BrpRequest: remove jsonrpc field (#23175)
# Objective `BrpRequest` includes a useless `jsonrpc` field that must always be set to "2.0". Remove it. This change could also be made to `BrpResponse`, but that would be much more involved due to the `serde(flatten)` while also offering less benefit (as users need to create this struct less often). I've not added a migration guide because no advice is needed to migrate. ## Testing Run the `server` + `client` example. --------- Co-authored-by: Mira Morgana <mira-morgana@posteo.net> Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com> |
||
|
|
55c4e977d0 |
Add component info to BRP registry schema (#23102)
# Objective Add component metadata to the BRP `registry.schema` method. The following data related to components is missing and this PR adds it: - Whether the component is mutable or not. - Type paths of required components. - Whether the component is a relationship or relationship target. Other metadata could easily be added, basically everything in the [`ComponentInfo`](https://docs.rs/bevy/latest/bevy/ecs/component/struct.ComponentInfo.html) type. ## Solution - Once per type, use `World::components` to obtain a potential `ComponentId` and `ComponentInfo`. - Use it to fill the new `component_info` field. - It doesn't introduce a breaking change, as the new field is optional and hidden for non-component types. ## Testing - I added a unit test. It verifies mutability, required components, and relationship kind for a few test components. - I also called BRP to retrieve a schema and verify the returned data is correct. --- ## Showcase Here are a few examples of the returned JSON schema when calling the BRP `registry.schema` method, with other fields omitted for brevity (they are unchanged): ```json { "bevy_ecs::hierarchy::ChildOf": { "componentInfo": { "isSendAndSync": true, "mutable": false, "relationshipKind": "Relationship", "storageType": "Table" } }, "bevy_ecs::hierarchy::Children": { "componentInfo": { "isSendAndSync": true, "mutable": true, "relationshipKind": "RelationshipTarget", "storageType": "Table" } }, "bevy_transform::components::transform::Transform": { "componentInfo": { "isSendAndSync": true, "mutable": true, "requiredComponentTypes": [ "bevy_transform::components::global_transform::GlobalTransform", "bevy_transform::components::transform::TransformTreeChanged" ], "storageType": "Table" } }, "bevy_render::sync_world::SyncToRenderWorld": { "componentInfo": { "isSendAndSync": true, "mutable": true, "storageType": "SparseSet" } } } ``` |
||
|
|
b9eb475f8e |
Tiny bevy_remote doc format fix (#23176)
Co-authored-by: Mira Morgana <mira-morgana@posteo.net> |
||
|
|
f4bb750f34 |
Remove component/resource redundancies (#22930)
Part of #19731 Follow-up to #22919 and #20934 This consolidates some of our component registration internals and removes some resource / component redundancies there. |
||
|
|
59e9ee3a1a |
Store Resources as components on singleton entities (#20934)
This is part of #19731. # Resources as Components ## Motivation More things should be entities. This simplifies the API, the lower-level implementation and the tools we have for entities and components can be used for other things in the engine. In particular, for resources, it is really handy to have observers, which we currently don't have. See #20821 under 1A, for a more specific use. ## Current Work This removes the `resources` field from the world storage and instead store the resources on singleton entities. For easy lookup, we add a `HashMap<ComponentId, Entity>` to `World`, in order to quickly find the singleton entity where the resource is stored. Because we store resources on entities, we derive `Component` alongside `Resource`, this means that ```rust #[derive(Resource)] struct Foo; ``` turns into ```rust #[derive(Resource, Component)] struct Foo; ``` This was also done for reflections, meaning that ```rust #[derive(Resource, Reflect)] #[refect(Resource)] struct Bar; ``` becomes ```rust #[derive(Resource, Component, Reflect)] #[refect(Resource, Component)] struct Bar; ``` In order to distinguish resource entities, they are tagged with the `IsResource` component. Additionally, to ensure that they aren't queried by accident, they are also tagged as being internal entities, which means that they don't show up in queries by default. ## Drawbacks - Currently you can't have a struct that is both a `Resource` and a `Component`, because `Resource` expands to also implement `Component`, this means that this throws a compiler error as it's implemented twice. - Because every reflected Resource must also implement `ReflectComponent` you need to import `bevy_ecs::reflect::ReflectComponent` every time you use `#[reflect(Resource)]`. This is kind of unintuitive. ## Future Work - Simplify `Access` in the ECS, to only deal with components (and not components *and* resources). - Newtype `Res<Resource>` to `Single<Ref<Resource>>` (or something similair). - Eliminate `ReflectResource`. - Take stabs at simplifying the public facing API. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Dimitrios Loukadakis <dloukadakis@users.noreply.github.com> |
||
|
|
3ae32d500b |
move transmission stuff to bevy_pbr (#22687)
# Objective - It makes no sense for bevy_camera::Camera3d to talk about transmission quality settings - transmission is not a core thing its a purely pbr thing, why is in bevy_core_pipelines - the implementation is generally scattered all over the place - ScreenSpaceTransmissionQuality is a resource for no reason at all ## Solution - split out a struct for transmission stuff - consolidate stuff in bevy_pbr - make ScreenSpaceTransmissionQuality not a resource ## Testing transmission example looks good |
||
|
|
7b03032783 |
Reorganize some of bevy_reflect's exports into their respective modules (#22342)
# Objective - #22321 ## Solution Do it ## Testing `cargo clippy` and CI. --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
|
|
53791c227c |
Update BRP method names in code examples (#22387)
Fixes #22385
I searched for all the old methods listed in commit
|
||
|
|
6ee5067857 |
Make BRP builtins utilities parse and parse_some public (#22005)
# Objective Builtin BRP methods make constant use of `parse` and `parse_some` in parsing utilities. Making them public allows users to use them in their own code. ## Solution Make both functions `pub`. ## Testing I think testing is not necessary, especially since doc strings do not link anywhere. |
||
|
|
16409b8a02 |
Fix lints after Rust 1.92 (#22092)
# Objective CI is currently failing ## Solution Fix the lints (and work around all the rustc lint bugs that are apparently included) --------- Co-authored-by: MichiRecRoom <1008889+LikeLakers2@users.noreply.github.com> |
||
|
|
0af6fc1c74 |
Implement world.trigger_event remote method (#21798)
# Objective
Tools using `bevy_remote` will be able to identify (via the schema) and
trigger events.
- [x] Document `bevy_ecs/src/reflect/event.rs`
## Solution
I've added a method `world.trigger_event`, added `Event` to the schema's
reflection metadata and `ReflectEvent` to allow this.
## Testing
I have copied the (tested) code from my game but have NOT tested this
branch yet.
I am new to Rust/Cargo and need to go to sleep now, I'll figure this out
and test it tomorrow.
---
## Showcase
Here's what I needed to add to my game in order to allow my editor to
access and trigger an event:
```rust
#[derive(Event, Reflect)]
#[reflect(Event)]
pub struct AssignToRoute {
pub vehicle: Entity,
pub route: Entity,
pub origin: Entity,
}
```
Here's a screenshot of my editor using this feature:
<img width="1463" height="967" alt="Screenshot 2025-11-10 at 1 40 42 AM"
src="https://github.com/user-attachments/assets/7696ddb8-3552-4e0d-a78f-6178c0c66cbb"
/>
|
||
|
|
279a8678d4 |
Improved Entity Lifecycle: remove flushing, support manual spawning and despawning (#19451)
# Objective This is the next step for #19430 and is also convinient for #18670. For context, the way entities work on main is as a "allocate and use" system. Entity ids are allocated, and given a location. The location can then be changed, etc. Entities that are free have an invalid location. To allocate an entity, one must also set its location. This introduced the need for pending entities, where an entity would be reserved, pending, and at some point flushed. Pending and free entities have an invalid location, and others are assumed to have a valid one. This paradigm has a number of downsides: First, the entities metadata table is inseparable from the allocator, which makes remote reservation challenging. Second, the `World` must be flushed, even to do simple things, like allocate a temporary entity id. Third, users have little control over entity ids, only interacting with conceptual entities. This made things like `Entities::alloc_at` clunky and slow, leading to its removal, despite some users still having valid need of it. So the goal of this PR is to: - Decouple `Entities` from entity allocation to make room for other allocators and resolve `alloc_at` issues. - Decouple entity allocation from spawning to make reservation a moot point. - Introduce constructing and destructing entities, in addition to spawn/despawn. - Change `reserve` and `flush` patterns to `alloc` and `construct` patterns. It is possible to break this up into multiple prs, as I originally intended, but doing so would require lots of temporary scaffolding that would both hurt performance and make things harder to review. ## Solution This solution builds on #19433, which changed the representation of invalid entity locations from a constant to `None`. There's quite a few steps to this, each somewhat controversial: ### Entities with no location This pr introduces the idea of entity rows both with and without locations. This corresponds to entities that are constructed (the row has a location) and not constructed (the row has no location). When a row is free or pending, it is not constructed. When a row is outside the range of the meta list, it still exists; it's just not constructed. This extends to conceptual entities; conceptual entities may now be in one of 3 states: empty (constructed; no components), normal (constructed; 1 or more components), or null (not constructed). This extends to entity pointers (`EntityWorldMut`, etc): These now can point to "null"/not constructed entities. Depending on the privilege of the pointer, these can also construct or destruct the entity. This also changes how `Entity` ids relate to conceptual entities. An `Entity` now exists if its generation matches that of its row. An `Entity` that has the right generation for its row will claim to exist, even if it is not constructed. This means, for example, an `Entity` manually constructed with a large index and generation of 0 *will* exist if it has not been allocated yet. ### `Entities` is separate from the allocator This pr separates entity allocation from `Entities`. `Entities` is now only focused on tracking entity metadata, etc. The new `EntitiesAllocator` on `World` manages all allocations. This forces `Entities` to not rely on allocator state to determine if entities exist, etc, which is convinient for remote reservation and needed for custom allocators. It also paves the way for allocators not housed within the `World`, makes some unsafe code easier since the allocator and metadata live under different pointers, etc. This separation requires thinking about interactions with `Entities` in a new way. Previously, the `Entities` set the rules for what entities are valid and what entities are not. Now, it has no way of knowing. Instead, interaction with `Entities` are more like declaring some information for it to track than changing some information it was already tracking. To reflect this, `set` has been split up into `declare` and `update`. ### Constructing and destructing As mentioned, entities that have no location (not constructed) can be constructed at any time. This takes on exactly the same meaning as the previous `spawn_non_existent`. It creates/declares a location instead of updating an old one. As an example, this makes spawning an entity now literately just allocate a new id and construct it immediately. Conversely, entities that are constructed may be destructed. This removes all components and despawns related entities, just like `despawn`. The only difference is that destructing does not free the entity id for reuse. Between constructing and destructing, all needs for `alloc_at` are resolved. If you want to keep the id for custom reuse, just destruct instead of despawn! Despawn, now just destructs the entity and frees it. Destructing a not constructed entity will do nothing. Constructing an already constructed entity will panic. This is to guard against users constructing a manually formed `Entity` that the allocator could later hand out. However, public construction methods have proper error handling for this. Despawning a not constructed entity just frees its id. ### No more flushing All places that once needed to reserve and flush entity ids now allocate and construct them instead. This improves performance and simplifies things. ### Flow chart  (Thanks @ItsDoot) ## Testing - CI - Some new tests - A few deleted (no longer applicable) tests - If you see something you think should have a test case, I'll gladly add it. ## Showcase Here's an example of constructing and destructing ```rust let e4 = world.spawn_null(); world .entity_mut(e4) .construct((TableStored("junk"), A(0))) .unwrap() .destruct() .construct((TableStored("def"), A(456))) .unwrap(); ``` ## Future Work - [x] More expansive docs. This should definitely should be done, but I'd rather do that in a future pr to separate writing review from code review. If you have more ideas for how to introduce users to these concepts, I'd like to see them. As it is, we don't do a very good job of explaining entities to users. Ex: `Entity` doesn't always correspond to a conceptual entity. - [ ] Try to remove panics from `EntityWorldMut`. There is (and was) a lot of assuming the entity is constructed there (was assuming it was not despawned). - [ ] A lot of names are still centered around spawn/despawn, which is more user-friendly than construct/destruct but less precise. Might be worth changing these over. - [ ] Making a centralized bundle despawner would make sense now. - [ ] Of course, build on this for remote reservation and, potentially, for paged entities. ## Performance <details> <summary>Benchmarks</summary> ```txt critcmp main pr19451 -t 1 group main pr19451 ----- ---- ------- add_remove/sparse_set 1.13 594.7±6.80µs ? ?/sec 1.00 527.4±8.01µs ? ?/sec add_remove/table 1.08 799.6±15.53µs ? ?/sec 1.00 739.7±15.10µs ? ?/sec add_remove_big/sparse_set 1.10 614.6±6.50µs ? ?/sec 1.00 557.0±19.04µs ? ?/sec add_remove_big/table 1.03 2.8±0.01ms ? ?/sec 1.00 2.7±0.02ms ? ?/sec added_archetypes/archetype_count/100 1.01 30.9±0.50µs ? ?/sec 1.00 30.5±0.44µs ? ?/sec added_archetypes/archetype_count/1000 1.00 638.0±19.77µs ? ?/sec 1.03 657.0±73.61µs ? ?/sec added_archetypes/archetype_count/10000 1.02 5.5±0.14ms ? ?/sec 1.00 5.4±0.09ms ? ?/sec all_added_detection/50000_entities_ecs::change_detection::Sparse 1.02 47.9±1.22µs ? ?/sec 1.00 46.8±0.40µs ? ?/sec all_added_detection/50000_entities_ecs::change_detection::Table 1.02 45.4±1.89µs ? ?/sec 1.00 44.6±0.78µs ? ?/sec build_schedule/1000_schedule 1.02 942.6±11.53ms ? ?/sec 1.00 925.2±10.35ms ? ?/sec build_schedule/100_schedule 1.01 5.8±0.12ms ? ?/sec 1.00 5.7±0.12ms ? ?/sec build_schedule/100_schedule_no_constraints 1.03 803.1±28.93µs ? ?/sec 1.00 781.1±50.11µs ? ?/sec build_schedule/500_schedule_no_constraints 1.00 5.6±0.31ms ? ?/sec 1.08 6.0±0.27ms ? ?/sec busy_systems/01x_entities_03_systems 1.00 24.4±1.35µs ? ?/sec 1.01 24.7±1.35µs ? ?/sec busy_systems/03x_entities_03_systems 1.00 38.1±1.70µs ? ?/sec 1.04 39.7±1.49µs ? ?/sec busy_systems/03x_entities_09_systems 1.01 111.4±2.27µs ? ?/sec 1.00 109.9±2.46µs ? ?/sec busy_systems/03x_entities_15_systems 1.00 174.8±2.56µs ? ?/sec 1.01 176.6±4.22µs ? ?/sec contrived/03x_entities_09_systems 1.00 59.0±2.92µs ? ?/sec 1.01 59.8±3.03µs ? ?/sec contrived/03x_entities_15_systems 1.00 97.5±4.87µs ? ?/sec 1.01 98.8±4.69µs ? ?/sec contrived/05x_entities_09_systems 1.00 75.3±3.76µs ? ?/sec 1.01 76.4±4.11µs ? ?/sec despawn_world/10000_entities 1.32 344.8±4.47µs ? ?/sec 1.00 261.4±4.91µs ? ?/sec despawn_world/100_entities 1.22 4.3±0.04µs ? ?/sec 1.00 3.5±0.54µs ? ?/sec despawn_world/1_entities 1.01 169.6±7.88ns ? ?/sec 1.00 167.8±11.45ns ? ?/sec despawn_world_recursive/10000_entities 1.20 1723.0±53.82µs ? ?/sec 1.00 1437.0±26.11µs ? ?/sec despawn_world_recursive/100_entities 1.16 17.9±0.10µs ? ?/sec 1.00 15.5±0.16µs ? ?/sec despawn_world_recursive/1_entities 1.01 372.8±15.68ns ? ?/sec 1.00 367.7±16.90ns ? ?/sec ecs::entity_cloning::hierarchy_many/clone 1.03 227.9±24.67µs 1559.9 KElem/sec 1.00 221.1±29.74µs 1607.8 KElem/sec ecs::entity_cloning::hierarchy_many/reflect 1.00 406.2±23.46µs 875.2 KElem/sec 1.02 413.9±22.45µs 858.9 KElem/sec ecs::entity_cloning::hierarchy_tall/clone 1.01 12.2±0.34µs 4.0 MElem/sec 1.00 12.0±1.41µs 4.1 MElem/sec ecs::entity_cloning::hierarchy_tall/reflect 1.02 15.3±0.39µs 3.2 MElem/sec 1.00 15.0±2.14µs 3.2 MElem/sec ecs::entity_cloning::single/clone 1.02 659.0±100.01ns 1481.8 KElem/sec 1.00 643.3±101.49ns 1517.9 KElem/sec ecs::entity_cloning::single/reflect 1.03 1135.2±72.17ns 860.2 KElem/sec 1.00 1098.3±65.99ns 889.1 KElem/sec empty_archetypes/for_each/10 1.02 8.1±0.57µs ? ?/sec 1.00 8.0±0.37µs ? ?/sec empty_archetypes/for_each/100 1.01 8.1±0.34µs ? ?/sec 1.00 8.1±0.28µs ? ?/sec empty_archetypes/for_each/1000 1.03 8.4±0.25µs ? ?/sec 1.00 8.2±0.29µs ? ?/sec empty_archetypes/iter/100 1.01 8.1±0.29µs ? ?/sec 1.00 8.0±0.34µs ? ?/sec empty_archetypes/iter/1000 1.02 8.5±0.31µs ? ?/sec 1.00 8.4±0.62µs ? ?/sec empty_archetypes/iter/10000 1.01 10.6±1.22µs ? ?/sec 1.00 10.5±0.49µs ? ?/sec empty_archetypes/par_for_each/10 1.01 8.8±0.49µs ? ?/sec 1.00 8.7±0.31µs ? ?/sec empty_archetypes/par_for_each/100 1.00 8.7±0.48µs ? ?/sec 1.04 9.0±0.34µs ? ?/sec empty_archetypes/par_for_each/10000 1.01 21.2±0.41µs ? ?/sec 1.00 20.9±0.44µs ? ?/sec empty_commands/0_entities 1.72 3.7±0.01ns ? ?/sec 1.00 2.1±0.02ns ? ?/sec empty_systems/100_systems 1.00 82.9±3.29µs ? ?/sec 1.07 88.3±3.77µs ? ?/sec empty_systems/2_systems 1.01 8.2±0.71µs ? ?/sec 1.00 8.2±0.38µs ? ?/sec empty_systems/4_systems 1.00 8.2±0.72µs ? ?/sec 1.03 8.4±0.71µs ? ?/sec entity_hash/entity_set_build/10000 1.10 45.9±1.60µs 207.7 MElem/sec 1.00 41.6±0.39µs 229.0 MElem/sec entity_hash/entity_set_build/3162 1.06 12.7±0.77µs 236.7 MElem/sec 1.00 12.0±0.75µs 250.6 MElem/sec entity_hash/entity_set_lookup_hit/10000 1.02 14.5±0.30µs 658.3 MElem/sec 1.00 14.2±0.07µs 672.6 MElem/sec entity_hash/entity_set_lookup_hit/3162 1.01 4.4±0.03µs 682.7 MElem/sec 1.00 4.4±0.01µs 691.3 MElem/sec entity_hash/entity_set_lookup_miss_gen/10000 1.01 61.3±4.12µs 155.6 MElem/sec 1.00 60.6±1.47µs 157.3 MElem/sec entity_hash/entity_set_lookup_miss_gen/3162 1.00 9.5±0.02µs 316.3 MElem/sec 1.01 9.7±0.88µs 311.7 MElem/sec entity_hash/entity_set_lookup_miss_id/100 1.00 145.5±1.49ns 655.4 MElem/sec 1.03 149.8±1.59ns 636.7 MElem/sec entity_hash/entity_set_lookup_miss_id/10000 1.85 63.9±3.57µs 149.3 MElem/sec 1.00 34.6±3.81µs 275.8 MElem/sec entity_hash/entity_set_lookup_miss_id/316 1.00 562.0±9.58ns 536.2 MElem/sec 1.02 573.9±1.27ns 525.1 MElem/sec entity_hash/entity_set_lookup_miss_id/3162 1.03 9.1±0.10µs 330.7 MElem/sec 1.00 8.9±0.24µs 339.0 MElem/sec event_propagation/four_event_types 1.12 541.5±3.84µs ? ?/sec 1.00 482.7±4.64µs ? ?/sec event_propagation/single_event_type 1.07 769.5±10.21µs ? ?/sec 1.00 715.9±15.16µs ? ?/sec event_propagation/single_event_type_no_listeners 1.56 393.4±2.89µs ? ?/sec 1.00 251.4±3.68µs ? ?/sec events_iter/size_16_events_100 1.01 64.0±0.18ns ? ?/sec 1.00 63.4±0.23ns ? ?/sec events_iter/size_4_events_100 1.02 64.8±0.90ns ? ?/sec 1.00 63.4±0.24ns ? ?/sec events_iter/size_4_events_1000 1.01 586.5±8.00ns ? ?/sec 1.00 579.1±4.93ns ? ?/sec events_send/size_16_events_100 1.00 142.7±24.34ns ? ?/sec 1.03 147.1±28.36ns ? ?/sec events_send/size_16_events_10000 1.01 12.2±0.13µs ? ?/sec 1.00 12.1±0.12µs ? ?/sec fake_commands/10000_commands 1.43 63.3±8.21µs ? ?/sec 1.00 44.1±0.16µs ? ?/sec fake_commands/1000_commands 1.40 6.2±0.01µs ? ?/sec 1.00 4.4±0.02µs ? ?/sec fake_commands/100_commands 1.38 629.4±1.69ns ? ?/sec 1.00 457.1±0.84ns ? ?/sec few_changed_detection/50000_entities_ecs::change_detection::Table 1.00 57.7±0.86µs ? ?/sec 1.07 61.6±1.19µs ? ?/sec few_changed_detection/5000_entities_ecs::change_detection::Sparse 1.05 5.4±0.53µs ? ?/sec 1.00 5.1±0.56µs ? ?/sec few_changed_detection/5000_entities_ecs::change_detection::Table 1.00 4.3±0.30µs ? ?/sec 1.18 5.1±0.35µs ? ?/sec insert_commands/insert 1.11 402.5±10.75µs ? ?/sec 1.00 363.6±8.07µs ? ?/sec insert_commands/insert_batch 1.00 174.9±3.03µs ? ?/sec 1.02 177.9±5.74µs ? ?/sec insert_simple/base 1.04 564.1±23.01µs ? ?/sec 1.00 544.3±60.70µs ? ?/sec insert_simple/unbatched 1.32 929.3±180.10µs ? ?/sec 1.00 704.1±132.88µs ? ?/sec iter_fragmented/base 1.02 280.0±2.86ns ? ?/sec 1.00 274.0±4.85ns ? ?/sec iter_fragmented/foreach 1.00 97.3±0.42ns ? ?/sec 1.03 100.6±3.44ns ? ?/sec iter_fragmented/foreach_wide 1.04 2.7±0.04µs ? ?/sec 1.00 2.6±0.03µs ? ?/sec iter_fragmented_sparse/base 1.00 5.6±0.05ns ? ?/sec 1.04 5.8±0.06ns ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_10000_entities_ecs::change_detection::Sparse 1.00 737.7±27.38µs ? ?/sec 1.01 747.5±30.01µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_10000_entities_ecs::change_detection::Table 1.02 678.3±25.13µs ? ?/sec 1.00 662.1±19.63µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_1000_entities_ecs::change_detection::Sparse 1.09 76.0±9.35µs ? ?/sec 1.00 70.0±3.29µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_1000_entities_ecs::change_detection::Table 1.03 64.7±3.40µs ? ?/sec 1.00 62.8±1.80µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_100_entities_ecs::change_detection::Table 1.02 7.6±0.12µs ? ?/sec 1.00 7.5±0.16µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_10_entities_ecs::change_detection::Sparse 1.00 1003.5±12.38ns ? ?/sec 1.01 1013.7±32.64ns ? ?/sec multiple_archetypes_none_changed_detection/20_archetypes_10_entities_ecs::change_detection::Sparse 1.03 187.1±21.18ns ? ?/sec 1.00 181.9±22.86ns ? ?/sec multiple_archetypes_none_changed_detection/5_archetypes_10_entities_ecs::change_detection::Sparse 1.00 52.8±8.19ns ? ?/sec 1.03 54.3±8.06ns ? ?/sec multiple_archetypes_none_changed_detection/5_archetypes_10_entities_ecs::change_detection::Table 1.00 46.8±2.23ns ? ?/sec 1.03 48.0±2.48ns ? ?/sec no_archetypes/system_count/0 1.00 16.3±0.17ns ? ?/sec 1.02 16.6±0.16ns ? ?/sec no_archetypes/system_count/100 1.02 851.5±9.32ns ? ?/sec 1.00 832.9±7.93ns ? ?/sec none_changed_detection/5000_entities_ecs::change_detection::Sparse 1.00 3.4±0.04µs ? ?/sec 1.02 3.5±0.05µs ? ?/sec nonempty_spawn_commands/10000_entities 1.89 261.1±6.99µs ? ?/sec 1.00 137.8±8.47µs ? ?/sec nonempty_spawn_commands/1000_entities 1.90 26.4±3.18µs ? ?/sec 1.00 13.9±2.38µs ? ?/sec nonempty_spawn_commands/100_entities 1.87 2.6±0.07µs ? ?/sec 1.00 1388.8±97.31ns ? ?/sec observe/trigger_simple 1.09 347.5±1.51µs ? ?/sec 1.00 317.7±2.62µs ? ?/sec observe/trigger_targets_simple/10000_entity 1.04 696.5±15.50µs ? ?/sec 1.00 672.0±13.88µs ? ?/sec par_iter_simple/with_0_fragment 1.01 34.4±0.51µs ? ?/sec 1.00 33.9±0.53µs ? ?/sec par_iter_simple/with_1000_fragment 1.04 45.5±0.93µs ? ?/sec 1.00 43.9±1.85µs ? ?/sec par_iter_simple/with_100_fragment 1.03 36.2±0.50µs ? ?/sec 1.00 35.1±0.44µs ? ?/sec par_iter_simple/with_10_fragment 1.03 37.5±0.97µs ? ?/sec 1.00 36.5±0.74µs ? ?/sec param/combinator_system/8_dyn_params_system 1.00 10.4±0.73µs ? ?/sec 1.01 10.5±0.79µs ? ?/sec param/combinator_system/8_piped_systems 1.05 8.0±0.65µs ? ?/sec 1.00 7.6±0.57µs ? ?/sec query_get/50000_entities_sparse 1.06 136.7±0.35µs ? ?/sec 1.00 128.6±0.44µs ? ?/sec query_get_many_10/50000_calls_sparse 1.02 1649.4±77.80µs ? ?/sec 1.00 1614.4±78.91µs ? ?/sec query_get_many_2/50000_calls_sparse 1.00 191.3±3.66µs ? ?/sec 1.01 193.3±0.75µs ? ?/sec query_get_many_2/50000_calls_table 1.00 243.9±0.55µs ? ?/sec 1.05 257.2±8.62µs ? ?/sec query_get_many_5/50000_calls_sparse 1.00 585.9±7.70µs ? ?/sec 1.03 600.6±5.99µs ? ?/sec query_get_many_5/50000_calls_table 1.00 673.7±7.44µs ? ?/sec 1.07 722.3±10.77µs ? ?/sec run_condition/no/1000_systems 1.00 23.7±0.06µs ? ?/sec 1.06 25.1±0.07µs ? ?/sec run_condition/no/100_systems 1.00 1460.5±4.28ns ? ?/sec 1.03 1510.1±3.69ns ? ?/sec run_condition/no/10_systems 1.00 201.5±0.53ns ? ?/sec 1.04 209.1±2.37ns ? ?/sec run_condition/yes/1000_systems 1.00 1225.7±22.58µs ? ?/sec 1.02 1253.7±24.90µs ? ?/sec run_condition/yes/100_systems 1.02 89.4±3.43µs ? ?/sec 1.00 88.0±3.96µs ? ?/sec run_condition/yes_using_query/1000_systems 1.00 1288.3±26.57µs ? ?/sec 1.03 1323.0±24.73µs ? ?/sec run_condition/yes_using_query/100_systems 1.00 108.8±2.51µs ? ?/sec 1.03 112.3±3.09µs ? ?/sec run_condition/yes_using_resource/100_systems 1.03 99.0±3.37µs ? ?/sec 1.00 96.2±4.80µs ? ?/sec run_empty_schedule/MultiThreaded 1.03 15.3±0.10ns ? ?/sec 1.00 14.9±0.03ns ? ?/sec run_empty_schedule/Simple 1.01 15.2±0.15ns ? ?/sec 1.00 15.0±0.25ns ? ?/sec sized_commands_0_bytes/10000_commands 1.57 52.6±0.41µs ? ?/sec 1.00 33.5±0.10µs ? ?/sec sized_commands_0_bytes/1000_commands 1.57 5.3±0.01µs ? ?/sec 1.00 3.4±0.00µs ? ?/sec sized_commands_0_bytes/100_commands 1.56 536.5±4.83ns ? ?/sec 1.00 343.6±1.12ns ? ?/sec sized_commands_12_bytes/10000_commands 1.22 63.0±0.53µs ? ?/sec 1.00 51.5±6.06µs ? ?/sec sized_commands_12_bytes/1000_commands 1.25 5.7±0.01µs ? ?/sec 1.00 4.6±0.05µs ? ?/sec sized_commands_12_bytes/100_commands 1.27 579.3±1.28ns ? ?/sec 1.00 455.4±0.85ns ? ?/sec sized_commands_512_bytes/10000_commands 1.11 248.4±85.81µs ? ?/sec 1.00 224.3±52.11µs ? ?/sec sized_commands_512_bytes/1000_commands 1.09 22.8±0.18µs ? ?/sec 1.00 21.0±0.17µs ? ?/sec sized_commands_512_bytes/100_commands 1.13 1852.2±11.21ns ? ?/sec 1.00 1635.3±4.91ns ? ?/sec spawn_commands/10000_entities 1.04 844.2±11.96µs ? ?/sec 1.00 811.5±13.25µs ? ?/sec spawn_commands/1000_entities 1.05 84.9±3.66µs ? ?/sec 1.00 80.5±4.13µs ? ?/sec spawn_commands/100_entities 1.06 8.6±0.12µs ? ?/sec 1.00 8.1±0.12µs ? ?/sec spawn_world/10000_entities 1.03 413.2±25.20µs ? ?/sec 1.00 400.9±49.97µs ? ?/sec spawn_world/100_entities 1.02 4.1±0.62µs ? ?/sec 1.00 4.1±0.69µs ? ?/sec spawn_world/1_entities 1.04 42.2±3.23ns ? ?/sec 1.00 40.6±6.81ns ? ?/sec world_entity/50000_entities 1.18 88.3±0.42µs ? ?/sec 1.00 74.7±0.16µs ? ?/sec world_get/50000_entities_sparse 1.02 182.2±0.32µs ? ?/sec 1.00 179.5±0.84µs ? ?/sec world_get/50000_entities_table 1.01 198.3±0.46µs ? ?/sec 1.00 196.2±0.63µs ? ?/sec world_query_for_each/50000_entities_sparse 1.00 32.7±0.12µs ? ?/sec 1.01 33.1±0.46µs ? ?/sec ``` </details> This roughly doubles command spawning speed! Despawning also sees a 20-30% improvement. Dummy commands improve by 10-50% (due to not needing an entity flush). Other benchmarks seem to be noise and are negligible. It looks to me like a massive performance win! --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com> Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com> Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> Co-authored-by: Trashtalk217 <24552941+Trashtalk217@users.noreply.github.com> Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
2219069c72 |
remove deprecated APIs in 0.17 (#21723)
# Objective - removes all APIs that were marked as deprecated in 0.17, |
||
|
|
5f9b085cbb |
BRP allow spawning/inserting Components with only Reflect (#20981)
# Objective Fixes #20952 ## Solution Improve flow for getting type path. Add new test to make sure it will work later on. ## Testing New test is added to confirm that the solution works, I wrote the test first to check if I have the same issue as described in report. It was that and the fix was provided by @natepiano so my work here is mostly test. |
||
|
|
8a2d9c1231 |
Document missing brp methods (#21007)
# Objective Document missing brp methods: - `registry.schema` - `rpc.discover` ## Solution - Update crate-level documentation ## Testing - `cargo doc -p bevy_remote` |
||
|
|
4d74baf1ae |
BufferedEvent -> Message Rename (#20953)
This renames the concept of `BufferedEvent` to `Message`, and updates our APIs, comments, and documentation to refer to these types as "messages" instead of "events". It also removes/updates anything that considers messages to be "observable", "listenable", or "triggerable". This is a followup to https://github.com/bevyengine/bevy/pull/20731, which omitted the `BufferedEvent -> Message` rename for brevity. See that post for rationale. |
||
|
|
eda118d033 |
Event Rearchitecture (#20731)
There is general consensus that our terminology for Events, "entity events", Observers, and BufferedEvents needs clarity. Additionally, many of us also agree that the current Observer system would benefit from additional static-ness: currently it is assumed that you can use events in pretty much any context, and they all go through the exact same code path. Alice put forth a proposal to [Overhaul Observers](https://hackmd.io/@bevy/rk4S92hmlg), and we have already partially implemented it for 0.17. I think it does a great job of outlining many of the issues at play, and it solves them reasonably well. But I _also_ think the proposed solution isn't yet ideal. Given that it is already partially implemented for 0.17, it is a breaking change, _and_ given that we have already broken the Observer API a number of times, I think we need to sort this out before the next release. This is a big changeset, but it is _largely_ just a reframing of what is already there. I haven't fundamentally changed the behaviors. I've just refined and constrained in a way that allows us to do what we are currently doing in a clearer, simpler, and more performant way. First, I'll give some quick notes on Alice's proposal (which you all should read if you haven't yet!): ### Notes on Alice's Proposal - I like the move toward a more static API - I think we've gone too far down the "separate terminology" path. The proposal introduces a zoo of apis, terms, and "subterms". I think we need to simplify our concepts and names to make this all easier to talk about and use in practice. - BroadcastEvent feels like the wrong name. EntityEvent is also "broadcast" in the exact same way - BufferedEvent is a completely different system than EntityEvent and BroadcastEvent. This muddles concepts too much. It needs its own standalone, single-word concept name. - "Universal observers": I think this should be fully context driven, rather than needing encoding in the API. - I agree we can't get rid of buffered events, and that merging them with "broadcast events" isn't helpful - I'm not quite sure how we'd make the proposed PropagateEvent subtrait work transparently. This can't be "layered on top" as a trait. It needs to be baked in at more fundamental level. * I don't like `app.add_broadcast_observers()`, `app.add_universal_observers()`, `Observer::entity_observer`, `Observer::broadcast`, etc. The `On` event should statically determine whether an observer is an "entity observer" or a "broadcast" Observer. This would already be encoded in the type system and is therefore something we can do on the developer's behalf. Likewise, any observer being registered at a top level is inherently _not_ a specific entity observer. All of these variants serve to make users guess and poke around in a way that is unnecessary. I want simple one word concept names, single constructors, etc. ### Proposed Principals - Static-ness: - Events should only be usable in the context they were defined to be used. - When triggered, Observers should *only* have access to fields and behaviors that are relevant: - Dont return Option or PLACEHOLDER: the field or function shouldn't exist - Entity events that don't support propagation shouldn't expose that functionality - Don't do unnecessary work at runtime - Event triggers shouldn't branch through every potential event code path - Don't clone potentially large lists of event context unnecessarily (Ex: we currently clone the component list for every observer invocation) - Minimize codegen - Don't recompile things redundantly. - Don't compile unnecessary code paths. - Clear and Simple - Minimize the number of concept names floating around, and lock each concept down heavily to a specific context - I'm convinced at this point that "buffered events" and "observer events" sharing concept names is wrong. We need two clean and clear terms, and I'm willing to give "buffered events" a slightly worse name if it means "observer events" can be nicer. - Don't throw the concept name "Event" out ... it is a very good name. Instead, constrain it to one specific thing. - Minimize our API surface - Events contain all context, including what used to be the "target". This lets people define the "target" name that makes the most sense for the context, and lets the documentation fully describe the context of that "target". ### Concepts - **Event** (the thing you "observe") - Rationale: "Event" is the clear choice for this concept. An "event" feels like something that happens in real time. "Event observers" are things that observe events when they occur (are triggered). Additionally, this is the concept that "propagates", and "event propagation" is a term people understand. - **Trigger**: (the verb that "causes" events to happen for targets). Events are Triggered. This can include additional context/ data that is passed to observers / informs the trigger behavior. Events have _exactly_ one Trigger. If you want a different trigger behavior, define a new event. This makes the system more static, more predictable, and easier to understand and document. `world.trigger_ref_with` makes it possible to pass in mutable reference to your own Trigger data, making it possible to customize the input trigger data and read out the final trigger data. - **Observer** (the thing that "observes" events): An event's `Trigger` determines which observers will run. - **Event Types**: You can build any "type" of event. The concept of a "target" has been removed. Instead, define a `Trigger` that expects a specific kind of event (ex: `E: EntityEvent`). - **EntityEvent** We add a new `EntityEvent` trait, which defines an `event.entity()` accessor. This is used by the `Trigger` impls : `EntityTrigger`, `PropagateEntityTrigger`, and `EntityComponentsTrigger`. - **Message** (the buffered thing you "read" and "write") - `Message` is a solid metaphor for what this is ... it is data that is written and then at some later point read by someone / something else. I expect existing consumers of "buffered events" to lament this name change, as "event" feels nicer. But having a separate name is within everyone's best interest. - **MessageReader** (the thing that reads messages) - **MessageWriter** (the thing that writes messages) ### The Changes - `Event` trait changes - Event is now used exclusively by Observers - Added `Event::Trigger`, which defines what trigger implementation this event will use - Added the `Trigger` trait - All of the shared / hard-coded observer trigger logic has been broken out into individual context-specific Trigger traits. - "Trigger Targets" have been removed. - Instead, Events, in combination with their Trigger impl, decide how they will be triggered. In general, this means that Events now include their "targets" as fields on the event. - APIs like `trigger_targets` have been replaced by `trigger`, which can now be used for any `Event` - `EntityEvent` trait changes - Propagation config has been removed from the `EntityEvent` trait. It now lives on the `Trigger` trait (specifically the `PropagateEntityTrigger` trait). - `EntityEvent` now provides `entity / entity_mut` accessors for the Event it is implemented for - `EntityEvent` defaults to having no propagation (uses the simpler `EntityTrigger`) - `#[entity_event(propagate)]` enables the "default" propagation logic (uses ChildOf). The existing `#[entity_event(traversal = X)]` has been renamed to `#[entity_event(propagate = X)` - Deriving `EntityEvent` requires either a single `MyEvent(Entity)`, the `entity` field name (`MyEvent { entity: Entity}`), or `MyEvent { #[event_entity] custom: Entity }` - Animation event changes - Animation events now have their own `AnimationEvent` trait, which sets the `AnimationEventTrigger`. This allows developers to pass in events that _dont_ include the Entity field (as this is set by the system). The custom trigger also opens the doors to cheaply passing in additional animation system context, accessible through `On` - `EntityComponentsTrigger` - The built in Add/Remove/etc lifecycle events now use the `EntityComponentsTrigger`, which passes in the components as additional state. This _significantly_ cuts down on clones, as it does a borrow rather than cloning the list into _each_ observer execution. - Each event now has an `entity` field. - Style changes - Prefer the event name for variables: `explode: On<Explode>` not `event: On<Explode>` - Prefer using the direct field name for the entity on entity events, rather than `event.entity()`. This allows us to use more specific names where appropriate, provides better / more contextual docs, and coaches developers to think of `On<MyEvent>` _as_ the event itself. Take a look at the changes to the examples and the built-in events to see what this looks like in practice. ### Downsides - Moving the "target" into the event adds some new constraints: - Triggering the same event for multiple entities requires multiple trigger calls. For "expensive" events (ex: lots of data attached to the event), this will be more awkward. Your options become: - Create multiple instances of the event, cloning the expensive data - Use `trigger_ref`, and mutate the event on each call to change the target. - Move the "expensive" shared data into the Trigger, and use `trigger_ref_with`` - We could build a new EntityEvent method that abstracts over the "event mutation" behavior and provides something like the old `trigger_target` behavior. - Use a different `EntityTargetTrigger` (not currently provided by bevy, but we could), which brings back the old behavior. This would be used with `trigger_with` to replicate the old pattern: `world.trigger_with(MyEvent, [e1, e2].into())` (or we could make the `into()` implicit) - Bubbling the event involves mutating the event to set the entity. This means that `trigger_ref` will result in the event's `EntityEvent::entity()` being the final bubbled entity instead of the initial entity. - Some APIs (trivially) benefit from the "target entity" being separate from the event. Specifically, this new API requires changes to the "Animation Event" system in AnimationPlayer. I think this is actually a good change set, as it allows us to: - Cheaply expose more animation state as part of a new AnimationEventTrigger impl - Move that "implict" entity target provided by the AnimationPlayer into the AnimationEventTrigger - Encode the "animation event trigger-ness" of the event into the type itself (by requiring `#[event(trigger = AnimationEventTrigger)]`) - By not implementing Default for AnimationEventTrigger, we can block animation events from being fired manually by the user. ### Draft TODO - [x] Fill in documentation and update existing docs - [ ] Benchmark: I expect this impl to be significantly faster. There might also be tangible binary size improvements, as I've removed a lot of redundant codegen. - [x] Update release notes and migration guides ### Next Steps - The `BufferedEvent -> Message` rename was not included to keep the size down. Fixes #19648 --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Jan Hohenheim <jan@hohenheim.ch> |
||
|
|
94bb6e1585 |
Fix broken json documentation (#20789)
It's pretty self-explanatory. There was simply a missing ' " ' |
||
|
|
90c5c123c6 |
Fix doc paths to not use deprecated re-exports (#20496)
# Objective - Prepare for removing re-exports ## Solution - title ## Testing - cargo check --examples --all-features |
||
|
|
8e52194d70 |
CI fixes for Rust 1.89 (#20462)
Adopted from #20456 Notes: * The origin of the `dead_code` lints were coming from the `ShaderType ` derive macro. This has been reported as https://github.com/teoxoy/encase/issues/102, and a temporary workspace-wide `allow` added to the top level Cargo.toml. * One of the lints pointed out that `PartialEq` and `Eq` may not work as expected for function pointers, so `CloneBehavior` no longer implements either trait, and pattern matching is used in instead. Original PR Description: ># Objective > >Unbreak CI. > > ## Solution > > Fix the lints. > >I've opted for anonymous lifetimes in every revealed case so far; please let me know if you think I should used named lifetimes in specific cases and why. > >## Testing > >Is CI green? > >## Context > > This lint originally had a much larger splash damage, with fairly negative effects on Bevy. See https://github.com/rust-lang/rust/issues/131725. > > The more restricted former is much more helpful, despite the large diff in this PR. Bevy is a large code base! > >## TODO > >- [x] discuss proposed lifetime lint fixes >,- [x] use cargo clippy --fix to fix newly uncovered docs misformatting >- [x] fix newly revealed dead code issues >- [x] ensure CI is green --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Mike <mike.hsu@gmail.com> |
||
|
|
3517af235a |
Renamed BRP methods to be more explicit. (#19377)
Fixed #18055. Based on the discussion in the related issue, the following BRP methods have been renamed: | Old | New | |------------------------|-------------------------------| | `bevy/query` | `world.query` | | `bevy/spawn` | `world.spawn_entity` | | `bevy/destroy` | `world.despawn_entity` | | `bevy/reparent` | `world.reparent_entities` | | `bevy/get` | `world.get_components` | | `bevy/insert` | `world.insert_components` | | `bevy/remove` | `world.remove_components` | | `bevy/list` | `world.list_components` | | `bevy/mutate` | `world.mutate_components` | | `bevy/get+watch` | `world.get_components+watch` | | `bevy/list+watch` | `world.list_components+watch` | | `bevy/get_resource` | `world.get_resources` | | `bevy/insert_resource` | `world.insert_resources` | | `bevy/remove_resource` | `world.remove_resources` | | `bevy/list_resources` | `world.list_resources` | | `bevy/mutate_resource` | `world.mutate_resources` | | `registry/schema` | `registry.schema` | I also replaced the word `destroy` with `despawn` to match `EntityCommands::despawn` and other related methods. |
||
|
|
0dce905613 |
Faster FilteredEntity(Ref|Mut) and Entity(Ref|Mut)Except by borrowing Access (#20111)
# Objective Improve the performance of queries using `FilteredEntityRef`, `FilteredEntityMut`, `EntityRefExcept`, and `EntityMutExcept`. In particular, this appears to speed up `bevy_animation::animate_targets` by 10% in many-foxes. `FilteredEntity(Ref|Mut)` needs to store an `Access` to determine which components may be accessed. Prior to #15396, this required cloning the `Access` for each instance. Now, we can borrow the `Access` from the query state and make cheap pointer copies. `Entity(Ref|Mut)Except` avoided needing to clone an `Access` by calling functions on the `Bundle` trait. Unfortunately, that meant we needed to convert from a type to a `ComponentId` for every component in the bundle on every check. Now, we can do those conversions up front and pass references to an `Access`. Finally, fix a bug where `Entity(Ref|Mut)Except` would not initialize their components during `init_state`. I noticed this while updating `init_state` and fixed it while I was there. That was normally harmless because the components would be registered elsewhere, but a system like `fn system(_q1: Query<EntityMutExcept<C>>, _q2: Query<&mut C>) {}` would fail to find the `ComponentId` for `C` and not exclude it from the access for `q1`, and then panic with conflicting access from `q2`. ## Solution Change `FilteredEntityRef` and `FilteredEntityMut` to store `&'s Access` instead of `Access`, and change `EntityRefExcept` and `EntityMutExcept` to store an extra `&'s Access`. This adds the `'s` lifetime to those four types, and most changes are adding lifetimes as appropriate. Change the `WorldQuery::State` for `Entity(Ref|Mut)Except` to store an `Access` that can be borrowed from, replacing the `SmallVec<[ComponentId; 4]>` that was used only to set the query access. To support the conversions from `EntityRef` and `EntityMut`, we need to be able to create a `&'static Access` for read-all or write-all. I could not change `fn read_all_components()` to be `const` because it called the non-`const` `FixedBitSet::clear()`, so I created separate constructor functions. ## Testing Ran `cargo run --example many_foxes --features bevy/trace_tracy --release` before and after, and compared the results of `animate_targets`, since that is the only in-engine use of `EntityMutExcept` and was the motivation for creating it. Yellow is this PR, red is main: <img width="695" height="690" alt="image" src="https://github.com/user-attachments/assets/24531a3f-65bf-46d0-baa5-29ea9e56b16a" /> |
||
|
|
d16d216083 |
Add support for returning all Component and values to query method in the Bevy Remote Protocol (#19857)
# Objective
We should have an API with filtering to allow BRP clients to retrieve
all relevant data from the world state. Currently working on adding
examples - but reviews are appreciated! Still semi-WIP while I get my
head around bevy’s reflection and implementation :)
## Solution
This change adds support to query all entities in the world, and returns
all of their Reflected Components with corresponding values. For custom
`Components` it's important to still implement `Reflect` so that this
endpoint returns these. This will be useful for the
`bevy_entity_inspector` so that we can easily get the current world
state. We have modified the existing query API
so that clients can now pass in an empty `components[]` on the JSON
request.
## Testing
Updated example to showcase how to use the new endpoint to get all data:
```rust
/// Create a query_all request to send to the remote Bevy app.
/// This request will return all entities in the app, their components, and their
/// component values.
fn run_query_all_components_and_entities(url: String) -> Result<(), anyhow::Error> {
let query_all_req = BrpRequest {
jsonrpc: String::from("2.0"),
method: String::from(BRP_QUERY_METHOD),
id: Some(serde_json::to_value(1)?),
params: None,
};
println!("query_all req: {:#?}", query_all_req);
let query_all_res = ureq::post(&url)
.send_json(query_all_req)?
.body_mut()
.read_json::<serde_json::Value>()?;
println!("{query_all_res:#}");
Ok(())
}
```
---
## Showcase
In the `client.rs` example, we can clearly see (assuming the `server.rs`
is running) a query hit for all entities and components:
```text
query_all req: BrpRequest {
jsonrpc: "2.0",
method: "bevy/query",
id: Some(
Number(1),
),
params: Some(
Object {
"data": Object {
"components": Array [],
"has": Array [],
"option": Array [],
},
"filter": Object {
"with": Array [],
"without": Array [],
},
"strict": Bool(false),
},
),
}
```
And in the massive response:
```text
.....
{
"components": {
"bevy_window::monitor::Monitor": {
"name": "\\\\.\\DISPLAY1",
"physical_height": 1080,
"physical_position": [
-1920,
0
],
"physical_width": 1920,
"refresh_rate_millihertz": 240000,
"scale_factor": 1.25,
"video_modes": [
{
"bit_depth": 32,
"physical_size": [
1920,
1080
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1680,
1050
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1600,
900
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1440,
900
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1400,
1050
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1366,
768
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1360,
768
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1280,
1024
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1280,
960
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1280,
800
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1280,
768
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1280,
720
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1280,
600
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1152,
864
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
1024,
768
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
800,
600
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
640,
480
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
640,
400
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
512,
384
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
400,
300
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
320,
240
],
"refresh_rate_millihertz": 240000
},
{
"bit_depth": 32,
"physical_size": [
320,
200
],
"refresh_rate_millihertz": 240000
}
]
}
},
"entity": 4294967267
},
....
```
What's also really cool about this and `bevy_reflect` is that we also
get custom components returned as well (see below for `"server::Cube":
1.0` as the custom reflected struct specified in `server.rs`:
```text
{
"components": {
"bevy_render::primitives::Aabb": {
"center": [
0.0,
0.0,
0.0
],
"half_extents": [
0.5,
0.5,
0.5
]
},
"bevy_render::view::visibility::InheritedVisibility": true,
"bevy_render::view::visibility::ViewVisibility": true,
"bevy_render::view::visibility::Visibility": "Inherited",
"bevy_transform::components::global_transform::GlobalTransform": [
1.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
1.0,
0.0,
2.4572744369506836,
0.0
],
"bevy_transform::components::transform::Transform": {
"rotation": [
0.0,
0.0,
0.0,
1.0
],
"scale": [
1.0,
1.0,
1.0
],
"translation": [
0.0,
2.4572744369506836,
0.0
]
},
"bevy_transform::components::transform::TransformTreeChanged": null,
"server::Cube": 1.0
},
```
---------
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
|
||
|
|
92e65d5eb1 | Upgrade to Rust 1.88 (#19825) | ||
|
|
84f21f7c8a |
Schema types metadata (#19524)
# Objective - Currently there is predefinied list of supported DataTypes that can be detected on Bevy JSON Schema generation and mapped as reflect_types array elements. - Make it possible to register custom `reflectTypes` mappings for Bevy JSON Schema. ## Solution - Create a `SchemaTypesMetadata` Resource that will hold mappings for `TypeId` of `TypeData`. List is bigger from beggining and it is possible to expand it without forking package. ## Testing - I use it for quite a while in my game, I have a fork of bevy_remote with more changes that later I want to merge to main as well. --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
|
|
4e694aea53 |
ECS: put strings only used for debug behind a feature (#19558)
# Objective - Many strings in bevy_ecs are created but only used for debug: system name, component name, ... - Those strings make a significant part of the final binary and are no use in a released game ## Solution - Use [`strings`](https://linux.die.net/man/1/strings) to find ... strings in a binary - Try to find where they come from - Many are made from `type_name::<T>()` and only used in error / debug messages - Add a new structure `DebugName` that holds no value if `debug` feature is disabled - Replace `core::any::type_name::<T>()` by `DebugName::type_name::<T>()` ## Testing Measurements were taken without the new feature being enabled by default, to help with commands ### File Size I tried building the `breakout` example with `cargo run --release --example breakout` |`debug` enabled|`debug` disabled| |-|-| |81621776 B|77735728B| |77.84MB|74.13MB| ### Compilation time `hyperfine --min-runs 15 --prepare "cargo clean && sleep 5" 'RUSTC_WRAPPER="" cargo build --release --example breakout' 'RUSTC_WRAPPER="" cargo build --release --example breakout --features debug'` ``` breakout' 'RUSTC_WRAPPER="" cargo build --release --example breakout --features debug' Benchmark 1: RUSTC_WRAPPER="" cargo build --release --example breakout Time (mean ± σ): 84.856 s ± 3.565 s [User: 1093.817 s, System: 32.547 s] Range (min … max): 78.038 s … 89.214 s 15 runs Benchmark 2: RUSTC_WRAPPER="" cargo build --release --example breakout --features debug Time (mean ± σ): 92.303 s ± 2.466 s [User: 1193.443 s, System: 33.803 s] Range (min … max): 90.619 s … 99.684 s 15 runs Summary RUSTC_WRAPPER="" cargo build --release --example breakout ran 1.09 ± 0.05 times faster than RUSTC_WRAPPER="" cargo build --release --example breakout --features debug ``` |
||
|
|
6ddd0f16a8 |
Component lifecycle reorganization and documentation (#19543)
# Objective I set out with one simple goal: clearly document the differences between each of the component lifecycle events via module docs. Unfortunately, no such module existed: the various lifecycle code was scattered to the wind. Without a unified module, it's very hard to discover the related types, and there's nowhere good to put my shiny new documentation. ## Solution 1. Unify the assorted types into a single `bevy_ecs::component_lifecycle` module. 2. Write docs. 3. Write a migration guide. ## Testing Thanks CI! ## Follow-up 1. The lifecycle event names are pretty confusing, especially `OnReplace`. We should consider renaming those. No bikeshedding in my PR though! 2. Observers need real module docs too :( 3. Any additional functional changes should be done elsewhere; this is a simple docs and re-org PR. --------- Co-authored-by: theotherphil <phil.j.ellison@gmail.com> |
||
|
|
8ad7118443 |
Only get valid component ids (#19510)
# Objective - #19504 showed a 11x regression in getting component values for unregistered components. This pr should fix that and improve others a little too. - This is some cleanup work from #18173 . ## Solution - Whenever we expect a component value to exist, we only care about fully registered components, not queued to be registered components since, for the value to exist, it must be registered. - So we can use the faster `get_valid_*` instead of `get_*` in a lot of places. - Also found a bug where `valid_*` did not forward to `get_valid_*` properly. That's fixed. ## Testing CI |
||
|
|
2b8bf45f0d |
Fix BRP query failing when specifying missing/invalid components (#18871)
# 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"}} ``` |
||
|
|
0b4858726c |
Make entity::index non max (#18704)
# Objective There are two problems this aims to solve. First, `Entity::index` is currently a `u32`. That means there are `u32::MAX + 1` possible entities. Not only is that awkward, but it also make `Entity` allocation more difficult. I discovered this while working on remote entity reservation, but even on main, `Entities` doesn't handle the `u32::MAX + 1` entity very well. It can not be batch reserved because that iterator uses exclusive ranges, which has a maximum upper bound of `u32::MAX - 1`. In other words, having `u32::MAX` as a valid index can be thought of as a bug right now. We either need to make that invalid (this PR), which makes Entity allocation cleaner and makes remote reservation easier (because the length only needs to be u32 instead of u64, which, in atomics is a big deal), or we need to take another pass at `Entities` to make it handle the `u32::MAX` index properly. Second, `TableRow`, `ArchetypeRow` and `EntityIndex` (a type alias for u32) all have `u32` as the underlying type. That means using these as the index type in a `SparseSet` uses 64 bits for the sparse list because it stores `Option<IndexType>`. By using `NonMaxU32` here, we cut the memory of that list in half. To my knowledge, `EntityIndex` is the only thing that would really benefit from this niche. `TableRow` and `ArchetypeRow` I think are not stored in an `Option` in bulk. But if they ever are, this would help. Additionally this ensures `TableRow::INVALID` and `ArchetypeRow::INVALID` never conflict with an actual row, which in a nice bonus. As a related note, if we do components as entities where `ComponentId` becomes `Entity`, the the `SparseSet<ComponentId>` will see a similar memory improvement too. ## Solution Create a new type `EntityRow` that wraps `NonMaxU32`, similar to `TableRow` and `ArchetypeRow`. Change `Entity::index` to this type. ## Downsides `NonMax` is implemented as a `NonZero` with a binary inversion. That means accessing and storing the value takes one more instruction. I don't think that's a big deal, but it's worth mentioning. As a consequence, `to_bits` uses `transmute` to skip the inversion which keeps it a nop. But that also means that ordering has now flipped. In other words, higher indices are considered less than lower indices. I don't think that's a problem, but it's also worth mentioning. ## Alternatives We could keep the index as a u32 type and just document that `u32::MAX` is invalid, modifying `Entities` to ensure it never gets handed out. (But that's not enforced by the type system.) We could still take advantage of the niche here in `ComponentSparseSet`. We'd just need some unsafe manual conversions, which is probably fine, but opens up the possibility for correctness problems later. We could change `Entities` to fully support the `u32::MAX` index. (But that makes `Entities` more complex and potentially slightly slower.) ## Testing - CI - A few tests were changed because they depend on different ordering and `to_bits` values. ## Future Work - It might be worth removing the niche on `Entity::generation` since there is now a different niche. - We could move `Entity::generation` into it's own type too for clarity. - We should change `ComponentSparseSet` to take advantage of the new niche. (This PR doesn't change that yet.) - Consider removing or updating `Identifier`. This is only used for `Entity`, so it might be worth combining since `Entity` is now more unique. --------- Co-authored-by: atlv <email@atlasdostal.com> Co-authored-by: Zachary Harrold <zac@harrold.com.au> |
||
|
|
7b1c9f192e |
Adopt consistent FooSystems naming convention for system sets (#18900)
# Objective Fixes a part of #14274. Bevy has an incredibly inconsistent naming convention for its system sets, both internally and across the ecosystem. <img alt="System sets in Bevy" src="https://github.com/user-attachments/assets/d16e2027-793f-4ba4-9cc9-e780b14a5a1b" width="450" /> *Names of public system set types in Bevy* Most Bevy types use a naming of `FooSystem` or just `Foo`, but there are also a few `FooSystems` and `FooSet` types. In ecosystem crates on the other hand, `FooSet` is perhaps the most commonly used name in general. Conventions being so wildly inconsistent can make it harder for users to pick names for their own types, to search for system sets on docs.rs, or to even discern which types *are* system sets. To reign in the inconsistency a bit and help unify the ecosystem, it would be good to establish a common recommended naming convention for system sets in Bevy itself, similar to how plugins are commonly suffixed with `Plugin` (ex: `TimePlugin`). By adopting a consistent naming convention in first-party Bevy, we can softly nudge ecosystem crates to follow suit (for types where it makes sense to do so). Choosing a naming convention is also relevant now, as the [`bevy_cli` recently adopted lints](https://github.com/TheBevyFlock/bevy_cli/pull/345) to enforce naming for plugins and system sets, and the recommended naming used for system sets is still a bit open. ## Which Name To Use? Now the contentious part: what naming convention should we actually adopt? This was discussed on the Bevy Discord at the end of last year, starting [here](<https://discord.com/channels/691052431525675048/692572690833473578/1310659954683936789>). `FooSet` and `FooSystems` were the clear favorites, with `FooSet` very narrowly winning an unofficial poll. However, it seems to me like the consensus was broadly moving towards `FooSystems` at the end and after the poll, with Cart ([source](https://discord.com/channels/691052431525675048/692572690833473578/1311140204974706708)) and later Alice ([source](https://discord.com/channels/691052431525675048/692572690833473578/1311092530732859533)) and also me being in favor of it. Let's do a quick pros and cons list! Of course these are just what I thought of, so take it with a grain of salt. `FooSet`: - Pro: Nice and short! - Pro: Used by many ecosystem crates. - Pro: The `Set` suffix comes directly from the trait name `SystemSet`. - Pro: Pairs nicely with existing APIs like `in_set` and `configure_sets`. - Con: `Set` by itself doesn't actually indicate that it's related to systems *at all*, apart from the implemented trait. A set of what? - Con: Is `FooSet` a set of `Foo`s or a system set related to `Foo`? Ex: `ContactSet`, `MeshSet`, `EnemySet`... `FooSystems`: - Pro: Very clearly indicates that the type represents a collection of systems. The actual core concept, system(s), is in the name. - Pro: Parallels nicely with `FooPlugins` for plugin groups. - Pro: Low risk of conflicts with other names or misunderstandings about what the type is. - Pro: In most cases, reads *very* nicely and clearly. Ex: `PhysicsSystems` and `AnimationSystems` as opposed to `PhysicsSet` and `AnimationSet`. - Pro: Easy to search for on docs.rs. - Con: Usually results in longer names. - Con: Not yet as widely used. Really the big problem with `FooSet` is that it doesn't actually describe what it is. It describes what *kind of thing* it is (a set of something), but not *what it is a set of*, unless you know the type or check its docs or implemented traits. `FooSystems` on the other hand is much more self-descriptive in this regard, at the cost of being a bit longer to type. Ultimately, in some ways it comes down to preference and how you think of system sets. Personally, I was originally in favor of `FooSet`, but have been increasingly on the side of `FooSystems`, especially after seeing what the new names would actually look like in Avian and now Bevy. I prefer it because it usually reads better, is much more clearly related to groups of systems than `FooSet`, and overall *feels* more correct and natural to me in the long term. For these reasons, and because Alice and Cart also seemed to share a preference for it when it was previously being discussed, I propose that we adopt a `FooSystems` naming convention where applicable. ## Solution Rename Bevy's system set types to use a consistent `FooSet` naming where applicable. - `AccessibilitySystem` → `AccessibilitySystems` - `GizmoRenderSystem` → `GizmoRenderSystems` - `PickSet` → `PickingSystems` - `RunFixedMainLoopSystem` → `RunFixedMainLoopSystems` - `TransformSystem` → `TransformSystems` - `RemoteSet` → `RemoteSystems` - `RenderSet` → `RenderSystems` - `SpriteSystem` → `SpriteSystems` - `StateTransitionSteps` → `StateTransitionSystems` - `RenderUiSystem` → `RenderUiSystems` - `UiSystem` → `UiSystems` - `Animation` → `AnimationSystems` - `AssetEvents` → `AssetEventSystems` - `TrackAssets` → `AssetTrackingSystems` - `UpdateGizmoMeshes` → `GizmoMeshSystems` - `InputSystem` → `InputSystems` - `InputFocusSet` → `InputFocusSystems` - `ExtractMaterialsSet` → `MaterialExtractionSystems` - `ExtractMeshesSet` → `MeshExtractionSystems` - `RumbleSystem` → `RumbleSystems` - `CameraUpdateSystem` → `CameraUpdateSystems` - `ExtractAssetsSet` → `AssetExtractionSystems` - `Update2dText` → `Text2dUpdateSystems` - `TimeSystem` → `TimeSystems` - `AudioPlaySet` → `AudioPlaybackSystems` - `SendEvents` → `EventSenderSystems` - `EventUpdates` → `EventUpdateSystems` A lot of the names got slightly longer, but they are also a lot more consistent, and in my opinion the majority of them read much better. For a few of the names I took the liberty of rewording things a bit; definitely open to any further naming improvements. There are still also cases where the `FooSystems` naming doesn't really make sense, and those I left alone. This primarily includes system sets like `Interned<dyn SystemSet>`, `EnterSchedules<S>`, `ExitSchedules<S>`, or `TransitionSchedules<S>`, where the type has some special purpose and semantics. ## Todo - [x] Should I keep all the old names as deprecated type aliases? I can do this, but to avoid wasting work I'd prefer to first reach consensus on whether these renames are even desired. - [x] Migration guide - [x] Release notes |
||
|
|
e9a0ef49f9 |
Rename bevy_platform_support to bevy_platform (#18813)
# Objective The goal of `bevy_platform_support` is to provide a set of platform agnostic APIs, alongside platform-specific functionality. This is a high traffic crate (providing things like HashMap and Instant). Especially in light of https://github.com/bevyengine/bevy/discussions/18799, it deserves a friendlier / shorter name. Given that it hasn't had a full release yet, getting this change in before Bevy 0.16 makes sense. ## Solution - Rename `bevy_platform_support` to `bevy_platform`. |
||
|
|
d82c359a5a |
Add Default for all schedule labels (#18731)
# Objective In `bevy_enhanced_input`, I'm trying to associate `Actions` with a schedule. I can do this via an associated type on a trait, but there's no way to construct the associated label except by requiring a `Default` implementation. However, Bevy labels don't implement `Default`. ## Solution Add `Default` to all built-in labels. I think it should be useful in general. |
||
|
|
746b593833 |
Fix indentation of bevy/query strict parameter in docs (#18681)
# Objective - The `strict` field of [`BrpQueryParams`](https://dev-docs.bevyengine.org/bevy/remote/builtin_methods/struct.BrpQueryParams.html) was newly added as part of 0.16. - Its documentation in `lib.rs` improperly indents `strict`, making look like its part of [`BrpQueryFilter`](https://dev-docs.bevyengine.org/bevy/remote/builtin_methods/struct.BrpQueryFilter.html):  ## Solution - Fix `strict`'s indentation so its clear that it is a field of `BrpQueryParams`, not `BrpQueryFilter`. I would like this to be included in 0.16, since it's a trivial documentation change that fixes an error, but if it needs to be removed from the milestone that's fine. ## Testing Run `cargo doc -p bevy_remote --no-deps` and verify the indentation is fixed. :) |
||
|
|
031f67ebb6 |
fix error and lints when building for wasm32 (#18500)
# Objective - Some crates don't compile or have clippy warnings when building for wasm32 ## Solution - bevy_asset: unused lifetime - bevy_gltf: the error is not too large in wasm32 - bevy_remote: fails to compile as feature http is also gated on wasm32 - bevy_winit: unused import `error` |
||
|
|
4127ac1662 |
Properly gate functionality on http in bevy_remote (#18478)
# Objective Noticed that `bevy_remote` fails to compile without default features. ## Solution Adjusted offending method to avoid reliance on `http` module when it is disabled. ## Testing - CI - `cargo clippy -p bevy_remote --no-default-features` |
||
|
|
2b21b6cc13 |
FilteredResource returns a Result instead of a simple Option (#18265)
# Objective FilteredResource::get should return a Result instead of Option Fixes #17480 --- ## Migration Guide Users will need to handle the different return type on FilteredResource::get, FilteredResource::get_id, FilteredResource::get_mut as it is now a Result not an Option. |
||
|
|
8f38ea352e |
RPC Discover endpoint with basic informations (#18068)
# Objective It does not resolves issue for full support for OpenRPC for `bevy_remote`, but it is a first step in that direction. Connected to the #16744 issue. ## Solution - Adds `rpc.discover` endpoint to the bevy_remote which follows https://spec.open-rpc.org/#openrpc-document For now in methods array only the name, which is the endpoint address is populated. - Moves json_schema structs into new module inside `bevy_remote`. ## Testing Tested the commands by running the BRP sample( cargo run --example server --features="bevy_remote") and with these curl command: ```sh curl -X POST -d '{ "jsonrpc": "2.0", "id": 1, "method": "rpc.discover"}' 127.0.0.1:15702 | jq . ``` The output is: ```json { "jsonrpc": "2.0", "id": 1, "result": { "info": { "title": "Bevy Remote Protocol", "version": "0.16.0-dev" }, "methods": [ { "name": "bevy/mutate_component", "params": [] }, { "name": "bevy/insert", "params": [] }, { "name": "bevy/get", "params": [] }, { "name": "bevy/spawn", "params": [] }, { "name": "bevy/get+watch", "params": [] }, { "name": "bevy/destroy", "params": [] }, { "name": "bevy/list", "params": [] }, { "name": "bevy/mutate_resource", "params": [] }, { "name": "bevy/reparent", "params": [] }, { "name": "bevy/registry/schema", "params": [] }, { "name": "bevy/get_resource", "params": [] }, { "name": "bevy/query", "params": [] }, { "name": "bevy/remove_resource", "params": [] }, { "name": "rpc.discover", "params": [] }, { "name": "bevy/insert_resource", "params": [] }, { "name": "bevy/list_resources", "params": [] }, { "name": "bevy/remove", "params": [] }, { "name": "bevy/list+watch", "params": [] } ], "openrpc": "1.3.2", "servers": [ { "name": "Server", "url": "127.0.0.1:15702" } ] } } ``` --------- Co-authored-by: Viktor Gustavsson <villor94@gmail.com> |