|
1 | 1 | use alloc::{boxed::Box, vec::Vec}; |
2 | 2 | use bevy_platform::cell::SyncCell; |
| 3 | +use bevy_utils::prelude::DebugName; |
3 | 4 | use variadics_please::all_tuples; |
4 | 5 |
|
5 | 6 | use crate::{ |
| 7 | + change_detection::{CheckChangeTicks, Tick}, |
6 | 8 | prelude::QueryBuilder, |
7 | | - query::{QueryData, QueryFilter, QueryState}, |
| 9 | + query::{FilteredAccessSet, QueryData, QueryFilter, QueryState}, |
8 | 10 | resource::Resource, |
9 | 11 | 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, |
12 | 15 | }, |
13 | 16 | world::{ |
14 | | - FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut, |
15 | | - FilteredResourcesMutBuilder, FromWorld, World, |
| 17 | + unsafe_world_cell::UnsafeWorldCell, DeferredWorld, FilteredResources, |
| 18 | + FilteredResourcesBuilder, FilteredResourcesMut, FilteredResourcesMutBuilder, FromWorld, |
| 19 | + World, |
16 | 20 | }, |
17 | 21 | }; |
18 | | -use core::fmt::Debug; |
| 22 | +use core::{fmt::Debug, marker::PhantomData, mem}; |
19 | 23 |
|
20 | | -use super::{Res, ResMut, SystemState}; |
| 24 | +use super::{Res, ResMut, RunSystemError, SystemState, SystemStateFlags}; |
21 | 25 |
|
22 | 26 | /// A builder that can create a [`SystemParam`]. |
23 | 27 | /// |
@@ -119,6 +123,17 @@ pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized { |
119 | 123 | fn build_state(self, world: &mut World) -> SystemState<P> { |
120 | 124 | SystemState::from_builder(world, self) |
121 | 125 | } |
| 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 | + } |
122 | 137 | } |
123 | 138 |
|
124 | 139 | /// A [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization. |
@@ -204,6 +219,254 @@ impl ParamBuilder { |
204 | 219 | } |
205 | 220 | } |
206 | 221 |
|
| 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 | + |
207 | 470 | // SAFETY: Any `QueryState<D, F>` for the correct world is valid for `Query::State`, |
208 | 471 | // and we check the world during `build`. |
209 | 472 | unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static> |
|
0 commit comments