Skip to main content

pebble/ecs/
resources.rs

1use std::cell::{RefCell, RefMut};
2
3thread_local! {
4    /// Name of the system currently executing on this thread, set by
5    /// [`crate::app::App::run_stage_once`] around each system's `run` call.
6    /// Used to enrich the panic message when a system fetches a resource
7    /// that isn't present.
8    static CURRENT_SYSTEM: std::cell::Cell<Option<&'static str>> = std::cell::Cell::new(None);
9}
10
11/// Set the name of the system about to run on this thread. Returns a guard
12/// that restores the previous value on drop, so nested/re-entrant calls
13/// behave correctly.
14pub(crate) struct CurrentSystemGuard(Option<&'static str>);
15
16impl Drop for CurrentSystemGuard {
17    fn drop(&mut self) {
18        CURRENT_SYSTEM.with(|c| c.set(self.0));
19    }
20}
21
22pub(crate) fn set_current_system(name: &'static str) -> CurrentSystemGuard {
23    let previous = CURRENT_SYSTEM.with(|c| c.replace(Some(name)));
24    CurrentSystemGuard(previous)
25}
26
27fn current_system_suffix() -> String {
28    CURRENT_SYSTEM.with(|c| match c.get() {
29        Some(name) => format!(" (while running system `{name}`)"),
30        None => String::new(),
31    })
32}
33
34/// Container for singleton resources stored inside the ECS world.
35///
36/// All resources live on a single hidden entity so they participate in the
37/// same borrow-checking rules as regular components. [`Resources`] is passed
38/// to every system alongside the [`hecs::World`].
39pub struct Resources {
40    pub(crate) resource_entity: hecs::Entity,
41    cmds: RefCell<hecs::CommandBuffer>,
42}
43
44impl Resources {
45    /// Create a new `Resources` container, spawning the internal resource entity.
46    pub fn new(world: &mut hecs::World) -> Self {
47        Self {
48            resource_entity: world.spawn(()),
49            cmds: RefCell::new(hecs::CommandBuffer::default()),
50        }
51    }
52
53    /// Insert or replace a resource of type `T`.
54    pub fn insert_resource<T>(&mut self, world: &mut hecs::World, res: T)
55    where
56        T: hecs::Component,
57    {
58        world.insert_one(self.resource_entity, res).ok();
59    }
60
61    /// Borrow resource `T`, panicking if it is not present.
62    pub fn get_resource<'a, T>(&self, world: &'a hecs::World) -> hecs::Ref<'a, T>
63    where
64        T: hecs::Component,
65    {
66        world.get::<&T>(self.resource_entity).unwrap_or_else(|_| {
67            panic!(
68                "Resource not found: {}{}",
69                std::any::type_name::<T>(),
70                current_system_suffix()
71            )
72        })
73    }
74
75    /// Mutably borrow resource `T`, panicking if it is not present.
76    pub fn get_resource_mut<'a, T>(&self, world: &'a hecs::World) -> hecs::RefMut<'a, T>
77    where
78        T: hecs::Component,
79    {
80        world.get::<&mut T>(self.resource_entity).unwrap_or_else(|_| {
81            panic!(
82                "Resource not found: {}{}",
83                std::any::type_name::<T>(),
84                current_system_suffix()
85            )
86        })
87    }
88
89    /// Returns `true` if resource `T` is currently present.
90    pub fn has_resource<T>(&self, world: &hecs::World) -> bool
91    where
92        T: hecs::Component,
93    {
94        if let Ok(_) = world.get::<&T>(self.resource_entity) {
95            return true;
96        }
97
98        false
99    }
100
101    /// Borrow the shared command buffer used to defer world mutations.
102    pub fn get_command_buffer<'a>(&'a self) -> RefMut<'a, hecs::CommandBuffer> {
103        self.cmds.borrow_mut()
104    }
105
106    /// Insert resource `T` only if it is not already present.
107    ///
108    /// Returns `true` if the resource was inserted, `false` if it already existed.
109    pub fn try_insert<T>(&mut self, world: &mut hecs::World, res: T) -> bool
110    where
111        T: hecs::Component,
112    {
113        if self.has_resource::<T>(world) {
114            return false;
115        }
116        world.insert_one(self.resource_entity, res).ok();
117        true
118    }
119}