Skip to content

Commit

Permalink
Initial attempt
Browse files Browse the repository at this point in the history
  • Loading branch information
grace125 committed Oct 28, 2024
1 parent fb7b3e0 commit b5f13d3
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 7 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3852,3 +3852,8 @@ doc-scrape-examples = true

[package.metadata.example.testbed_3d]
hidden = true

[[example]]
name = "playground"
path = "examples/playground.rs"
doc-scrape-examples = false
6 changes: 6 additions & 0 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ bitflags::bitflags! {
const ON_INSERT_OBSERVER = (1 << 5);
const ON_REPLACE_OBSERVER = (1 << 6);
const ON_REMOVE_OBSERVER = (1 << 7);
const ON_MUTATE_OBSERVER = (1 << 8);
}
}

Expand Down Expand Up @@ -697,6 +698,11 @@ impl Archetype {
pub fn has_remove_observer(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_REMOVE_OBSERVER)
}

#[inline]
pub fn has_mutate_observer(&self) -> bool {
self.flags().contains(ArchetypeFlags::ON_MUTATE_OBSERVER)
}
}

/// The next [`ArchetypeId`] in an [`Archetypes`] collection.
Expand Down
23 changes: 22 additions & 1 deletion crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
prelude::World,
query::DebugCheckedUnwrap,
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
world::{unsafe_world_cell::UnsafeWorldCell, ON_ADD, ON_INSERT, ON_REPLACE},
world::{unsafe_world_cell::UnsafeWorldCell, ON_ADD, ON_INSERT, ON_MUTATE, ON_REPLACE},
};
use bevy_ptr::{ConstNonNull, OwningPtr};
use bevy_utils::{all_tuples, HashMap, HashSet, TypeIdMap};
Expand Down Expand Up @@ -1093,6 +1093,13 @@ impl<'w> BundleInserter<'w> {
add_bundle.iter_inserted(),
);
}
if new_archetype.has_mutate_observer() {
deferred_world.trigger_observers(
ON_MUTATE,
entity,
add_bundle.iter_inserted(),
);
}
}
InsertMode::Keep => {
// insert triggers only for new components if we're not replacing them (since
Expand All @@ -1109,6 +1116,13 @@ impl<'w> BundleInserter<'w> {
add_bundle.iter_added(),
);
}
if new_archetype.has_mutate_observer() {
deferred_world.trigger_observers(
ON_MUTATE,
entity,
add_bundle.iter_added(),
);
}
}
}
}
Expand Down Expand Up @@ -1249,6 +1263,13 @@ impl<'w> BundleSpawner<'w> {
bundle_info.iter_contributed_components(),
);
}
if archetype.has_mutate_observer() {
deferred_world.trigger_observers(
ON_MUTATE,
entity,
bundle_info.iter_contributed_components(),
);
}
};

location
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub mod prelude {
},
world::{
Command, EntityMut, EntityRef, EntityWorldMut, FilteredResources, FilteredResourcesMut,
FromWorld, OnAdd, OnInsert, OnRemove, OnReplace, World,
FromWorld, OnAdd, OnInsert, OnRemove, OnReplace, OnMutate, World,
},
};

Expand Down
13 changes: 13 additions & 0 deletions crates/bevy_ecs/src/observer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ pub struct Observers {
on_insert: CachedObservers,
on_replace: CachedObservers,
on_remove: CachedObservers,
on_mutate: CachedObservers,
// Map from trigger type to set of observers
cache: HashMap<ComponentId, CachedObservers>,
}
Expand All @@ -258,6 +259,7 @@ impl Observers {
ON_INSERT => &mut self.on_insert,
ON_REPLACE => &mut self.on_replace,
ON_REMOVE => &mut self.on_remove,
ON_MUTATE => &mut self.on_mutate,
_ => self.cache.entry(event_type).or_default(),
}
}
Expand All @@ -268,6 +270,7 @@ impl Observers {
ON_INSERT => Some(&self.on_insert),
ON_REPLACE => Some(&self.on_replace),
ON_REMOVE => Some(&self.on_remove),
ON_MUTATE => Some(&self.on_mutate),
_ => self.cache.get(&event_type),
}
}
Expand Down Expand Up @@ -342,6 +345,7 @@ impl Observers {
ON_INSERT => Some(ArchetypeFlags::ON_INSERT_OBSERVER),
ON_REPLACE => Some(ArchetypeFlags::ON_REPLACE_OBSERVER),
ON_REMOVE => Some(ArchetypeFlags::ON_REMOVE_OBSERVER),
ON_MUTATE => Some(ArchetypeFlags::ON_MUTATE_OBSERVER),
_ => None,
}
}
Expand Down Expand Up @@ -378,6 +382,14 @@ impl Observers {
{
flags.insert(ArchetypeFlags::ON_REMOVE_OBSERVER);
}

if self
.on_mutate
.component_observers
.contains_key(&component_id)
{
flags.insert(ArchetypeFlags::ON_MUTATE_OBSERVER);
}
}
}

Expand Down Expand Up @@ -565,6 +577,7 @@ impl World {
}
}

// TODO GRACE: write tests here
#[cfg(test)]
mod tests {
use alloc::vec;
Expand Down
67 changes: 62 additions & 5 deletions crates/bevy_ecs/src/system/system_param.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
pub use crate::change_detection::{NonSendMut, Res, ResMut};
use crate::{
archetype::{Archetype, Archetypes},
archetype::{self, Archetype, Archetypes},
bundle::Bundles,
change_detection::{Ticks, TicksMut},
component::{ComponentId, ComponentTicks, Components, Tick},
component::{self, ComponentId, ComponentTicks, Components, Tick},
entity::Entities,
query::{
Access, FilteredAccess, FilteredAccessSet, QueryData, QueryFilter, QuerySingleError,
Expand All @@ -12,8 +12,7 @@ use crate::{
storage::ResourceData,
system::{Query, Single, SystemMeta},
world::{
unsafe_world_cell::UnsafeWorldCell, DeferredWorld, FilteredResources, FilteredResourcesMut,
FromWorld, World,
unsafe_world_cell::UnsafeWorldCell, DeferredWorld, FilteredResources, FilteredResourcesMut, FromWorld, OnMutate, World, ON_MUTATE
},
};
use bevy_ecs_macros::impl_param_set;
Expand All @@ -29,7 +28,7 @@ use core::{
ops::{Deref, DerefMut},
};

use super::Populated;
use super::{IntoSystem, Populated};

/// A parameter that can be used in a [`System`](super::System).
///
Expand Down Expand Up @@ -320,6 +319,64 @@ unsafe impl<D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Qu
// world data that the query needs.
unsafe { Query::new(world, state, system_meta.last_run, change_tick) }
}

fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
let mut vec = Vec::new();
for archetype_id in state.matched_archetypes() {
let Some(archetype) = world.archetypes().get(archetype_id) else { continue; };
if !archetype.has_mutate_observer() { continue; }
// println!("{:?}", archetype_id);
let (writes, cant_write) = system_meta.component_access_set.combined_access().component_writes();
let writes = if cant_write { vec![] } else { writes.collect() };
// println!("{:?}", writes);

for archetype_entity in archetype.entities() {
let entity = archetype_entity.id();
let entity_ref = world.entity(entity);
let writes_here = writes.iter().cloned().filter(|c|
entity_ref
.get_change_ticks_by_id(*c)
.map(|ticks| {
// println!("{:?}", ticks);
ticks.changed.get() >= system_meta.last_run.get()
}) // FIXME GRACE: get this_run
.unwrap_or(false)
).collect::<Vec<_>>();
vec.push((entity, writes_here));
}
}

let mut deferred_world = DeferredWorld::from(world);
for (e, modified_components) in vec {
unsafe {
deferred_world.trigger_observers(ON_MUTATE, e, modified_components.iter().cloned());
}
}
}

// fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {



// for archetype_id in state.matched_archetypes() {
// let Some(archetype) = world.archetypes().get(archetype_id) else { continue; };
// if !archetype.has_mutate_observer() { continue; }
// let (writes, are_writes) = system_meta.archetype_component_access.component_writes();
// if are_writes {
// for comp in writes {

// }
// }
// for archetype_entity in archetype.entities() {

// let entity = archetype_entity.id();
// for component in archetype.components() {

// }
// }
// }
// // world.commands().trigger_targets(OnMutate, targets);
// }
}

pub(crate) fn init_query_param<D: QueryData + 'static, F: QueryFilter + 'static>(
Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_ecs/src/world/component_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub const ON_INSERT: ComponentId = ComponentId::new(1);
pub const ON_REPLACE: ComponentId = ComponentId::new(2);
/// [`ComponentId`] for [`OnRemove`]
pub const ON_REMOVE: ComponentId = ComponentId::new(3);
/// [`ComponentId`] for [`OnMutate`]
pub const ON_MUTATE: ComponentId = ComponentId::new(4);

/// Trigger emitted when a component is added to an entity. See [`crate::component::ComponentHooks::on_add`]
/// for more information.
Expand Down Expand Up @@ -41,3 +43,9 @@ pub struct OnReplace;
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
pub struct OnRemove;

/// Trigger emitted when a component is mutated.
#[derive(Event, Debug)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
pub struct OnMutate;
1 change: 1 addition & 0 deletions crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ impl World {
assert_eq!(ON_INSERT, self.register_component::<OnInsert>());
assert_eq!(ON_REPLACE, self.register_component::<OnReplace>());
assert_eq!(ON_REMOVE, self.register_component::<OnRemove>());
assert_eq!(ON_MUTATE, self.register_component::<OnMutate>());
}
/// Creates a new empty [`World`].
///
Expand Down
55 changes: 55 additions & 0 deletions examples/playground.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

#![allow(missing_docs)]

use bevy::prelude::*;

// TODO GRACE: remove this file

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, on_space_press)
.add_observer(on_a_mutated)
.run();
}

#[derive(Component)]
struct A(i32);

fn setup(mut commands: Commands) {
commands.spawn(A(0));
}

fn on_space_press(input: Res<ButtonInput<KeyCode>>, mut query: Query<&mut A>) {
if input.just_pressed(KeyCode::Space) {
for mut a in query.iter_mut() {
println!("asdfjkaskldfjlksadfljsdfk");
a.0 = 100;
}
}
}

fn on_a_mutated(trigger: Trigger<OnMutate, A>) {
println!("A mutation happened!");
}


// fn main() {
// App::new()
// .add_plugins(DefaultPlugins)
// .add_systems(Startup, setup)
// .add_observer(on_a_added)
// .run();
// }

// #[derive(Component)]
// struct A(i32);

// fn setup(mut commands: Commands) {
// commands.spawn_empty().insert(A(0)).insert(A(1));
// }

// fn on_a_added(trigger: Trigger<OnAdd, A>) {
// println!("Abafjgklasdfgjp;kl")
// }

0 comments on commit b5f13d3

Please sign in to comment.