Skip to main content

pebble/ecs/
resources.rs

1use std::cell::{Cell, RefCell, RefMut};
2
3/// Container for singleton resources stored inside the ECS world.
4///
5/// All resources live on a single hidden entity so they participate in the
6/// same borrow-checking rules as regular components. [`Resources`] is passed
7/// to every system alongside the [`hecs::World`].
8pub struct Resources {
9    pub(crate) resource_entity: hecs::Entity,
10    cmds: RefCell<hecs::CommandBuffer>,
11    generation: Cell<u64>,
12}
13
14impl Resources {
15    /// Create a new `Resources` container, spawning the internal resource entity.
16    pub fn new(world: &mut hecs::World) -> Self {
17        Self {
18            resource_entity: world.spawn(()),
19            cmds: RefCell::new(hecs::CommandBuffer::default()),
20            generation: Cell::new(0),
21        }
22    }
23
24    /// Insert or replace a resource of type `T`.
25    pub fn insert_resource<T>(&mut self, world: &mut hecs::World, res: T)
26    where
27        T: hecs::Component,
28    {
29        world.insert_one(self.resource_entity, res).ok();
30        self.generation.set(self.generation.get() + 1);
31    }
32
33    /// Borrow resource `T`, panicking if it is not present.
34    pub fn get_resource<'a, T>(&self, world: &'a hecs::World) -> hecs::Ref<'a, T>
35    where
36        T: hecs::Component,
37    {
38        world
39            .get::<&T>(self.resource_entity)
40            .unwrap_or_else(|_| panic!("Resource not found: {}", std::any::type_name::<T>()))
41    }
42
43    /// Mutably borrow resource `T`, panicking if it is not present.
44    pub fn get_resource_mut<'a, T>(&self, world: &'a hecs::World) -> hecs::RefMut<'a, T>
45    where
46        T: hecs::Component,
47    {
48        world
49            .get::<&mut T>(self.resource_entity)
50            .unwrap_or_else(|_| panic!("Resource not found: {}", std::any::type_name::<T>()))
51    }
52
53    /// Returns `true` if resource `T` is currently present.
54    pub fn has_resource<T>(&self, world: &hecs::World) -> bool
55    where
56        T: hecs::Component,
57    {
58        if let Ok(_) = world.get::<&T>(self.resource_entity) {
59            return true;
60        }
61
62        false
63    }
64
65    /// Borrow the shared command buffer used to defer world mutations.
66    pub fn get_command_buffer<'a>(&'a self) -> RefMut<'a, hecs::CommandBuffer> {
67        self.cmds.borrow_mut()
68    }
69
70    /// Insert resource `T` only if it is not already present.
71    ///
72    /// Returns `true` if the resource was inserted, `false` if it already existed.
73    pub fn try_insert<T>(&mut self, world: &mut hecs::World, res: T) -> bool
74    where
75        T: hecs::Component,
76    {
77        if self.has_resource::<T>(world) {
78            return false;
79        }
80        world.insert_one(self.resource_entity, res).ok();
81        self.generation.set(self.generation.get() + 1);
82        true
83    }
84
85    pub fn generation(&self) -> u64 {
86        self.generation.get()
87    }
88
89    /// Manually bump the generation counter.
90    ///
91    /// Called by [`App`](crate::app::App) after flushing the command buffer when
92    /// it detects that new resources were inserted via [`Commands`](crate::ecs::system::Commands)
93    /// (which bypasses the normal [`insert_resource`](Self::insert_resource) path).
94    pub(crate) fn bump_generation(&self) {
95        self.generation.set(self.generation.get() + 1);
96    }
97}