Skip to content

Commit 7e51f60

Browse files
authored
Add IntoSystem::with_input and ::with_input_from system wrappers (#18067)
# Objective Originally [provided as a solution to a user's problem in Discord](https://discord.com/channels/691052431525675048/1247654592838111302/1344431131277394042), library authors might find the need to present user-registered systems with system-specific data. Typically `Local<T>` is used for this type of thing, but its not generally feasible or possible to configure/set the underlying `T` data for locals. Alternatively, we can use `SystemInput` to pass the data. ## Solution - Added `IntoSystem::with_input`: Allows system-specific data to be passed in explicitly. - Added `IntoSystem::with_input_from`: Allows system-specific data to be created at initialization time via `FromWorld`. ## Testing Added two new tests, testing each of `with_input` and `with_input_from`.
1 parent b8724c2 commit 7e51f60

File tree

2 files changed

+345
-5
lines changed

2 files changed

+345
-5
lines changed

crates/bevy_ecs/src/system/mod.rs

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ pub use system_name::*;
153153
pub use system_param::*;
154154
pub use system_registry::*;
155155

156-
use crate::world::World;
156+
use crate::world::{FromWorld, World};
157157

158158
/// Conversion trait to turn something into a [`System`].
159159
///
@@ -228,6 +228,77 @@ pub trait IntoSystem<In: SystemInput, Out, Marker>: Sized {
228228
IntoAdapterSystem::new(f, self)
229229
}
230230

231+
/// Passes a mutable reference to `value` as input to the system each run,
232+
/// turning it into a system that takes no input.
233+
///
234+
/// `Self` can have any [`SystemInput`] type that takes a mutable reference
235+
/// to `T`, such as [`InMut`].
236+
///
237+
/// # Example
238+
///
239+
/// ```
240+
/// # use bevy_ecs::prelude::*;
241+
/// #
242+
/// fn my_system(InMut(value): InMut<usize>) {
243+
/// *value += 1;
244+
/// if *value > 10 {
245+
/// println!("Value is greater than 10!");
246+
/// }
247+
/// }
248+
///
249+
/// # let mut schedule = Schedule::default();
250+
/// schedule.add_systems(my_system.with_input(0));
251+
/// # bevy_ecs::system::assert_is_system(my_system.with_input(0));
252+
/// ```
253+
fn with_input<T>(self, value: T) -> WithInputWrapper<Self::System, T>
254+
where
255+
for<'i> In: SystemInput<Inner<'i> = &'i mut T>,
256+
T: Send + Sync + 'static,
257+
{
258+
WithInputWrapper::new(self, value)
259+
}
260+
261+
/// Passes a mutable reference to a value of type `T` created via
262+
/// [`FromWorld`] as input to the system each run, turning it into a system
263+
/// that takes no input.
264+
///
265+
/// `Self` can have any [`SystemInput`] type that takes a mutable reference
266+
/// to `T`, such as [`InMut`].
267+
///
268+
/// # Example
269+
///
270+
/// ```
271+
/// # use bevy_ecs::prelude::*;
272+
/// #
273+
/// struct MyData {
274+
/// value: usize,
275+
/// }
276+
///
277+
/// impl FromWorld for MyData {
278+
/// fn from_world(world: &mut World) -> Self {
279+
/// // Fetch from the world the data needed to create `MyData`
280+
/// # MyData { value: 0 }
281+
/// }
282+
/// }
283+
///
284+
/// fn my_system(InMut(data): InMut<MyData>) {
285+
/// data.value += 1;
286+
/// if data.value > 10 {
287+
/// println!("Value is greater than 10!");
288+
/// }
289+
/// }
290+
/// # let mut schedule = Schedule::default();
291+
/// schedule.add_systems(my_system.with_input_from::<MyData>());
292+
/// # bevy_ecs::system::assert_is_system(my_system.with_input_from::<MyData>());
293+
/// ```
294+
fn with_input_from<T>(self) -> WithInputFromWrapper<Self::System, T>
295+
where
296+
for<'i> In: SystemInput<Inner<'i> = &'i mut T>,
297+
T: FromWorld + Send + Sync + 'static,
298+
{
299+
WithInputFromWrapper::new(self)
300+
}
301+
231302
/// Get the [`TypeId`] of the [`System`] produced after calling [`into_system`](`IntoSystem::into_system`).
232303
#[inline]
233304
fn system_type_id(&self) -> TypeId {
@@ -347,8 +418,8 @@ mod tests {
347418
Schedule,
348419
},
349420
system::{
350-
Commands, In, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, Res, ResMut,
351-
Single, StaticSystemParam, System, SystemState,
421+
Commands, In, InMut, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, Res,
422+
ResMut, Single, StaticSystemParam, System, SystemState,
352423
},
353424
world::{DeferredWorld, EntityMut, FromWorld, OnAdd, World},
354425
};
@@ -1879,4 +1950,40 @@ mod tests {
18791950
.commands()
18801951
.queue(|_world: &mut World| -> () { todo!() });
18811952
}
1953+
1954+
#[test]
1955+
fn with_input() {
1956+
fn sys(InMut(v): InMut<usize>) {
1957+
*v += 1;
1958+
}
1959+
1960+
let mut world = World::new();
1961+
let mut system = IntoSystem::into_system(sys.with_input(42));
1962+
system.initialize(&mut world);
1963+
system.run((), &mut world);
1964+
assert_eq!(*system.value(), 43);
1965+
}
1966+
1967+
#[test]
1968+
fn with_input_from() {
1969+
struct TestData(usize);
1970+
1971+
impl FromWorld for TestData {
1972+
fn from_world(_world: &mut World) -> Self {
1973+
Self(5)
1974+
}
1975+
}
1976+
1977+
fn sys(InMut(v): InMut<TestData>) {
1978+
v.0 += 1;
1979+
}
1980+
1981+
let mut world = World::new();
1982+
let mut system = IntoSystem::into_system(sys.with_input_from::<TestData>());
1983+
assert!(system.value().is_none());
1984+
system.initialize(&mut world);
1985+
assert!(system.value().is_some());
1986+
system.run((), &mut world);
1987+
assert_eq!(system.value().unwrap().0, 6);
1988+
}
18821989
}

crates/bevy_ecs/src/system/schedule_system.rs

Lines changed: 235 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use crate::{
55
component::{ComponentId, Tick},
66
error::Result,
77
query::{Access, FilteredAccessSet},
8-
system::{input::SystemIn, BoxedSystem, System},
9-
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
8+
system::{input::SystemIn, BoxedSystem, System, SystemInput},
9+
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, FromWorld, World},
1010
};
1111

1212
use super::{IntoSystem, SystemParamValidationError};
@@ -118,5 +118,238 @@ impl<S: System<In = ()>> System for InfallibleSystemWrapper<S> {
118118
}
119119
}
120120

121+
/// See [`IntoSystem::with_input`] for details.
122+
pub struct WithInputWrapper<S, T>
123+
where
124+
for<'i> S: System<In: SystemInput<Inner<'i> = &'i mut T>>,
125+
T: Send + Sync + 'static,
126+
{
127+
system: S,
128+
value: T,
129+
}
130+
131+
impl<S, T> WithInputWrapper<S, T>
132+
where
133+
for<'i> S: System<In: SystemInput<Inner<'i> = &'i mut T>>,
134+
T: Send + Sync + 'static,
135+
{
136+
/// Wraps the given system with the given input value.
137+
pub fn new<M>(system: impl IntoSystem<S::In, S::Out, M, System = S>, value: T) -> Self {
138+
Self {
139+
system: IntoSystem::into_system(system),
140+
value,
141+
}
142+
}
143+
144+
/// Returns a reference to the input value.
145+
pub fn value(&self) -> &T {
146+
&self.value
147+
}
148+
149+
/// Returns a mutable reference to the input value.
150+
pub fn value_mut(&mut self) -> &mut T {
151+
&mut self.value
152+
}
153+
}
154+
155+
impl<S, T> System for WithInputWrapper<S, T>
156+
where
157+
for<'i> S: System<In: SystemInput<Inner<'i> = &'i mut T>>,
158+
T: Send + Sync + 'static,
159+
{
160+
type In = ();
161+
162+
type Out = S::Out;
163+
164+
fn name(&self) -> Cow<'static, str> {
165+
self.system.name()
166+
}
167+
168+
fn component_access(&self) -> &Access<ComponentId> {
169+
self.system.component_access()
170+
}
171+
172+
fn component_access_set(&self) -> &FilteredAccessSet<ComponentId> {
173+
self.system.component_access_set()
174+
}
175+
176+
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
177+
self.system.archetype_component_access()
178+
}
179+
180+
fn is_send(&self) -> bool {
181+
self.system.is_send()
182+
}
183+
184+
fn is_exclusive(&self) -> bool {
185+
self.system.is_exclusive()
186+
}
187+
188+
fn has_deferred(&self) -> bool {
189+
self.system.has_deferred()
190+
}
191+
192+
unsafe fn run_unsafe(
193+
&mut self,
194+
_input: SystemIn<'_, Self>,
195+
world: UnsafeWorldCell,
196+
) -> Self::Out {
197+
self.system.run_unsafe(&mut self.value, world)
198+
}
199+
200+
fn apply_deferred(&mut self, world: &mut World) {
201+
self.system.apply_deferred(world);
202+
}
203+
204+
fn queue_deferred(&mut self, world: DeferredWorld) {
205+
self.system.queue_deferred(world);
206+
}
207+
208+
unsafe fn validate_param_unsafe(
209+
&mut self,
210+
world: UnsafeWorldCell,
211+
) -> Result<(), SystemParamValidationError> {
212+
self.system.validate_param_unsafe(world)
213+
}
214+
215+
fn initialize(&mut self, world: &mut World) {
216+
self.system.initialize(world);
217+
}
218+
219+
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
220+
self.system.update_archetype_component_access(world);
221+
}
222+
223+
fn check_change_tick(&mut self, change_tick: Tick) {
224+
self.system.check_change_tick(change_tick);
225+
}
226+
227+
fn get_last_run(&self) -> Tick {
228+
self.system.get_last_run()
229+
}
230+
231+
fn set_last_run(&mut self, last_run: Tick) {
232+
self.system.set_last_run(last_run);
233+
}
234+
}
235+
236+
/// Constructed in [`IntoSystem::with_input_from`].
237+
pub struct WithInputFromWrapper<S, T> {
238+
system: S,
239+
value: Option<T>,
240+
}
241+
242+
impl<S, T> WithInputFromWrapper<S, T>
243+
where
244+
for<'i> S: System<In: SystemInput<Inner<'i> = &'i mut T>>,
245+
T: Send + Sync + 'static,
246+
{
247+
/// Wraps the given system.
248+
pub fn new<M>(system: impl IntoSystem<S::In, S::Out, M, System = S>) -> Self {
249+
Self {
250+
system: IntoSystem::into_system(system),
251+
value: None,
252+
}
253+
}
254+
255+
/// Returns a reference to the input value, if it has been initialized.
256+
pub fn value(&self) -> Option<&T> {
257+
self.value.as_ref()
258+
}
259+
260+
/// Returns a mutable reference to the input value, if it has been initialized.
261+
pub fn value_mut(&mut self) -> Option<&mut T> {
262+
self.value.as_mut()
263+
}
264+
}
265+
266+
impl<S, T> System for WithInputFromWrapper<S, T>
267+
where
268+
for<'i> S: System<In: SystemInput<Inner<'i> = &'i mut T>>,
269+
T: FromWorld + Send + Sync + 'static,
270+
{
271+
type In = ();
272+
273+
type Out = S::Out;
274+
275+
fn name(&self) -> Cow<'static, str> {
276+
self.system.name()
277+
}
278+
279+
fn component_access(&self) -> &Access<ComponentId> {
280+
self.system.component_access()
281+
}
282+
283+
fn component_access_set(&self) -> &FilteredAccessSet<ComponentId> {
284+
self.system.component_access_set()
285+
}
286+
287+
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
288+
self.system.archetype_component_access()
289+
}
290+
291+
fn is_send(&self) -> bool {
292+
self.system.is_send()
293+
}
294+
295+
fn is_exclusive(&self) -> bool {
296+
self.system.is_exclusive()
297+
}
298+
299+
fn has_deferred(&self) -> bool {
300+
self.system.has_deferred()
301+
}
302+
303+
unsafe fn run_unsafe(
304+
&mut self,
305+
_input: SystemIn<'_, Self>,
306+
world: UnsafeWorldCell,
307+
) -> Self::Out {
308+
let value = self
309+
.value
310+
.as_mut()
311+
.expect("System input value was not found. Did you forget to initialize the system before running it?");
312+
self.system.run_unsafe(value, world)
313+
}
314+
315+
fn apply_deferred(&mut self, world: &mut World) {
316+
self.system.apply_deferred(world);
317+
}
318+
319+
fn queue_deferred(&mut self, world: DeferredWorld) {
320+
self.system.queue_deferred(world);
321+
}
322+
323+
unsafe fn validate_param_unsafe(
324+
&mut self,
325+
world: UnsafeWorldCell,
326+
) -> Result<(), SystemParamValidationError> {
327+
self.system.validate_param_unsafe(world)
328+
}
329+
330+
fn initialize(&mut self, world: &mut World) {
331+
self.system.initialize(world);
332+
if self.value.is_none() {
333+
self.value = Some(T::from_world(world));
334+
}
335+
}
336+
337+
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
338+
self.system.update_archetype_component_access(world);
339+
}
340+
341+
fn check_change_tick(&mut self, change_tick: Tick) {
342+
self.system.check_change_tick(change_tick);
343+
}
344+
345+
fn get_last_run(&self) -> Tick {
346+
self.system.get_last_run()
347+
}
348+
349+
fn set_last_run(&mut self, last_run: Tick) {
350+
self.system.set_last_run(last_run);
351+
}
352+
}
353+
121354
/// Type alias for a `BoxedSystem` that a `Schedule` can store.
122355
pub type ScheduleSystem = BoxedSystem<(), Result>;

0 commit comments

Comments
 (0)