Skip to content

Easy Query from World (too hard to get a Query<(x,y,z)> without a system) #2687

@tom-leys

Description

@tom-leys

What problem does this solve or what need does it fill?

Given access to a &mut World, I need to run queries, modify / create components and access resources. I can only do this in the context of a system. I'd like something simpler.

I use these for world-to-world messaging, sync with outside stuff (Godot), unit tests and actions for my LockStep implimentation.

What solution would you like?

I'd like to be able to do this:

let  (     mut commands,
            mut client_list,
            mut actor_inputs,
            mut biped,
            mut ships,
        ) : (Commands,
             ResMut<ClientList>,
             Query<&mut ActorInput>,
             Query<&mut Biped>,
             Query<(Entity, &SpaceShip)>) = world.system_data();

another example would be

        for (ship, pos) in world.system_data::<Query<(&SpaceShip, &Pos)>>().into_iter() {
            built_str +=
                format!("{:?} : ({}, {} : {} degrees)\n", ship, pos.x, pos.y, pos.facing).as_str();
        }

I initially thought this is what world.query did. It allowed you to unpack any query that you could have made the argument list of a system. I.e anything with signature SystemParam. This is what Specs does with specs::World::system_data.

It could take a mutable reference to the world and lock the entire world until you return the SystemParam, i.e query(&'a mut self) -> SystemParam<'a... >

What alternative(s) have you considered?

I tried using world.query() but it returns just one query and using it is awkward. You need to keep passing world in. I intitially thought I got a useful bevy_ecs::Query but instead got some QueryState, which is something I don't know how to turn into a Query.

I considered using World::resource_scope but it supports just one resource at a time, and not Queries or components at all.

I tried creating some traits for worlds and queries that made them easier to use, but I can't make a method in world that returns an easy to use Iter without storing a reference to world in the iter and ... it started getting too hard.

An iterator in world would allow this (with a custom iterator)

for (ship, pos) in world.query_iter::<(&SpaceShip, &Pos)>(){
    // ...
}

I tried making short-lived systems to wrap my code which needed queries, but that requires gymnastics to pass in &self since &self is not 'static. I used rc::Rc and rc::Weak to get around that:

impl ExecutesAction for AdjustFloor {
    type UndoData = AdjustFloorUndo;

    fn apply(&self, world: &mut World) -> Result<AdjustFloorUndo, ActionResult> {
        let undo = AdjustFloorUndo {
            area: self.area,
            flags: self.flags,
            prev_floor: None, // world.get_resource::<Floor>().get_subsection(self.area), // Will need to save previous walls here...
        };

        let self_ptr = Rc::new(*self);
        if self.flag_add() {
            (|this:In<Weak<AdjustFloor>>, mut q: (Commands, ResMut<Floor>, ResMut<ModuleMap>)|
                {
                    let this = this.0.upgrade().unwrap();
                    AdjustFloor::do_add_floors_and_walls(&this, q)
                }).system().run(Rc::downgrade(&self_ptr), world);
        } else {
            (|this:In<Weak<AdjustFloor>>, mut q: (Commands, ResMut<Floor>, ResMut<ModuleMap>)|
                {
                    let this = this.0.upgrade().unwrap();
                    AdjustFloor::do_remove_floors_and_walls(&this, q)
                }).system().run(Rc::downgrade(&self_ptr), world);
        }

        Ok(undo)
    }

In Specs I used to just call do_add_floors_and_walls with a reference to a specs::World and then do this:

    fn do_add_floors_and_walls(&self, world: &mut World) {
        let (mut floor, events, entities, mut modules, sync, mut map) = world.system_data::<(
            Write<Floor>,
            Read<WorldEvents>,
            Entities,
            WriteStorage<ProductionModule>,
            ReadStorage<SyncStatus>,
            Write<ModuleMap>,
        )>();

Additional context

My Game (recent blog) has multiple Worlds, one per spaceship. There is one more world that ties them together, the "Space World"

I'm using multiple words because I want each space-ship to be its own deterministic sim and keeping them separate makes this easier.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ECSEntities, components, systems, and eventsC-UsabilityA targeted quality-of-life change that makes Bevy easier to use

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions