1use crate::{
2 component::{CheckChangeTicks, Tick},
3 error::Result,
4 query::FilteredAccessSet,
5 schedule::{InternedSystemSet, SystemSet},
6 system::{
7 check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, IntoResult,
8 IntoSystem, System, SystemIn, SystemInput, SystemMeta,
9 },
10 world::{unsafe_world_cell::UnsafeWorldCell, World},
11};
12
13use alloc::{borrow::Cow, vec, vec::Vec};
14use bevy_utils::prelude::DebugName;
15use core::marker::PhantomData;
16use variadics_please::all_tuples;
17
18use super::{RunSystemError, SystemParamValidationError, SystemStateFlags};
19
20pub struct ExclusiveFunctionSystem<Marker, Out, F>
27where
28 F: ExclusiveSystemParamFunction<Marker>,
29{
30 func: F,
31 #[cfg(feature = "hotpatching")]
32 current_ptr: subsecond::HotFnPtr,
33 param_state: Option<<F::Param as ExclusiveSystemParam>::State>,
34 system_meta: SystemMeta,
35 marker: PhantomData<fn() -> (Marker, Out)>,
37}
38
39impl<Marker, Out, F> ExclusiveFunctionSystem<Marker, Out, F>
40where
41 F: ExclusiveSystemParamFunction<Marker>,
42{
43 pub fn with_name(mut self, new_name: impl Into<Cow<'static, str>>) -> Self {
47 self.system_meta.set_name(new_name);
48 self
49 }
50}
51
52#[doc(hidden)]
54pub struct IsExclusiveFunctionSystem;
55
56impl<Out, Marker, F> IntoSystem<F::In, Out, (IsExclusiveFunctionSystem, Marker, Out)> for F
57where
58 Out: 'static,
59 Marker: 'static,
60 F::Out: IntoResult<Out>,
61 F: ExclusiveSystemParamFunction<Marker>,
62{
63 type System = ExclusiveFunctionSystem<Marker, Out, F>;
64 fn into_system(func: Self) -> Self::System {
65 ExclusiveFunctionSystem {
66 func,
67 #[cfg(feature = "hotpatching")]
68 current_ptr: subsecond::HotFn::current(
69 <F as ExclusiveSystemParamFunction<Marker>>::run,
70 )
71 .ptr_address(),
72 param_state: None,
73 system_meta: SystemMeta::new::<F>(),
74 marker: PhantomData,
75 }
76 }
77}
78
79const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?";
80
81impl<Marker, Out, F> System for ExclusiveFunctionSystem<Marker, Out, F>
82where
83 Marker: 'static,
84 Out: 'static,
85 F::Out: IntoResult<Out>,
86 F: ExclusiveSystemParamFunction<Marker>,
87{
88 type In = F::In;
89 type Out = Out;
90
91 #[inline]
92 fn name(&self) -> DebugName {
93 self.system_meta.name.clone()
94 }
95
96 #[inline]
97 fn flags(&self) -> SystemStateFlags {
98 SystemStateFlags::NON_SEND | SystemStateFlags::EXCLUSIVE
103 }
104
105 #[inline]
106 unsafe fn run_unsafe(
107 &mut self,
108 input: SystemIn<'_, Self>,
109 world: UnsafeWorldCell,
110 ) -> Result<Self::Out, RunSystemError> {
111 let world = unsafe { world.world_mut() };
113 world.last_change_tick_scope(self.system_meta.last_run, |world| {
114 #[cfg(feature = "trace")]
115 let _span_guard = self.system_meta.system_span.enter();
116
117 let params = F::Param::get_param(
118 self.param_state.as_mut().expect(PARAM_MESSAGE),
119 &self.system_meta,
120 );
121
122 #[cfg(feature = "hotpatching")]
123 let out = {
124 let mut hot_fn =
125 subsecond::HotFn::current(<F as ExclusiveSystemParamFunction<Marker>>::run);
126 unsafe {
129 hot_fn
130 .try_call_with_ptr(self.current_ptr, (&mut self.func, world, input, params))
131 .expect("Error calling hotpatched system. Run a full rebuild")
132 }
133 };
134 #[cfg(not(feature = "hotpatching"))]
135 let out = self.func.run(world, input, params);
136
137 world.flush();
138 self.system_meta.last_run = world.increment_change_tick();
139
140 IntoResult::into_result(out)
141 })
142 }
143
144 #[cfg(feature = "hotpatching")]
145 #[inline]
146 fn refresh_hotpatch(&mut self) {
147 let new = subsecond::HotFn::current(<F as ExclusiveSystemParamFunction<Marker>>::run)
148 .ptr_address();
149 if new != self.current_ptr {
150 log::debug!("system {} hotpatched", self.name());
151 }
152 self.current_ptr = new;
153 }
154
155 #[inline]
156 fn apply_deferred(&mut self, _world: &mut World) {
157 }
161
162 #[inline]
163 fn queue_deferred(&mut self, _world: crate::world::DeferredWorld) {
164 }
168
169 #[inline]
170 unsafe fn validate_param_unsafe(
171 &mut self,
172 _world: UnsafeWorldCell,
173 ) -> Result<(), SystemParamValidationError> {
174 Ok(())
176 }
177
178 #[inline]
179 fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
180 self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
181 self.param_state = Some(F::Param::init(world, &mut self.system_meta));
182 FilteredAccessSet::new()
183 }
184
185 #[inline]
186 fn check_change_tick(&mut self, check: CheckChangeTicks) {
187 check_system_change_tick(
188 &mut self.system_meta.last_run,
189 check,
190 self.system_meta.name.clone(),
191 );
192 }
193
194 fn default_system_sets(&self) -> Vec<InternedSystemSet> {
195 let set = crate::schedule::SystemTypeSet::<Self>::new();
196 vec![set.intern()]
197 }
198
199 fn get_last_run(&self) -> Tick {
200 self.system_meta.last_run
201 }
202
203 fn set_last_run(&mut self, last_run: Tick) {
204 self.system_meta.last_run = last_run;
205 }
206}
207
208#[diagnostic::on_unimplemented(
213 message = "`{Self}` is not an exclusive system",
214 label = "invalid system"
215)]
216pub trait ExclusiveSystemParamFunction<Marker>: Send + Sync + 'static {
217 type In: SystemInput;
219
220 type Out;
222
223 type Param: ExclusiveSystemParam;
225
226 fn run(
228 &mut self,
229 world: &mut World,
230 input: <Self::In as SystemInput>::Inner<'_>,
231 param_value: ExclusiveSystemParamItem<Self::Param>,
232 ) -> Self::Out;
233}
234
235#[doc(hidden)]
237pub struct HasExclusiveSystemInput;
238
239macro_rules! impl_exclusive_system_function {
240 ($($param: ident),*) => {
241 #[expect(
242 clippy::allow_attributes,
243 reason = "This is within a macro, and as such, the below lints may not always apply."
244 )]
245 #[allow(
246 non_snake_case,
247 reason = "Certain variable names are provided by the caller, not by us."
248 )]
249 impl<Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn($($param,)*) -> Out> for Func
250 where
251 Func: Send + Sync + 'static,
252 for <'a> &'a mut Func:
253 FnMut(&mut World, $($param),*) -> Out +
254 FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
255 Out: 'static,
256 {
257 type In = ();
258 type Out = Out;
259 type Param = ($($param,)*);
260 #[inline]
261 fn run(&mut self, world: &mut World, _in: (), param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
262 fn call_inner<Out, $($param,)*>(
266 mut f: impl FnMut(&mut World, $($param,)*) -> Out,
267 world: &mut World,
268 $($param: $param,)*
269 ) -> Out {
270 f(world, $($param,)*)
271 }
272 let ($($param,)*) = param_value;
273 call_inner(self, world, $($param),*)
274 }
275 }
276
277 #[expect(
278 clippy::allow_attributes,
279 reason = "This is within a macro, and as such, the below lints may not always apply."
280 )]
281 #[allow(
282 non_snake_case,
283 reason = "Certain variable names are provided by the caller, not by us."
284 )]
285 impl<In, Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<(HasExclusiveSystemInput, fn(In, $($param,)*) -> Out)> for Func
286 where
287 Func: Send + Sync + 'static,
288 for <'a> &'a mut Func:
289 FnMut(In, &mut World, $($param),*) -> Out +
290 FnMut(In::Param<'_>, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
291 In: SystemInput + 'static,
292 Out: 'static,
293 {
294 type In = In;
295 type Out = Out;
296 type Param = ($($param,)*);
297 #[inline]
298 fn run(&mut self, world: &mut World, input: In::Inner<'_>, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
299 fn call_inner<In: SystemInput, Out, $($param,)*>(
303 _: PhantomData<In>,
304 mut f: impl FnMut(In::Param<'_>, &mut World, $($param,)*) -> Out,
305 input: In::Inner<'_>,
306 world: &mut World,
307 $($param: $param,)*
308 ) -> Out {
309 f(In::wrap(input), world, $($param,)*)
310 }
311 let ($($param,)*) = param_value;
312 call_inner(PhantomData::<In>, self, input, world, $($param),*)
313 }
314 }
315 };
316}
317all_tuples!(impl_exclusive_system_function, 0, 16, F);
320
321#[cfg(test)]
322mod tests {
323 use crate::system::input::SystemInput;
324
325 use super::*;
326
327 #[test]
328 fn into_system_type_id_consistency() {
329 fn test<T, In: SystemInput, Out, Marker>(function: T)
330 where
331 T: IntoSystem<In, Out, Marker> + Copy,
332 {
333 fn reference_system(_world: &mut World) {}
334
335 use core::any::TypeId;
336
337 let system = IntoSystem::into_system(function);
338
339 assert_eq!(
340 system.type_id(),
341 function.system_type_id(),
342 "System::type_id should be consistent with IntoSystem::system_type_id"
343 );
344
345 assert_eq!(
346 system.type_id(),
347 TypeId::of::<T::System>(),
348 "System::type_id should be consistent with TypeId::of::<T::System>()"
349 );
350
351 assert_ne!(
352 system.type_id(),
353 IntoSystem::into_system(reference_system).type_id(),
354 "Different systems should have different TypeIds"
355 );
356 }
357
358 fn exclusive_function_system(_world: &mut World) {}
359
360 test(exclusive_function_system);
361 }
362}