diff options
author | HampusM <hampus@hampusmat.com> | 2024-08-16 19:30:06 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2024-08-16 19:30:06 +0200 |
commit | 77e0e30f6faedadae3ff57f2b3abb0548a6a27f1 (patch) | |
tree | 5c85b4e0ca0143fec487641152eee3ebe8dfd8af | |
parent | 7da4b6acf1c08a87a709698d06a4237b38d0aa8a (diff) |
fix(ecs): prevent nested queries causing panic
-rw-r--r-- | ecs/src/component/storage.rs | 59 | ||||
-rw-r--r-- | ecs/src/util.rs | 50 |
2 files changed, 28 insertions, 81 deletions
diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index f8b5b20..5d9bb06 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -3,6 +3,7 @@ use std::borrow::Borrow; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::slice::Iter as SliceIter; +use std::vec::IntoIter as OwnedVecIter; use crate::archetype::Id as ArchetypeId; use crate::component::{ @@ -13,7 +14,7 @@ use crate::component::{ }; use crate::entity::Uid as EntityUid; use crate::type_name::TypeName; -use crate::util::{RefCellRefMap, Sortable}; +use crate::util::Sortable; use crate::EntityComponent; #[derive(Debug, Default)] @@ -42,18 +43,7 @@ impl Storage // This looks stupid but the borrow checker complains otherwise if self.archetype_lookup.borrow().contains_key(&archetype_id) { - let archetype_lookup = self.archetype_lookup.borrow(); - - return ArchetypeRefIter { - inner: RefCellRefMap::new(archetype_lookup, |archetype_lookup| { - archetype_lookup - .get(&archetype_id) - .unwrap() - .archetype_indices - .iter() - }), - archetypes: &self.archetypes, - }; + return self.iter_archetypes_by_lookup(&archetype_id); } let comp_ids_set = create_non_opt_component_id_set(components_metadata.borrow()); @@ -79,18 +69,7 @@ impl Storage }, ); - let archetype_lookup = self.archetype_lookup.borrow(); - - ArchetypeRefIter { - inner: RefCellRefMap::new(archetype_lookup, |archetype_lookup| { - archetype_lookup - .get(&archetype_id) - .unwrap() - .archetype_indices - .iter() - }), - archetypes: &self.archetypes, - } + self.iter_archetypes_by_lookup(&archetype_id) } #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] @@ -306,6 +285,28 @@ impl Storage }) .map(|archetype_index| *archetype_index) } + + fn iter_archetypes_by_lookup( + &self, + archetype_id: &ArchetypeId, + ) -> ArchetypeRefIter<'_> + { + let archetype_lookup = self.archetype_lookup.borrow(); + + // The archetype indices have to be cloned to prevent a panic when query + // iterations are nested. The panic happens because the archetype_lookup RefCell + // is borrowed and it tries to mutably borrow it + let archetype_indices = archetype_lookup + .get(archetype_id) + .unwrap() + .archetype_indices + .clone(); + + return ArchetypeRefIter { + indices: archetype_indices.into_iter(), + archetypes: &self.archetypes, + }; + } } impl TypeName for Storage @@ -441,11 +442,7 @@ impl ArchetypeEntity #[derive(Debug)] pub struct ArchetypeRefIter<'component_storage> { - inner: RefCellRefMap< - 'component_storage, - HashMap<ArchetypeId, ArchetypeLookupEntry>, - SliceIter<'component_storage, usize>, - >, + indices: OwnedVecIter<usize>, archetypes: &'component_storage [Archetype], } @@ -455,7 +452,7 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage> fn next(&mut self) -> Option<Self::Item> { - let archetype_index = *self.inner.next()?; + let archetype_index = self.indices.next()?; Some( self.archetypes diff --git a/ecs/src/util.rs b/ecs/src/util.rs index 5ec21c7..f4f8632 100644 --- a/ecs/src/util.rs +++ b/ecs/src/util.rs @@ -1,6 +1,3 @@ -use std::cell::Ref; -use std::ops::{Deref, DerefMut}; - pub trait Sortable { type Item; @@ -49,50 +46,3 @@ impl<Item> Sortable for Vec<Item> self.sort_by_key(func) } } - -#[derive(Debug)] -pub struct RefCellRefMap<'a, Value: 'a, MappedValue: 'a> -{ - mapped_value: MappedValue, - _refcell_ref: Ref<'a, Value>, -} - -impl<'a, Value: 'a, MappedValue: 'a> RefCellRefMap<'a, Value, MappedValue> -{ - pub fn new( - refcell_ref: Ref<'a, Value>, - map_fn: impl Fn(&'a Value) -> MappedValue, - ) -> Self - { - // Convert the lifetime to 'static. This is necessary to make the compiler not - // complain about refcell_ref being moved - // - // SAFETY: This is fine since we pass it to map_fn with the original lifetime 'a - let val_ref = unsafe { &*(&*refcell_ref as *const Value) }; - - let mapped_value = map_fn(val_ref); - - Self { - mapped_value, - _refcell_ref: refcell_ref, - } - } -} - -impl<'a, Value: 'a, MappedValue: 'a> Deref for RefCellRefMap<'a, Value, MappedValue> -{ - type Target = MappedValue; - - fn deref(&self) -> &Self::Target - { - &self.mapped_value - } -} - -impl<'a, Value: 'a, MappedValue: 'a> DerefMut for RefCellRefMap<'a, Value, MappedValue> -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.mapped_value - } -} |