Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Slightly less WIP] Design SystemParams to hold state #1364

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 35 additions & 14 deletions crates/bevy_app/src/app_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::{
stage, startup_stage, PluginGroup, PluginGroupBuilder,
};
use bevy_ecs::{
clear_trackers_system, FromResources, IntoSystem, Resource, Resources, RunOnce, Schedule,
Stage, StateStage, System, SystemStage, World,
clear_trackers_system, AsSystem, FromResources, IntoSystem, Resource, Resources, RunOnce,
Schedule, Stage, StateStage, System, SystemStage, World,
};
use bevy_utils::tracing::debug;

Expand Down Expand Up @@ -125,48 +125,63 @@ impl AppBuilder {
self
}

pub fn add_system<S: System<In = (), Out = ()>>(&mut self, system: S) -> &mut Self {
pub fn add_system<S: AsSystem>(&mut self, system: S) -> &mut Self
where
S::System: System<In = (), Out = ()>,
{
self.add_system_to_stage(stage::UPDATE, system)
}

pub fn on_state_enter<T: Clone + Resource, S: System<In = (), Out = ()>>(
pub fn on_state_enter<T: Clone + Resource, S: AsSystem>(
&mut self,
stage: &str,
state: T,
system: S,
) -> &mut Self {
) -> &mut Self
where
S::System: System<In = (), Out = ()>,
{
self.stage(stage, |stage: &mut StateStage<T>| {
stage.on_state_enter(state, system)
})
}

pub fn on_state_update<T: Clone + Resource, S: System<In = (), Out = ()>>(
pub fn on_state_update<T: Clone + Resource, S: AsSystem>(
&mut self,
stage: &str,
state: T,
system: S,
) -> &mut Self {
) -> &mut Self
where
S::System: System<In = (), Out = ()>,
{
self.stage(stage, |stage: &mut StateStage<T>| {
stage.on_state_update(state, system)
})
}

pub fn on_state_exit<T: Clone + Resource, S: System<In = (), Out = ()>>(
pub fn on_state_exit<T: Clone + Resource, S: AsSystem>(
&mut self,
stage: &str,
state: T,
system: S,
) -> &mut Self {
) -> &mut Self
where
S::System: System<In = (), Out = ()>,
{
self.stage(stage, |stage: &mut StateStage<T>| {
stage.on_state_exit(state, system)
})
}

pub fn add_startup_system_to_stage<S: System<In = (), Out = ()>>(
pub fn add_startup_system_to_stage<S: AsSystem>(
&mut self,
stage_name: &'static str,
system: S,
) -> &mut Self {
) -> &mut Self
where
S::System: System<In = (), Out = ()>,
{
self.app
.schedule
.stage(stage::STARTUP, |schedule: &mut Schedule| {
Expand All @@ -175,7 +190,10 @@ impl AppBuilder {
self
}

pub fn add_startup_system<S: System<In = (), Out = ()>>(&mut self, system: S) -> &mut Self {
pub fn add_startup_system<S: AsSystem>(&mut self, system: S) -> &mut Self
where
S::System: System<In = (), Out = ()>,
{
self.add_startup_system_to_stage(startup_stage::STARTUP, system)
}

Expand All @@ -197,11 +215,14 @@ impl AppBuilder {
.add_stage(stage::LAST, SystemStage::parallel())
}

pub fn add_system_to_stage<S: System<In = (), Out = ()>>(
pub fn add_system_to_stage<S: AsSystem>(
&mut self,
stage_name: &'static str,
system: S,
) -> &mut Self {
) -> &mut Self
where
S::System: System<In = (), Out = ()>,
{
self.app.schedule.add_system_to_stage(stage_name, system);
self
}
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_app/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
/// Reads events of type `T` in order and tracks which events have already been read.
#[derive(SystemParam)]
pub struct EventReader<'a, T: bevy_ecs::Resource> {
last_event_count: Local<'a, (usize, PhantomData<T>)>,
// Each local is unique, so this won't collide
last_event_count: Local<'a, usize>,
events: Res<'a, Events<T>>,
}

Expand Down Expand Up @@ -216,7 +217,7 @@ impl<'a, T: bevy_ecs::Resource> EventReader<'a, T> {

/// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events.
pub fn iter_with_id(&mut self) -> impl DoubleEndedIterator<Item = (&T, EventId<T>)> {
internal_event_reader(&mut self.last_event_count.0, &self.events).map(|(event, id)| {
internal_event_reader(&mut self.last_event_count, &self.events).map(|(event, id)| {
trace!("EventReader::iter() -> {}", id);
(event, id)
})
Expand Down
70 changes: 45 additions & 25 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
let query_fn_mut = &query_fn_muts[0..query_count];
tokens.extend(TokenStream::from(quote! {
impl<#(#lifetime,)* #(#query: WorldQuery,)* #(#filter: QueryFilter,)*> QueryTuple for (#(Query<#lifetime, #query, #filter>,)*) {
type Fetch = (#( (#query::Fetch, #filter), )*);
unsafe fn new(world: &World, component_access: &TypeAccess<ArchetypeComponent>) -> Self {
(
#(
Expand All @@ -332,13 +333,17 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
}
}

impl<'a, #(#query: Fetch<'a>,)* #(#filter: QueryFilter,)*> QueryTupleFetch<'a> for (#((#query, #filter),)*) {
type Item = (#(Query<'a, <#query as Fetch<'a>>::Query, #filter>,)*);
}

impl<#(#lifetime,)* #(#query: WorldQuery,)* #(#filter: QueryFilter,)*> QuerySet<(#(Query<#lifetime, #query, #filter>,)*)> {
#(#query_fn)*
#(#query_fn_mut)*
}
}));
}

// println!("{}", tokens);
tokens
}

Expand Down Expand Up @@ -416,19 +421,6 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
.filter(|g| matches!(g, GenericParam::Type(_)))
.collect();

let phantoms = lifetimeless_generics
.iter()
.map(|g| {
let g = match g {
GenericParam::Type(g) => &g.ident,
_ => panic!(),
};
quote! { ::std::marker::PhantomData::<#g>, }
})
.fold(quote!(), |old, new| {
quote! { #old #new }
});

let mut punctuated_generics = Punctuated::<_, Token![,]>::new();
punctuated_generics.extend(lifetimeless_generics.iter());

Expand All @@ -439,30 +431,58 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
}));

let struct_name = &ast.ident;
let fetch_struct_name = Ident::new(&format!("Fetch{}", struct_name), Span::call_site());
let config_struct_name = Ident::new(&format!("{}Config", struct_name), Span::call_site());
let state_struct_name = Ident::new(&format!("{}State", struct_name), Span::call_site());

let output = quote! {
// This construction, as strange as it may seem,
// is to force the config struct to be 'static.
#[allow(non_camel_case_types)]
pub struct #config_struct_name<#(#fields,)*>{
#(pub #fields: #fields,)*
}
#[allow(non_camel_case_types)]
pub struct #state_struct_name<#(#fields,)* #punctuated_generic_idents>{
#(#fields: #fields,)*
_phantom: std::marker::PhantomData<fn()->(#punctuated_generic_idents)>
}

TokenStream::from(quote! {
pub struct #fetch_struct_name<#punctuated_generics>(#phantoms);
impl #impl_generics #path::SystemParam for #struct_name#ty_generics #where_clause {
type Fetch = #fetch_struct_name <#punctuated_generic_idents>;
}
type Config = #config_struct_name<#(<#field_types as #path::SystemParam>::Config,)*>;
type State = #state_struct_name<#(<#field_types as #path::SystemParam>::State,)* #punctuated_generic_idents>;
fn default_config() -> Self::Config {
#config_struct_name {
#(#fields: <#field_types as #path::SystemParam>::default_config(),)*
}
}

impl #impl_generics #path::FetchSystemParam<'a> for #fetch_struct_name<#punctuated_generic_idents> {
fn create_state(config: Self::Config) -> Self::State {
#state_struct_name {
#(#fields: <#field_types as #path::SystemParam>::create_state(config.#fields),)*
_phantom: std::marker::PhantomData
}
}
}
#[allow(non_camel_case_types)]
impl <'a, #(#fields: #path::ParamState<'a, Item=#field_types>,)* #punctuated_generics> #path::ParamState<'a> for #state_struct_name<#(#fields,)* #punctuated_generic_idents> {
type Item = #struct_name#ty_generics;
fn init(system_state: &mut #path::SystemState, world: &#path::World, resources: &mut #path::Resources) {
#(<<#field_types as SystemParam>::Fetch as #path::FetchSystemParam>::init(system_state, world, resources);)*
fn init(&mut self, system_state: &mut #path::SystemState, world: &#path::World, resources: &mut #path::Resources) {
#(self.#fields.init(system_state, world, resources);)*
}

unsafe fn get_param(
&'a mut self,
system_state: &'a #path::SystemState,
world: &'a #path::World,
resources: &'a #path::Resources,
) -> Option<Self::Item> {
Some(#struct_name {
#(#fields: <<#field_types as SystemParam>::Fetch as #path::FetchSystemParam>::get_param(system_state, world, resources)?,)*
#(#ignored_fields: <#ignored_field_types>::default(),)*
#(#fields: self.#fields.get_param(system_state, world, resources)?,)*
#(#ignored_fields: Default::default(),)*
})
}
}
})
};
// println!("{}", output);
TokenStream::from(output)
}
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/core/filter.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{core::ComponentFlags, Archetype, Bundle, Component, QueryAccess};
use std::{any::TypeId, marker::PhantomData, ptr::NonNull};

pub trait QueryFilter: Sized {
pub trait QueryFilter: Sized + 'static {
type EntityFilter: EntityFilter;
fn access() -> QueryAccess;
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter>;
Expand Down Expand Up @@ -164,7 +164,7 @@ impl<T: Component> QueryFilter for With<T> {

pub struct WithType<T: Bundle>(PhantomData<T>);

impl<T: Bundle> QueryFilter for WithType<T> {
impl<T: Bundle + 'static> QueryFilter for WithType<T> {
type EntityFilter = AnyEntityFilter;

fn access() -> QueryAccess {
Expand Down
9 changes: 8 additions & 1 deletion crates/bevy_ecs/src/core/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ pub trait WorldQuery {
pub unsafe trait ReadOnlyFetch {}

/// Streaming iterators over contiguous homogeneous ranges of components
pub trait Fetch<'a>: Sized {
pub trait Fetch<'a>: Sized + 'static {
/// Type of value to be fetched
type Item;
type Query: WorldQuery;

/// A value on which `get` may never be called
#[allow(clippy::declare_interior_mutable_const)] // no const fn in traits
Expand Down Expand Up @@ -70,6 +71,7 @@ impl WorldQuery for Entity {

impl<'a> Fetch<'a> for EntityFetch {
type Item = Entity;
type Query = Entity;

const DANGLING: Self = Self(NonNull::dangling());

Expand Down Expand Up @@ -102,6 +104,7 @@ unsafe impl<T> ReadOnlyFetch for FetchRead<T> {}

impl<'a, T: Component> Fetch<'a> for FetchRead<T> {
type Item = &'a T;
type Query = &'a T;

const DANGLING: Self = Self(NonNull::dangling());

Expand Down Expand Up @@ -230,6 +233,7 @@ pub struct FetchMut<T>(NonNull<T>, NonNull<ComponentFlags>);

impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
type Item = Mut<'a, T>;
type Query = &'a mut T;

const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());

Expand Down Expand Up @@ -264,6 +268,7 @@ unsafe impl<T> ReadOnlyFetch for TryFetch<T> where T: ReadOnlyFetch {}

impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
type Item = Option<T::Item>;
type Query = Option<T::Query>;

const DANGLING: Self = Self(None);

Expand All @@ -287,6 +292,7 @@ unsafe impl<T> ReadOnlyFetch for FlagsFetch<T> {}

impl<'a, T: Component> Fetch<'a> for FlagsFetch<T> {
type Item = Flags<T>;
type Query = Flags<T>;

const DANGLING: Self = Self(None, PhantomData::<T>);

Expand Down Expand Up @@ -521,6 +527,7 @@ macro_rules! tuple_impl {
($($name: ident),*) => {
impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) {
type Item = ($($name::Item,)*);
type Query = ($($name::Query,)*);
const DANGLING: Self = ($($name::DANGLING,)*);

#[allow(unused_variables, unused_mut)]
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ pub use system::{Query, *};
pub mod prelude {
pub use crate::{
core::WorldBuilderSource,
resource::{ChangedRes, FromResources, Local, Res, ResMut, Resource, Resources},
resource::{ChangedRes, FromResources, Res, ResMut, Resource, Resources},
schedule::{Schedule, State, StateStage, SystemStage},
system::{Commands, IntoSystem, Query, System},
Added, Bundle, Changed, Component, Entity, Flags, In, IntoChainSystem, Mut, Mutated, Or,
QuerySet, Ref, RefMut, With, Without, World,
system::{AsSystem, Commands, IntoSystem, Local, Query, System},
Added, Bundle, Changed, Component, Entity, Flags, In, IntoChainSystem, Mutated, Or,
QuerySet, With, Without, World,
};
}
36 changes: 1 addition & 35 deletions crates/bevy_ecs/src/resource/resource_query.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::FromResources;
use crate::{Resource, ResourceIndex, Resources, SystemId};
use crate::Resource;
use std::{
marker::PhantomData,
ops::{Deref, DerefMut},
Expand Down Expand Up @@ -100,39 +99,6 @@ impl<'a, T: Resource> DerefMut for ResMut<'a, T> {
}
}

/// Local<T> resources are unique per-system. Two instances of the same system will each have their own resource.
/// Local resources are automatically initialized using the FromResources trait.
#[derive(Debug)]
pub struct Local<'a, T: Resource + FromResources> {
value: *mut T,
_marker: PhantomData<&'a T>,
}

impl<'a, T: Resource + FromResources> Local<'a, T> {
pub(crate) unsafe fn new(resources: &Resources, id: SystemId) -> Self {
Local {
value: resources
.get_unsafe_ref::<T>(ResourceIndex::System(id))
.as_ptr(),
_marker: Default::default(),
}
}
}

impl<'a, T: Resource + FromResources> Deref for Local<'a, T> {
type Target = T;

fn deref(&self) -> &T {
unsafe { &*self.value }
}
}

impl<'a, T: Resource + FromResources> DerefMut for Local<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.value }
}
}

// #[cfg(test)]
// mod tests {
// use super::*;
Expand Down
Loading