How to add resources in a bundle?

(Nolan) #1

I’m trying to implement my first bundle, and need to add a resource to World. But I’m unclear as to how to do this. It looks like build is only for adding systems, and I’m not immediately sure where or how I can get access to World to add my resource. The docs seem to indicate that adding resources is possible, but where do I go about doing that in a bundle definition?

Thanks.

(Azriel Hoh) #2

There’s two places you can add resources:

  • Inside a System's setup(&mut self, res: &mut Resources) method:

    use specs::prelude::Resources;
    
    #[derive(Default)]
    struct Sys {
        reader: Option<ReaderId<Event>>,
    }
    
    impl<'a> System<'a> for Sys {
        type SystemData = Read<'a, EventChannel<Event>>;
    
        fn run(&mut self, events: Self::SystemData) {
            for event in events.read(&mut self.reader.as_mut().unwrap()) {
                // ..
            }
        }
    
        fn setup(&mut self, res: &mut Resources) {
            use specs::prelude::SystemData;
            Self::SystemData::setup(res);
            self.reader = Some(res.fetch_mut::<EventChannel<Event>>().register_reader());
        }
    }
    
  • Inside a State, usually in on_start():

    fn on_start(&mut self, mut data: StateData<'_, GameData<'_, '_>>) {
        let resource = unimplemented!();
        data.world.add_resource(resource);
    }
    

The former would be closer to “adding a resource from a bundle” – the bundle adds the system, and the system will add the resource to the world when the dispatcher is initialized.

(Nolan) #3

Ah, helpful, thanks! There are a couple places where resources are mentioned in a way implying that bundles can add them–chapter 2 of the Pong tutorial in the repo, and in the comments for SystemBundle. I wonder if those should be tweaked to only mention systems? I suppose a resource can be added in the setup method of systems, but that isn’t really adding it in the bundle then.

Thanks again, I’ll switch this over to a system.

(Azriel Hoh) #4

Oh I see, those comments are out of date. It used to be the case that you could add resources in Bundles, but that changed a while ago; we must’ve missed fixing the docs.

(Nolan) #5

OK, started work on implementing this. What is the point of this line?

Self::SystemData::setup(res);

According to the docs:

Sets up the system data for fetching it from the Resources

Sorry to seem pedantic, but what is “it” here? :slight_smile:
I’m also starting to think that a resource may not work for what I need here. I’ve written a text-to-speech crate which I’m trying to use to provide accessibility/speech feedback, and it doesn’t implement std::marker::Sync. The crate is my own, so maybe I can implement support for this somehow, but it does use FFI and work on multiple platforms, so I suspect it won’t be very easy. Is there another way of creating data in one system and accessing it elsewhere? In theory, the worst that can come of multiple systems sharing access to this TTS instance is that they pre-empt each others speech, which in most cases is desirable anyway.

Thanks for all the help.

(Azriel Hoh) #6

“it” is the SystemData. In terms my (simple) brain could comprehend:

  • Resources can be thought of as “global variables”, stored in a giant hashmap.
  • This “hashmap” is the World. The key of the hashmap is the Resource's type.
  • The SystemData of a System are the resources that the system uses from the World.
  • The Read<'s, Thing> wrapper tells specs that it’s only going to read the resource, Write<'s, Thing> means it may mutate it.
  • When an applicaiton starts up, the World is an empty hashmap.
  • The System#setup(res) method is where you’d go, hashmap.insert(Type, Type::new()).

This would be quite tedious if you had to do it manually for every single field in your SystemData, so the default implementation of System#setup(..) does it for you – it instantiates a Default::default() instance of each type.

If the type is !Default, then you have to insert the resource yourself, e.g. overriding System#setup(..), but that means the automatic setup is no longer called for you, so that’s what Self::SystemData::setup(res); does. Unfortunately this is an anti-pattern in object-oriented programming – call-super.