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

Safer dynamic event handling for observers #14919

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

ItsDoot
Copy link
Contributor

@ItsDoot ItsDoot commented Aug 26, 2024

Objective

This is a more scoped version of #14674, with a removal of the "tuple of event types" logic, which is notably controversial. Closes #14649.

Solution

This PR introduces an expansion of trigger-able event data into something slightly more Query-like.
With this, we can safely implement observers that can be triggered with multiple different event types, which don't necessarily need to access the event data. This new type is called DynamicEvent:

// Get our event component IDs somehow. They don't need to be statically known!
let foo_id = world.init_component::<Foo>();
let bar_id = world.init_component::<Bar>();

world.spawn(
    Observer::new(|trigger: Trigger<DynamicEvent> {
        // Called for foo and bar events
        let ptr_mut = trigger.event_mut();
        // If you need to access the event data, you can via the PtrMut returned above.
    })
    .with_event(foo_id) // This method was previously unsafe because it could result in UB.
    .with_event(bar_id) // But we don't do any casting automatically here, so we're safe! (It's on the user's side to do that)
);
// Trigger our runtime-known events dynamically:
unsafe {
    EmitDynamicTrigger::new_with_id(foo_id, Foo(10), ()).apply(&mut world);
    EmitDynamicTrigger::new_with_id(bar_id, Bar(false), ()).apply(&mut world);
}

Testing

  • A new test has been added.
  • A new benchmark has been added.

Migration Guide

Observer::with_event was renamed to Observer::with_event_unchecked. If you were using this function, consider using a new one that has been added under the old name, which isn't marked unsafe but requires your Trigger<E>'s E type to be DynamicEvent.

@alice-i-cecile alice-i-cecile added this to the 0.15 milestone Aug 26, 2024
@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Aug 26, 2024
@ItsDoot ItsDoot force-pushed the feat/observer-dynamic-event branch from c760845 to a233696 Compare August 26, 2024 01:09
@ItsDoot ItsDoot added M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide D-Unsafe Touches with unsafe code in some way D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes labels Aug 26, 2024
Copy link
Contributor

@NthTensor NthTensor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cursory look over, but will do a full review at some point. Approach is very clever, I am very surprised this works and is compatible with propagation. Nice work.

There is still no way to safely access the actual instantiation of a DynamicEvent? Was that in the original? Have we considered returning a Box<dyn Reflect>?

@ItsDoot
Copy link
Contributor Author

ItsDoot commented Aug 26, 2024

There is still no way to safely access the actual instantiation of a DynamicEvent? Was that in the original? Have we considered returning a Box<dyn Reflect>?

I'm working on adding a separate ReflectEvent type that returns an &mut dyn Reflect. It's not much more code to include it in this PR, if that would help sell it overall.

@NthTensor
Copy link
Contributor

NthTensor commented Aug 26, 2024

It's not much more code to include it in this PR, if that would help sell it overall.

Cool! Nah, keep it separate imo. This is complete. I will approve as long as it does what it says on the tin.

Comment on lines +319 to +320
// SAFETY: DynamicEvent itself does not perform any unsafe operations (like casting), so this is safe.
unsafe { self.with_event_unchecked(event) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This safety comment is not entirely clear to me. It doesn't seem to address the safety requirements of the with_event_unchecked function:

# Safety
The type of the `event` [`ComponentId`] _must_ match the actual value
of the event passed into the observer system.

In your safety comment there's no mention of the event ComponentId or the type of the actual event that will be passed into the observer system.

I guess this is safe because DynamicEvent can "match" any event type? If that's the case I would like to see clearer safety requirements from with_event_unchecked that allow for this situation, and a better safety comment here that addresses the requirements from with_event_unchecked.

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Oct 8, 2024
@alice-i-cecile
Copy link
Member

@ItsDoot I quite like this, but it's not vital to get this in for 0.15 so I'm bumping to 0.16. Let me know when you've resolved merge conflicts here though and I'll give you a proper review.

@alice-i-cecile alice-i-cecile modified the milestones: 0.15, 0.16 Oct 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes D-Unsafe Touches with unsafe code in some way M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged
Projects
Status: Active: bevy_mod_picking upstreaming
Development

Successfully merging this pull request may close these issues.

Observers that can observe multiple different event types
4 participants