fishfolk / bones

An easy-to-use game engine for making real games.
https://fishfolk.org/development/bones/introduction/
Other
236 stars 20 forks source link

Panic: if `get_resource` is called (provides None if missing), then `init_resource`, get a panic #488

Open MaxCWhitehead opened 2 days ago

MaxCWhitehead commented 2 days ago

world.get_resource does this:

    #[track_caller]
    pub fn get<T: HasSchema>(&self) -> Option<Ref<T>> {
        let b = self.untyped.get(T::schema()).borrow();
        if b.is_some() {
            Some(Ref::map(b, |b| unsafe {
                b.as_ref().unwrap().as_ref().cast_into_unchecked()
            }))
        } else {
            None
        }
    }

self.untyped.get() This inserts the the schema id into OnceMap, with contents being empty.

init_resource does:

    pub fn init_resource<R: HasSchema + FromWorld>(&mut self) -> RefMut<R> {
        if unlikely(!self.resources.contains::<R>()) {
            let value = R::from_world(self);
            self.resources.insert(value);
        }
        self.resource_mut()
    }

and contains only checks if ID is contained - not if resource has a value. This skips the init branch, and only calls self.resource_mut which panics on missing resource value.

This is the only place in bones that contains is used, I think we should update this to check if the resource has a value, and not just ID entry in map. This is what I would expect when using the function as well.

MaxCWhitehead commented 2 days ago

Seems to fix if change Resources::contains to do:

    pub fn contains<T: HasSchema>(&self) -> bool {
        self.untyped.contains(T::schema().id())
    }

instead of:

    pub fn contains<T: HasSchema>(&self) -> bool {
        self.untyped.resources.contains_key(&T::schema().id())
    }