
# Objective #7863 introduced a potential footgun. When trying to incorrectly add a user-defined type using `in_base_set`, the compiler will suggest that the user implement `BaseSystemSet` for their type. This is a reasonable-sounding suggestion, however this is not the correct way to make a base set, and will lead to a confusing panic message when a marker trait is implemented for the wrong type. ## Solution Rewrite the documentation for these traits, making it more clear that `BaseSystemSet` is a marker for types that are already base sets, and not a way to define a base set.
171 lines
4.5 KiB
Rust
171 lines
4.5 KiB
Rust
use std::any::TypeId;
|
|
use std::fmt::Debug;
|
|
use std::hash::{Hash, Hasher};
|
|
use std::marker::PhantomData;
|
|
|
|
pub use bevy_ecs_macros::{ScheduleLabel, SystemSet};
|
|
use bevy_utils::define_boxed_label;
|
|
use bevy_utils::label::DynHash;
|
|
|
|
use crate::system::{
|
|
ExclusiveSystemParamFunction, IsExclusiveFunctionSystem, IsFunctionSystem, SystemParamFunction,
|
|
};
|
|
|
|
define_boxed_label!(ScheduleLabel);
|
|
|
|
pub type BoxedSystemSet = Box<dyn SystemSet>;
|
|
pub type BoxedScheduleLabel = Box<dyn ScheduleLabel>;
|
|
|
|
/// Types that identify logical groups of systems.
|
|
pub trait SystemSet: DynHash + Debug + Send + Sync + 'static {
|
|
/// Returns `Some` if this system set is a [`SystemTypeSet`].
|
|
fn system_type(&self) -> Option<TypeId> {
|
|
None
|
|
}
|
|
|
|
/// Returns `true` if this set is a "base system set". Systems
|
|
/// can only belong to one base set at a time. Systems and Sets
|
|
/// can only be added to base sets using specialized `in_base_set`
|
|
/// APIs. This enables "mutually exclusive" behaviors. It also
|
|
/// enables schedules to have a "default base set", which can be used
|
|
/// to apply default configuration to systems.
|
|
fn is_base(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
/// Creates a boxed clone of the label corresponding to this system set.
|
|
fn dyn_clone(&self) -> Box<dyn SystemSet>;
|
|
}
|
|
|
|
/// A marker trait for `SystemSet` types where [`is_base`] returns `true`.
|
|
/// This should only be implemented for types that satisfy this requirement.
|
|
/// It is automatically implemented for base set types by `#[derive(SystemSet)]`.
|
|
///
|
|
/// [`is_base`]: SystemSet::is_base
|
|
pub trait BaseSystemSet: SystemSet {}
|
|
|
|
/// A marker trait for `SystemSet` types where [`is_base`] returns `false`.
|
|
/// This should only be implemented for types that satisfy this requirement.
|
|
/// It is automatically implemented for non-base set types by `#[derive(SystemSet)]`.
|
|
///
|
|
/// [`is_base`]: SystemSet::is_base
|
|
pub trait FreeSystemSet: SystemSet {}
|
|
|
|
impl PartialEq for dyn SystemSet {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.dyn_eq(other.as_dyn_eq())
|
|
}
|
|
}
|
|
|
|
impl Eq for dyn SystemSet {}
|
|
|
|
impl Hash for dyn SystemSet {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
self.dyn_hash(state);
|
|
}
|
|
}
|
|
|
|
impl Clone for Box<dyn SystemSet> {
|
|
fn clone(&self) -> Self {
|
|
self.dyn_clone()
|
|
}
|
|
}
|
|
|
|
/// A [`SystemSet`] grouping instances of the same function.
|
|
///
|
|
/// This kind of set is automatically populated and thus has some special rules:
|
|
/// - You cannot manually add members.
|
|
/// - You cannot configure them.
|
|
/// - You cannot order something relative to one if it has more than one member.
|
|
pub struct SystemTypeSet<T: 'static>(PhantomData<fn() -> T>);
|
|
|
|
impl<T: 'static> SystemTypeSet<T> {
|
|
pub(crate) fn new() -> Self {
|
|
Self(PhantomData)
|
|
}
|
|
}
|
|
|
|
impl<T> Debug for SystemTypeSet<T> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_tuple("SystemTypeSet")
|
|
.field(&std::any::type_name::<T>())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<T> Hash for SystemTypeSet<T> {
|
|
fn hash<H: Hasher>(&self, _state: &mut H) {
|
|
// all systems of a given type are the same
|
|
}
|
|
}
|
|
impl<T> Clone for SystemTypeSet<T> {
|
|
fn clone(&self) -> Self {
|
|
Self(PhantomData)
|
|
}
|
|
}
|
|
|
|
impl<T> Copy for SystemTypeSet<T> {}
|
|
|
|
impl<T> PartialEq for SystemTypeSet<T> {
|
|
#[inline]
|
|
fn eq(&self, _other: &Self) -> bool {
|
|
// all systems of a given type are the same
|
|
true
|
|
}
|
|
}
|
|
|
|
impl<T> Eq for SystemTypeSet<T> {}
|
|
|
|
impl<T> SystemSet for SystemTypeSet<T> {
|
|
fn system_type(&self) -> Option<TypeId> {
|
|
Some(TypeId::of::<T>())
|
|
}
|
|
|
|
fn dyn_clone(&self) -> Box<dyn SystemSet> {
|
|
Box::new(*self)
|
|
}
|
|
}
|
|
|
|
/// Types that can be converted into a [`SystemSet`].
|
|
pub trait IntoSystemSet<Marker>: Sized {
|
|
type Set: SystemSet;
|
|
|
|
fn into_system_set(self) -> Self::Set;
|
|
}
|
|
|
|
// systems sets
|
|
impl<S: SystemSet> IntoSystemSet<()> for S {
|
|
type Set = Self;
|
|
|
|
#[inline]
|
|
fn into_system_set(self) -> Self::Set {
|
|
self
|
|
}
|
|
}
|
|
|
|
// systems
|
|
impl<Marker, F> IntoSystemSet<(IsFunctionSystem, Marker)> for F
|
|
where
|
|
F: SystemParamFunction<Marker>,
|
|
{
|
|
type Set = SystemTypeSet<Self>;
|
|
|
|
#[inline]
|
|
fn into_system_set(self) -> Self::Set {
|
|
SystemTypeSet::new()
|
|
}
|
|
}
|
|
|
|
// exclusive systems
|
|
impl<Marker, F> IntoSystemSet<(IsExclusiveFunctionSystem, Marker)> for F
|
|
where
|
|
F: ExclusiveSystemParamFunction<Marker>,
|
|
{
|
|
type Set = SystemTypeSet<Self>;
|
|
|
|
#[inline]
|
|
fn into_system_set(self) -> Self::Set {
|
|
SystemTypeSet::new()
|
|
}
|
|
}
|