veloren_common_state/plugin/
memory_manager.rs

1use common::{
2    comp::{Health, Player},
3    uid::{IdMaps, Uid},
4};
5use specs::{
6    Component, Entities, Entity, Read, ReadStorage, WriteStorage, storage::GenericReadStorage,
7};
8use std::sync::atomic::{AtomicPtr, Ordering};
9
10pub struct EcsWorld<'a, 'b> {
11    pub entities: &'b Entities<'a>,
12    pub health: EcsComponentAccess<'a, 'b, Health>,
13    pub uid: EcsComponentAccess<'a, 'b, Uid>,
14    pub player: EcsComponentAccess<'a, 'b, Player>,
15    pub id_maps: &'b Read<'a, IdMaps>,
16}
17
18pub enum EcsComponentAccess<'a, 'b, T: Component> {
19    Read(&'b ReadStorage<'a, T>),
20    ReadOwned(ReadStorage<'a, T>),
21    Write(&'b WriteStorage<'a, T>),
22    WriteOwned(WriteStorage<'a, T>),
23}
24
25impl<T: Component> EcsComponentAccess<'_, '_, T> {
26    pub fn get(&self, entity: Entity) -> Option<&T> {
27        match self {
28            EcsComponentAccess::Read(e) => e.get(entity),
29            EcsComponentAccess::Write(e) => e.get(entity),
30            EcsComponentAccess::ReadOwned(e) => e.get(entity),
31            EcsComponentAccess::WriteOwned(e) => e.get(entity),
32        }
33    }
34}
35
36impl<'a, 'b, T: Component> From<&'b ReadStorage<'a, T>> for EcsComponentAccess<'a, 'b, T> {
37    fn from(a: &'b ReadStorage<'a, T>) -> Self { Self::Read(a) }
38}
39
40impl<'a, T: Component> From<ReadStorage<'a, T>> for EcsComponentAccess<'a, '_, T> {
41    fn from(a: ReadStorage<'a, T>) -> Self { Self::ReadOwned(a) }
42}
43
44impl<'a, 'b, T: Component> From<&'b WriteStorage<'a, T>> for EcsComponentAccess<'a, 'b, T> {
45    fn from(a: &'b WriteStorage<'a, T>) -> Self { Self::Write(a) }
46}
47
48impl<'a, T: Component> From<WriteStorage<'a, T>> for EcsComponentAccess<'a, '_, T> {
49    fn from(a: WriteStorage<'a, T>) -> Self { Self::WriteOwned(a) }
50}
51
52/// This structure wraps the ECS pointer to ensure safety
53pub struct EcsAccessManager {
54    ecs_pointer: AtomicPtr<EcsWorld<'static, 'static>>,
55}
56
57impl Default for EcsAccessManager {
58    fn default() -> Self {
59        Self {
60            ecs_pointer: AtomicPtr::new(std::ptr::null_mut()),
61        }
62    }
63}
64
65impl EcsAccessManager {
66    // This function take a World reference and a function to execute ensuring the
67    // pointer will never be corrupted during the execution of the function!
68    pub fn execute_with<T>(&self, world: &EcsWorld, func: impl FnOnce() -> T) -> T {
69        let _guard = scopeguard::guard((), |_| {
70            // ensure the pointer is cleared in any case
71            self.ecs_pointer
72                .store(std::ptr::null_mut(), Ordering::Relaxed);
73        });
74        self.ecs_pointer
75            .store(world as *const _ as *mut _, Ordering::Relaxed);
76        func()
77    }
78
79    /// This unsafe function returns a reference to the Ecs World
80    ///
81    /// # Safety
82    /// This function is safe to use if it matches the following requirements
83    ///  - The reference and subreferences like Entities, Components ... aren't
84    ///    leaked out the thread
85    ///  - The reference and subreferences lifetime doesn't exceed the source
86    ///    function lifetime
87    ///  - Always safe when called from `retrieve_action` if you don't pass a
88    ///    reference somewhere else
89    ///  - All that ensure that the reference doesn't exceed the execute_with
90    ///    function scope
91    pub unsafe fn get(&self) -> Option<&EcsWorld> {
92        // ptr::as_ref will automatically check for null
93        self.ecs_pointer.load(Ordering::Relaxed).as_ref()
94    }
95}