#Determining whether an app already has a system added

24 messages ยท Page 1 of 1 (latest)

mental heart
#

Hey all, I am trying to write a simple function to check if an app already has a system added, but for the life of me cannot get the default schedule from a reference to the app:

My code:

fn app_contains_system(app: &App, system: &SystemAppConfig) -> bool {
    let schedules = app.world.resource::<Schedules>();
    let schedule_label = &*app.default_schedule_label;

    if let Some(default_schedule) = schedules.get(schedule_label) {
        return default_schedule
            .graph()
            .systems()
            .find(|s| s.type_id() == system.type_id())
            .is_some();
    } else {
        panic!("Default schedule does not exist.");
    }
}

This throws the error:

    |
144 | fn app_contains_system(app: &App, system: &SystemAppConfig) -> bool {
    |                        ---  - let's call the lifetime of this reference `'1`
    |                        |
    |                        `app` is a reference that is only valid in the function body
...
148 |     if let Some(default_schedule) = schedules.get(schedule_label) {
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                                     |
    |                                     `app` escapes the function body here
    |                                     argument requires that `'1` must outlive `'static`

Does anyone know how I would be able to get around this?

mental heart
#

I tried deconstructing things a bit to see if I could figure out where it is going wrong:

fn app_contains_system(app: &mut App, system: &SystemAppConfig) -> bool {
    let mut schedules = app.world.resource_mut::<Schedules>();
    let schedule_label = &*app.default_schedule_label;
    let default_schedule = schedules.remove(schedule_label);

    if default_schedule.is_none() {
        panic!("Default schedule does not exist.");
    }

    let default_schedule = default_schedule.unwrap();

    let val = {
        let graph = default_schedule.graph();
        graph
            .systems()
            .find(|s| s.type_id() == system.type_id())
            .is_some()
    };

    schedules.insert(app.default_schedule_label.clone(), default_schedule);

    val
}

This now gives

153 |     let default_schedule = default_schedule.unwrap();
    |         ---------------- binding `default_schedule` declared here
...
156 |         let graph = default_schedule.graph();
    |                     ------------------------
    |                     |
    |                     borrow of `default_schedule` occurs here
    |                     argument requires that `default_schedule` is borrowed for `'static`

Why does graph() require a static lifetime, I can't figure it out? Is there anyway round this?

mental heart
#

Determining whether an app already has a system added

drowsy peak
#

not sure the cause, but try

app.world.resource_scope::<Schedules, _>(|world, mut schedules| { ... });
#

that might be for solving a different problem though ๐Ÿ˜…

mental heart
#

yeah I did try that as well as sometimes it seems to just fix things, but not here unfortunately, the .graph() call causes a fuss regardless

gleaming steppe
mental heart
#

wow that's some out of the box thinking

gleaming steppe
#

or, if you provide the systems, only offer them through a method with an internal state if it was called before using an static atomic bool and make that information be returned along with the system

#

both super hacky I guess ๐Ÿ˜…

mental heart
#

you might be onto something you know, does rust have any form of (I hate to say these words) global variable? Actually you've given me loads of inspiration thanks

#

I just need to be able to access and store state on the app! I can make a resource which just has HashSet<usize> of added system pointers

gleaming steppe
#

global state is a no no and requires unsafe, static atomics are maybe the closest thing but I would only have them in a small scope so you can be sure it is not used elsewhere

mental heart
#

yeah it's a stupid idea, but I have mut access to the app, so I can just use a resource

gleaming steppe
#

yeah if you have world access that works the best

#

static atomics are maybe more for otherwise stateless methods that have no other way to store information

#

again that should be the last resort

#

the first solution might be better I think, if that works

mental heart
#

I think it worked just running my tests ๐Ÿ™‚

gleaming steppe
#

you can even seal everything in that function (semi pseudo code, not sure if that return type works)

fn get_system_and_if_firstly(&mut world) -> (impl System, bool){
  #[derive(Resource, Default)]
  struct WasAdded;

  let was_added = world.contains_resource::<WasAdded>();
  world.init_resource::<WasAdded>();
  
  let system = |foo: Res<Foo>, bar: Query<&Bar>|{
    //your system
  };
  (system, was_added)
}
#

you could also return a command along the other two things that removes the resource to be super fancy

#

or take &mut App and add the command yourself

mental heart
#
        {
            let mut added_systems = app
                .world
                .remove_resource::<AddedSystemTracker>()
                .unwrap_or_else(|| {
                    panic!("Make sure the plugin is added to the app before calls to DefineAI")
                });

            app.add_system(ensure_entity_has_ai_meta::<T>);

            // Add utility systems
            for decision in &mut self.decisions {
                decision
                    .simple_considerations
                    .iter_mut()
                    .chain(decision.targeted_considerations.iter_mut())
                    .chain(decision.targeted_filter_considerations.iter_mut())
                    .for_each(|c| {
                        let system_app_config = c.system_app_config.take().unwrap();
                        if !added_systems.systems.contains(&c.input) {
                            app.add_system(
                                system_app_config.in_set(UtililityAISet::CalculateInputs),
                            );
                            added_systems.systems.insert(c.input);
                        }
                    });
            }
            app.add_system(ensure_entity_has_ai_meta::<T>);

            app.world.insert_resource(added_systems);
        }