-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - Add new SystemState and rename old SystemState to SystemMeta #2283
Conversation
Alrighty this should be good to go now. |
Trying to understand this change with regards to what you've proposed in #2089 (I was working out similar changes as part of that), and this seems to largely address that. This makes it trivial now to address that issue, simply by adding a new World Your point in "Future Work" around reusing this in the |
Hmm yeah I see no harm in adding that now. In fact: we'll want to do that now to ensure "world id safety" like we do in QueryState. Otherwise people can pass multiple worlds into the same SystemState, which is unlikely, but still very unsafe. |
Arg maybe it shouldn't live on world. Systems are an abstraction built "on top" of World. It feels a bit dirty to include it there (and we can have world id safety without it). Its not really much of an ergonomics win anyway: let mut system_state = SystemState::<Query<&A>>::new(&mut world);
let mut system_state = world.system_state::<Query<&A>>(); |
Ooh missed archetype updates too. Adding a test + fix now :) |
Yeah, seems sensible. This (breaking vs keeping this abstraction) was confusing me as I was delving into |
Alrighty now I think this is good to go. |
Just as a heads up, I don't feel too comfortable with the ECS implementation (yet) to comfortably approve an ECS PR that updates this much internals. |
bors r+ |
This enables `SystemParams` to be used outside of function systems. Anything can create and store `SystemState`, which enables efficient "param state cached" access to `SystemParams`. It adds a `ReadOnlySystemParamFetch` trait, which enables safe `SystemState::get` calls without unique world access. I renamed the old `SystemState` to `SystemMeta` to enable us to mirror the `QueryState` naming convention (but I'm happy to discuss alternative names if people have other ideas). I initially pitched this as `ParamState`, but given that it needs to include full system metadata, that doesn't feel like a particularly accurate name. ```rust #[derive(Eq, PartialEq, Debug)] struct A(usize); #[derive(Eq, PartialEq, Debug)] struct B(usize); let mut world = World::default(); world.insert_resource(A(42)); world.spawn().insert(B(7)); // we get nice lifetime elision when declaring the type on the left hand side let mut system_state: SystemState<(Res<A>, Query<&B>)> = SystemState::new(&mut world); let (a, query) = system_state.get(&world); assert_eq!(*a, A(42), "returned resource matches initial value"); assert_eq!( *query.single().unwrap(), B(7), "returned component matches initial value" ); // mutable system params require unique world access let mut system_state: SystemState<(ResMut<A>, Query<&mut B>)> = SystemState::new(&mut world); let (a, query) = system_state.get_mut(&mut world); // static lifetimes are required when declaring inside of structs struct SomeContainer { state: SystemState<(Res<'static, A>, Res<'static, B>)> } // this can be shortened using type aliases, which will be useful for complex param tuples type MyParams<'a> = (Res<'a, A>, Res<'a, B>); struct SomeContainer { state: SystemState<MyParams<'static>> } // It is the user's responsibility to call SystemState::apply(world) for parameters that queue up work let mut system_state: SystemState<(Commands, Query<&B>)> = SystemState::new(&mut world); { let (mut commands, query) = system_state.get(&world); commands.insert_resource(3.14); } system_state.apply(&mut world); ``` ## Future Work * Actually use SystemState inside FunctionSystem. This would be trivial, but it requires FunctionSystem to wrap SystemState in Option in its current form (which complicates system metadata lookup). I'd prefer to hold off until we adopt something like the later designs linked in #1364, which enable us to contruct Systems using a World reference (and also remove the need for `.system`). * Consider a "scoped" approach to automatically call SystemState::apply when systems params are no longer being used (either a container type with a Drop impl, or a function that takes a closure for user logic operating on params).
Pull request successfully merged into main. Build succeeded: |
…ine#2283) This enables `SystemParams` to be used outside of function systems. Anything can create and store `SystemState`, which enables efficient "param state cached" access to `SystemParams`. It adds a `ReadOnlySystemParamFetch` trait, which enables safe `SystemState::get` calls without unique world access. I renamed the old `SystemState` to `SystemMeta` to enable us to mirror the `QueryState` naming convention (but I'm happy to discuss alternative names if people have other ideas). I initially pitched this as `ParamState`, but given that it needs to include full system metadata, that doesn't feel like a particularly accurate name. ```rust #[derive(Eq, PartialEq, Debug)] struct A(usize); #[derive(Eq, PartialEq, Debug)] struct B(usize); let mut world = World::default(); world.insert_resource(A(42)); world.spawn().insert(B(7)); // we get nice lifetime elision when declaring the type on the left hand side let mut system_state: SystemState<(Res<A>, Query<&B>)> = SystemState::new(&mut world); let (a, query) = system_state.get(&world); assert_eq!(*a, A(42), "returned resource matches initial value"); assert_eq!( *query.single().unwrap(), B(7), "returned component matches initial value" ); // mutable system params require unique world access let mut system_state: SystemState<(ResMut<A>, Query<&mut B>)> = SystemState::new(&mut world); let (a, query) = system_state.get_mut(&mut world); // static lifetimes are required when declaring inside of structs struct SomeContainer { state: SystemState<(Res<'static, A>, Res<'static, B>)> } // this can be shortened using type aliases, which will be useful for complex param tuples type MyParams<'a> = (Res<'a, A>, Res<'a, B>); struct SomeContainer { state: SystemState<MyParams<'static>> } // It is the user's responsibility to call SystemState::apply(world) for parameters that queue up work let mut system_state: SystemState<(Commands, Query<&B>)> = SystemState::new(&mut world); { let (mut commands, query) = system_state.get(&world); commands.insert_resource(3.14); } system_state.apply(&mut world); ``` ## Future Work * Actually use SystemState inside FunctionSystem. This would be trivial, but it requires FunctionSystem to wrap SystemState in Option in its current form (which complicates system metadata lookup). I'd prefer to hold off until we adopt something like the later designs linked in bevyengine#1364, which enable us to contruct Systems using a World reference (and also remove the need for `.system`). * Consider a "scoped" approach to automatically call SystemState::apply when systems params are no longer being used (either a container type with a Drop impl, or a function that takes a closure for user logic operating on params).
This enables
SystemParams
to be used outside of function systems. Anything can create and storeSystemState
, which enables efficient "param state cached" access toSystemParams
.It adds a
ReadOnlySystemParamFetch
trait, which enables safeSystemState::get
calls without unique world access.I renamed the old
SystemState
toSystemMeta
to enable us to mirror theQueryState
naming convention (but I'm happy to discuss alternative names if people have other ideas). I initially pitched this asParamState
, but given that it needs to include full system metadata, that doesn't feel like a particularly accurate name.Future Work
.system
).