Skip to content

Implement world.trigger_event remote method#21798

Merged
mockersf merged 7 commits intobevyengine:mainfrom
Lancelotbronner:feature/remote_trigger_event
Dec 8, 2025
Merged

Implement world.trigger_event remote method#21798
mockersf merged 7 commits intobevyengine:mainfrom
Lancelotbronner:feature/remote_trigger_event

Conversation

@Lancelotbronner
Copy link
Contributor

@Lancelotbronner Lancelotbronner commented Nov 10, 2025

Objective

Tools using bevy_remote will be able to identify (via the schema) and trigger events.

  • Document bevy_ecs/src/reflect/event.rs

Solution

I've added a method world.trigger_event, added Event to the schema's reflection metadata and ReflectEvent to allow this.

Testing

I have copied the (tested) code from my game but have NOT tested this branch yet.
I am new to Rust/Cargo and need to go to sleep now, I'll figure this out and test it tomorrow.


Showcase

Here's what I needed to add to my game in order to allow my editor to access and trigger an event:

#[derive(Event, Reflect)]
#[reflect(Event)]
pub struct AssignToRoute {
    pub vehicle: Entity,
    pub route: Entity,
    pub origin: Entity,
}

Here's a screenshot of my editor using this feature:

Screenshot 2025-11-10 at 1 40 42 AM

@github-actions
Copy link
Contributor

Welcome, new contributor!

Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨

@Lancelotbronner Lancelotbronner marked this pull request as draft November 10, 2025 06:58
@ChristopherBiscardi
Copy link
Contributor

gave it a brief look. What happens if you use an EntityEvent?

Otherwise generally seems reasonable and follows the pattern for other such types.

@Lancelotbronner
Copy link
Contributor Author

gave it a brief look. What happens if you use an EntityEvent?

Otherwise generally seems reasonable and follows the pattern for other such types.

#[derive(EntityEvent, Reflect)]
#[reflect(Event)]
pub struct Explode(pub Entity);

commands.spawn(Name::new("WontExplode".to_string()));
commands.spawn(Name::new("WillExplode".to_string())).observe(
  |event: On<Explode>, mut commands: Commands| {
    println!("Boom!");
    commands.entity(event.event_target()).despawn();
  },
);

Sending the event for both entities only explodes "WillExplode", so I guess EntityEvent also works!

@Lancelotbronner
Copy link
Contributor Author

I'm trying to pass CI locally but I keep getting error: associated function new is never used on the following:

impl ReflectEventFns {
    /// Get the default set of [`ReflectEventFns`] for a specific event type using its
    /// [`FromType`] implementation.
    ///
    /// This is useful if you want to start with the default implementation before overriding some
    /// of the functions to create a custom implementation.
    pub fn new<'a, T: Event + FromReflect + TypePath>() -> Self
    where
        T::Trigger<'a>: Default,
    {
        <ReflectEvent as FromType<T>>::from_type().0
    }
}

It's right but doesn't warn on the equivalent methods for components, resources, bundles, etc. and they have no usage either according to my IDE.

Anything I'm missing? I don't want to allow(dead_code) either since they don't have it but I can't figure out where they're supposed to be used.

@Lancelotbronner Lancelotbronner marked this pull request as ready for review November 10, 2025 15:56
@hymm
Copy link
Contributor

hymm commented Nov 10, 2025

It's probably because ReflectEventFns is not being exported to the bevy_ecs level.

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-Dev-Tools Tools used to debug Bevy applications. S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged X-Uncontroversial This work is generally agreed upon labels Nov 10, 2025
@alice-i-cecile
Copy link
Member

Very sensible idea. Do we have pre-existing tests for this module? If so, we should be testing this too. If not, well, that can wait for follow-up.

@Lancelotbronner
Copy link
Contributor Author

Lancelotbronner commented Nov 11, 2025

Added a test for bevy_remote, we sorta didn't but now we have at least one testing the happy path.

I'm also trying to write a test for bevy_ecs but I can't figure out how to satisfy the borrow checker.
Can we consider the bevy_remote one an integration test for both?

Here's what I had for bevy_ecs so far:

#[derive(EntityEvent, Reflect)]
#[reflect(Event)]
struct Explode(pub Entity);

#[derive(Component)]
struct DespawnOnExplode;

#[test]
fn trigger_event() {
    let mut world = World::new();

    let type_registry = AppTypeRegistry::default();
    {
        let mut registry = type_registry.write();
        registry.register::<Explode>();
        registry.register_type_data::<Explode, ReflectEvent>();
    }
    world.insert_resource(type_registry);

    let mut system_state: SystemState<Commands> = SystemState::new(&mut world);
    let mut commands = system_state.get_mut(&mut world);

    let entity = commands
        .spawn_empty()
        .observe(|event: On<Explode>, mut commands: Commands| {
            commands.entity(event.0).despawn()
        })
        .id();
    let entity2 = commands.spawn(DespawnOnExplode).id();

    commands.add_observer(
        |event: On<Explode>,
         entities: Query<Entity, With<DespawnOnExplode>>,
         mut commands: Commands| {
            for entity in entities.iter() {
                commands.entity(entity).despawn()
            }
        },
    );

    let mut reflect_event = DynamicTupleStruct::default();
    reflect_event.insert(entity.to_bits());
    {
        let registry = *world.resource::<AppTypeRegistry>().write();
        let event =
            from_reflect_with_fallback::<Explode>(&reflect_event, &mut world, &registry);
        world.trigger(event);
    }

    assert!(world.get_entity(entity).is_err());
    assert!(world.get_entity(entity2).is_err());
}

@Lancelotbronner
Copy link
Contributor Author

Lancelotbronner commented Nov 14, 2025

So my local CI runs keep flagging unrelated code so I can't verify locally and now the actions CI will fail if I'm missing a use for documentation and fails if I add it because I'm not using it in code...

Also why are there like 3 different actions (build, check-doc, ci) that all build separately? Isn't that a bit wasteful?

@alice-i-cecile
Copy link
Member

So my local CI runs keep flagging unrelated code so I can't verify locally and now the actions CI will fail if I'm missing a use for documentation and fails if I add it because I'm not using it in code...

You can add the link in-line, using e.g. [Schedule](bevy::ecs::prelude::Schedule) :)

Also why are there like 3 different actions (build, check-doc, ci) that all build separately? Isn't that a bit wasteful?

In some ways! Generally our CI is limited by cache size and length of the longest job, not the number of runners. Some of the duplicated builds use slightly different settings as well. Not saying that it's perfect, but just giving you a bit of background.

@Lancelotbronner
Copy link
Contributor Author

Thanks! I might be able to get it to pass CI now! Still wondering what I did wrong for local CI to fail on latest main so I'm going off the remote CI's feedback

@alice-i-cecile
Copy link
Member

Thanks! I might be able to get it to pass CI now! Still wondering what I did wrong for local CI to fail on latest main so I'm going off the remote CI's feedback

Yep, that's totally fine. I sometimes do this myself when it's acting up or I'm feeling lazy.

@alice-i-cecile alice-i-cecile added S-Needs-Review Needs reviewer attention (from anyone!) to move forward and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Nov 20, 2025
@alice-i-cecile alice-i-cecile added this to the 0.18 milestone Nov 20, 2025
Copy link
Contributor

@AlephCubed AlephCubed left a comment

Choose a reason for hiding this comment

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

I don't know much about reflection, but this seems pretty straightforward/makes logical sense.

@alice-i-cecile alice-i-cecile removed the S-Needs-Review Needs reviewer attention (from anyone!) to move forward label Nov 30, 2025
@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Nov 30, 2025
@mockersf mockersf added this pull request to the merge queue Dec 8, 2025
Merged via the queue into bevyengine:main with commit 0af6fc1 Dec 8, 2025
40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Dev-Tools Tools used to debug Bevy applications. C-Feature A new feature, making something new possible S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Uncontroversial This work is generally agreed upon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants