Skip to content

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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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