Skip to content

Commit 8a9da46

Browse files
committed
Simplify system building with BuilderSystem
1 parent 8a6708a commit 8a9da46

File tree

2 files changed

+287
-7
lines changed

2 files changed

+287
-7
lines changed

crates/bevy_ecs/src/system/builder.rs

Lines changed: 270 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
use alloc::{boxed::Box, vec::Vec};
22
use bevy_platform::cell::SyncCell;
3+
use bevy_utils::prelude::DebugName;
34
use variadics_please::all_tuples;
45

56
use crate::{
7+
change_detection::{CheckChangeTicks, Tick},
68
prelude::QueryBuilder,
7-
query::{QueryData, QueryFilter, QueryState},
9+
query::{FilteredAccessSet, QueryData, QueryFilter, QueryState},
810
resource::Resource,
911
system::{
10-
DynSystemParam, DynSystemParamState, If, Local, ParamSet, Query, SystemParam,
11-
SystemParamValidationError,
12+
DynSystemParam, DynSystemParamState, FromInput, FunctionSystem, If, IntoResult, IntoSystem,
13+
Local, ParamSet, Query, ReadOnlySystem, System, SystemInput, SystemMeta, SystemParam,
14+
SystemParamFunction, SystemParamValidationError,
1215
},
1316
world::{
14-
FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut,
15-
FilteredResourcesMutBuilder, FromWorld, World,
17+
unsafe_world_cell::UnsafeWorldCell, DeferredWorld, FilteredResources,
18+
FilteredResourcesBuilder, FilteredResourcesMut, FilteredResourcesMutBuilder, FromWorld,
19+
World,
1620
},
1721
};
18-
use core::fmt::Debug;
22+
use core::{fmt::Debug, marker::PhantomData, mem};
1923

20-
use super::{Res, ResMut, SystemState};
24+
use super::{Res, ResMut, RunSystemError, SystemState, SystemStateFlags};
2125

2226
/// A builder that can create a [`SystemParam`].
2327
///
@@ -119,6 +123,17 @@ pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized {
119123
fn build_state(self, world: &mut World) -> SystemState<P> {
120124
SystemState::from_builder(world, self)
121125
}
126+
127+
/// Create a [`System`] from a [`SystemParamBuilder`]
128+
fn build_system<Marker, In, Out, Func>(
129+
self,
130+
func: Func,
131+
) -> IntoBuilderSystem<Marker, In, Out, Func, Self>
132+
where
133+
Func: SystemParamFunction<Marker, Param = P>,
134+
{
135+
IntoBuilderSystem::new(self, func)
136+
}
122137
}
123138

124139
/// A [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
@@ -204,6 +219,254 @@ impl ParamBuilder {
204219
}
205220
}
206221

222+
/// A marker type used to distinguish builder systems from plain function systems.
223+
#[doc(hidden)]
224+
pub struct IsBuilderSystem;
225+
226+
/// An [`IntoSystem`] creating an instance of [`BuilderSystem`]
227+
pub struct IntoBuilderSystem<Marker, In, Out, Func, Builder>
228+
where
229+
Func: SystemParamFunction<Marker>,
230+
Builder: SystemParamBuilder<Func::Param>,
231+
{
232+
builder: Builder,
233+
func: Func,
234+
_data: PhantomData<fn(In) -> (Marker, Out)>,
235+
}
236+
237+
impl<Marker, In, Out, Func, Builder> IntoBuilderSystem<Marker, In, Out, Func, Builder>
238+
where
239+
Func: SystemParamFunction<Marker>,
240+
Builder: SystemParamBuilder<Func::Param>,
241+
{
242+
/// Returns a new [`IntoBuilderSystem`] given a system param builder and system function
243+
pub fn new(builder: Builder, func: Func) -> Self {
244+
Self {
245+
builder,
246+
func,
247+
_data: PhantomData,
248+
}
249+
}
250+
}
251+
252+
impl<Marker, In, Out, Func, Builder> IntoSystem<In, Out, (IsBuilderSystem, Marker)>
253+
for IntoBuilderSystem<Marker, In, Out, Func, Builder>
254+
where
255+
Marker: 'static,
256+
In: SystemInput + 'static,
257+
Out: 'static,
258+
Func: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
259+
Builder: SystemParamBuilder<Func::Param> + Send + Sync + 'static,
260+
{
261+
type System = BuilderSystem<Marker, In, Out, Func, Builder>;
262+
263+
fn into_system(this: Self) -> Self::System {
264+
BuilderSystem::new(this.builder, this.func)
265+
}
266+
}
267+
268+
/// A [`System`] created from a [`SystemParamBuilder`] whose state is not
269+
/// initialized until the first run.
270+
pub struct BuilderSystem<Marker, In, Out, Func, Builder>
271+
where
272+
Func: SystemParamFunction<Marker>,
273+
Builder: SystemParamBuilder<Func::Param>,
274+
{
275+
inner: BuilderSystemInner<Marker, In, Out, Func, Builder>,
276+
}
277+
278+
impl<Marker, In, Out, Func, Builder> BuilderSystem<Marker, In, Out, Func, Builder>
279+
where
280+
Func: SystemParamFunction<Marker>,
281+
Builder: SystemParamBuilder<Func::Param>,
282+
{
283+
/// Returns a new `BuilderSystem` given a system param builder and a system function
284+
pub fn new(builder: Builder, func: Func) -> Self {
285+
Self {
286+
inner: BuilderSystemInner::Uninitialized {
287+
builder,
288+
func,
289+
meta: SystemMeta::new::<Func>(),
290+
},
291+
}
292+
}
293+
}
294+
295+
enum BuilderSystemInner<Marker, In, Out, Func, Builder>
296+
where
297+
Func: SystemParamFunction<Marker>,
298+
Builder: SystemParamBuilder<Func::Param>,
299+
{
300+
/// A properly initialized system whose state has been constructed
301+
Initialized {
302+
system: FunctionSystem<Marker, In, Out, Func>,
303+
},
304+
/// An unititialized system, whose state hasn't been constructed from
305+
/// the param builder yet
306+
Uninitialized {
307+
builder: Builder,
308+
func: Func,
309+
meta: SystemMeta,
310+
},
311+
// This only exists as a variant to use with `mem::replace` in `initialize`.
312+
// If this state is ever observed outside `initialize`, then a `panic!`
313+
// interrupted initialization, leaving this system in an invalid state.
314+
Invalid,
315+
}
316+
317+
impl<Marker, In, Out, Func, Builder> System for BuilderSystem<Marker, In, Out, Func, Builder>
318+
where
319+
Marker: 'static,
320+
In: SystemInput + 'static,
321+
Out: 'static,
322+
Func: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
323+
Builder: SystemParamBuilder<Func::Param> + Send + Sync + 'static,
324+
{
325+
type In = In;
326+
327+
type Out = Out;
328+
329+
#[inline]
330+
fn name(&self) -> DebugName {
331+
match &self.inner {
332+
BuilderSystemInner::Initialized { system } => system.name(),
333+
BuilderSystemInner::Uninitialized { meta, .. } => meta.name().clone(),
334+
BuilderSystemInner::Invalid => unreachable!(),
335+
}
336+
}
337+
338+
#[inline]
339+
fn flags(&self) -> SystemStateFlags {
340+
match &self.inner {
341+
BuilderSystemInner::Initialized { system, .. } => system.flags(),
342+
BuilderSystemInner::Uninitialized { meta, .. } => meta.flags(),
343+
BuilderSystemInner::Invalid => unreachable!(),
344+
}
345+
}
346+
347+
#[inline]
348+
unsafe fn run_unsafe(
349+
&mut self,
350+
input: super::SystemIn<'_, Self>,
351+
world: UnsafeWorldCell,
352+
) -> Result<Self::Out, RunSystemError> {
353+
match &mut self.inner {
354+
// SAFETY: requirements upheld by the caller.
355+
BuilderSystemInner::Initialized { system, .. } => unsafe {
356+
system.run_unsafe(input, world)
357+
},
358+
BuilderSystemInner::Uninitialized { .. } => panic!(
359+
"BuilderSystem {} was not initialized before calling run_unsafe.",
360+
self.name()
361+
),
362+
BuilderSystemInner::Invalid => unreachable!(),
363+
}
364+
}
365+
366+
#[cfg(feature = "hotpatching")]
367+
#[inline]
368+
fn refresh_hotpatch(&mut self) {
369+
match &mut self.inner {
370+
BuilderSystemInner::Initialized { system, .. } => system.refresh_hotpatch(),
371+
BuilderSystemInner::Uninitialized { .. } => {}
372+
BuilderSystemInner::Invalid => unreachable!(),
373+
}
374+
}
375+
376+
#[inline]
377+
fn apply_deferred(&mut self, world: &mut World) {
378+
match &mut self.inner {
379+
BuilderSystemInner::Initialized { system, .. } => system.apply_deferred(world),
380+
BuilderSystemInner::Uninitialized { .. } => {}
381+
BuilderSystemInner::Invalid => unreachable!(),
382+
}
383+
}
384+
385+
#[inline]
386+
fn queue_deferred(&mut self, world: DeferredWorld) {
387+
match &mut self.inner {
388+
BuilderSystemInner::Initialized { system, .. } => system.queue_deferred(world),
389+
BuilderSystemInner::Uninitialized { .. } => {}
390+
BuilderSystemInner::Invalid => unreachable!(),
391+
}
392+
}
393+
394+
#[inline]
395+
unsafe fn validate_param_unsafe(
396+
&mut self,
397+
world: UnsafeWorldCell,
398+
) -> Result<(), SystemParamValidationError> {
399+
match &mut self.inner {
400+
// SAFETY: requirements upheld by the caller.
401+
BuilderSystemInner::Initialized { system, .. } => unsafe {
402+
system.validate_param_unsafe(world)
403+
},
404+
BuilderSystemInner::Uninitialized { .. } => Ok(()),
405+
BuilderSystemInner::Invalid => unreachable!(),
406+
}
407+
}
408+
409+
#[inline]
410+
fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
411+
let inner = mem::replace(&mut self.inner, BuilderSystemInner::Invalid);
412+
match inner {
413+
BuilderSystemInner::Initialized { mut system } => {
414+
let access = system.initialize(world);
415+
self.inner = BuilderSystemInner::Initialized { system };
416+
access
417+
}
418+
BuilderSystemInner::Uninitialized { builder, func, .. } => {
419+
let mut system = builder.build_state(world).build_any_system(func);
420+
let access = system.initialize(world);
421+
self.inner = BuilderSystemInner::Initialized { system };
422+
access
423+
}
424+
BuilderSystemInner::Invalid => unreachable!(),
425+
}
426+
}
427+
428+
#[inline]
429+
fn check_change_tick(&mut self, check: CheckChangeTicks) {
430+
match &mut self.inner {
431+
BuilderSystemInner::Initialized { system, .. } => system.check_change_tick(check),
432+
BuilderSystemInner::Uninitialized { .. } => {}
433+
BuilderSystemInner::Invalid => unreachable!(),
434+
}
435+
}
436+
437+
#[inline]
438+
fn get_last_run(&self) -> Tick {
439+
match &self.inner {
440+
BuilderSystemInner::Initialized { system, .. } => system.get_last_run(),
441+
BuilderSystemInner::Uninitialized { meta, .. } => meta.get_last_run(),
442+
BuilderSystemInner::Invalid => unreachable!(),
443+
}
444+
}
445+
446+
#[inline]
447+
fn set_last_run(&mut self, last_run: Tick) {
448+
match &mut self.inner {
449+
BuilderSystemInner::Initialized { system, .. } => system.set_last_run(last_run),
450+
BuilderSystemInner::Uninitialized { meta, .. } => meta.set_last_run(last_run),
451+
BuilderSystemInner::Invalid => unreachable!(),
452+
}
453+
}
454+
}
455+
456+
// SAFETY: if the wrapped system is read-only, so is this one
457+
unsafe impl<Marker, In, Out, Func, Builder> ReadOnlySystem
458+
for BuilderSystem<Marker, In, Out, Func, Builder>
459+
where
460+
Marker: 'static,
461+
In: SystemInput + 'static,
462+
Out: 'static,
463+
Func: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
464+
Builder: SystemParamBuilder<Func::Param> + Send + Sync + 'static,
465+
// the important bound
466+
FunctionSystem<Marker, In, Out, Func>: ReadOnlySystem,
467+
{
468+
}
469+
207470
// SAFETY: Any `QueryState<D, F>` for the correct world is valid for `Query::State`,
208471
// and we check the world during `build`.
209472
unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>

crates/bevy_ecs/src/system/function_system.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ impl SystemMeta {
6464
&self.name
6565
}
6666

67+
/// Returns the system's state flags
68+
pub fn flags(&self) -> SystemStateFlags {
69+
self.flags
70+
}
71+
6772
/// Sets the name of this system.
6873
///
6974
/// Useful to give closure systems more readable and unique names for debugging and tracing.
@@ -79,6 +84,18 @@ impl SystemMeta {
7984
self.name = new_name.into();
8085
}
8186

87+
/// Gets the last time this system was run.
88+
#[inline]
89+
pub fn get_last_run(&self) -> Tick {
90+
self.last_run
91+
}
92+
93+
/// Sets the last time this system was run.
94+
#[inline]
95+
pub fn set_last_run(&mut self, last_run: Tick) {
96+
self.last_run = last_run;
97+
}
98+
8299
/// Returns true if the system is [`Send`].
83100
#[inline]
84101
pub fn is_send(&self) -> bool {

0 commit comments

Comments
 (0)