mirror of
https://github.com/lisk77/comet.git
synced 2025-10-24 05:48:50 +00:00
wip: transitioning to the newer comet_structs and new ComponentStorage through a FlatMap (not the rust implementation)
This commit is contained in:
parent
5b43c7a319
commit
db405bfb2e
12 changed files with 214 additions and 802 deletions
|
|
@ -1,64 +0,0 @@
|
||||||
pub struct MapNode<K, V> {
|
|
||||||
key: K,
|
|
||||||
value: V
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> MapNode<K,V> {
|
|
||||||
pub fn new(key: K, value: V) -> Self {
|
|
||||||
Self {
|
|
||||||
key,
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn key(&self) -> &K {
|
|
||||||
&self.key
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value(&self) -> &V {
|
|
||||||
&self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FlatMap<K, V> {
|
|
||||||
map: Vec<MapNode<K, V>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> FlatMap<K, V> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
map: Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(key: K, value: V) {
|
|
||||||
let node = MapNode::new(key, value);
|
|
||||||
self.map.push(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(key: K) {
|
|
||||||
for node in self.map {
|
|
||||||
if node.key() == key {
|
|
||||||
self.map.remove(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(key: K) -> Option<&V> {
|
|
||||||
for node in self.map {
|
|
||||||
if node.key() == key {
|
|
||||||
return Some(&node.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut(key: K) -> Option<&mut V> {
|
|
||||||
for node in self.map {
|
|
||||||
if node.key() == key {
|
|
||||||
return Some(&mut node.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
pub use column::Column;
|
|
||||||
pub use sparse_set::SparseSet;
|
|
||||||
|
|
||||||
mod column;
|
|
||||||
mod sparse_set;
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
use crate::{Component};
|
|
||||||
use std::{
|
|
||||||
alloc::{
|
|
||||||
handle_alloc_error,
|
|
||||||
Layout
|
|
||||||
},
|
|
||||||
any::TypeId,
|
|
||||||
collections::{
|
|
||||||
HashMap,
|
|
||||||
HashSet
|
|
||||||
},
|
|
||||||
hash::{
|
|
||||||
DefaultHasher,
|
|
||||||
Hash,
|
|
||||||
Hasher
|
|
||||||
},
|
|
||||||
mem::MaybeUninit,
|
|
||||||
ptr::NonNull
|
|
||||||
};
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SparseSet {
|
|
||||||
sparse: Vec<Option<usize>>,
|
|
||||||
dense: Column,
|
|
||||||
len: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SparseSet {
|
|
||||||
pub fn new<T: 'static>(capacity: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
sparse: Vec::with_capacity(capacity),
|
|
||||||
dense: Column::new::<T>(capacity),
|
|
||||||
len: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set<T: 'static>(&mut self, index: usize, element: T) {
|
|
||||||
if index >= self.sparse.len() {
|
|
||||||
self.sparse.resize_with(index + 1, || None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(column_index) = self.sparse[index] {
|
|
||||||
// Explicitly drop the existing component before replacing it
|
|
||||||
unsafe {
|
|
||||||
let existing_ptr = self.dense.data.get_unchecked_mut(column_index) as *mut T;
|
|
||||||
ptr::drop_in_place(existing_ptr);
|
|
||||||
ptr::write(existing_ptr, element);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let column_index = unsafe { self.dense.data.push_uninit() };
|
|
||||||
unsafe {
|
|
||||||
self.dense.data.initialize_unchecked(column_index, &element as *const T as *mut u8);
|
|
||||||
}
|
|
||||||
self.sparse[index] = Some(column_index);
|
|
||||||
self.len += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove<T: 'static>(&mut self, index: usize) -> Option<T> {
|
|
||||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let column_index = self.sparse[index];
|
|
||||||
let element = unsafe {
|
|
||||||
self.dense.data.swap_remove_and_forget_unchecked(column_index.unwrap())
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sparse[index] = None;
|
|
||||||
self.len -= 1;
|
|
||||||
|
|
||||||
Some(unsafe { ptr::read(element as *const T) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<T: 'static>(&self, index: usize) -> Option<&T> {
|
|
||||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dense.get::<T>(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
|
|
||||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dense.get_mut::<T>(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
51
crates/comet_ecs/src/archetypes.rs
Normal file
51
crates/comet_ecs/src/archetypes.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::ComponentSet;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Archetypes {
|
||||||
|
archetypes: HashMap<ComponentSet, Vec<u32>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Archetypes {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
archetypes: HashMap::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn component_sets(&self) -> Vec<ComponentSet> {
|
||||||
|
self.archetypes.keys().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_archetype(&mut self, components: ComponentSet) {
|
||||||
|
self.archetypes.insert(components, Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_archetype(&self, components: &ComponentSet) -> Option<&Vec<u32>> {
|
||||||
|
self.archetypes.get(components)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_archetype_mut(&mut self, components: &ComponentSet) -> Option<&mut Vec<u32>> {
|
||||||
|
self.archetypes.get_mut(components)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_entity_to_archetype(&mut self, components: &ComponentSet, entity: u32) {
|
||||||
|
if let Some(archetype) = self.archetypes.get_mut(components) {
|
||||||
|
archetype.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_entity_from_archetype(&mut self, components: &ComponentSet, entity: u32) {
|
||||||
|
if let Some(archetype) = self.archetypes.get_mut(components) {
|
||||||
|
archetype.retain(|&id| id != entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_archetype(&mut self, components: &ComponentSet) {
|
||||||
|
self.archetypes.remove(components);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_archetype(&self, components: &ComponentSet) -> bool {
|
||||||
|
self.archetypes.contains_key(components)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -298,7 +298,7 @@ impl Camera2D {
|
||||||
self.dimensions = dimensions;
|
self.dimensions = dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_view_frustum(&self, camera_pos: Position2D, entity: Position2D) -> bool {
|
pub fn in_view_frustum(&self, camera_pos: Position2D, entity: Position2D) -> bool {
|
||||||
let left = camera_pos.x() - self.zoom;
|
let left = camera_pos.x() - self.zoom;
|
||||||
let right = camera_pos.x() + self.zoom;
|
let right = camera_pos.x() + self.zoom;
|
||||||
let bottom = camera_pos.y() - self.zoom;
|
let bottom = camera_pos.y() - self.zoom;
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,4 @@ mod entity;
|
||||||
mod component;
|
mod component;
|
||||||
mod world;
|
mod world;
|
||||||
mod id;
|
mod id;
|
||||||
|
mod archetypes;
|
||||||
|
|
@ -1,591 +0,0 @@
|
||||||
use crate::{Component};
|
|
||||||
use std::{
|
|
||||||
alloc::{
|
|
||||||
handle_alloc_error,
|
|
||||||
Layout
|
|
||||||
},
|
|
||||||
any::TypeId,
|
|
||||||
collections::{
|
|
||||||
HashMap,
|
|
||||||
HashSet
|
|
||||||
},
|
|
||||||
hash::{
|
|
||||||
DefaultHasher,
|
|
||||||
Hash,
|
|
||||||
Hasher
|
|
||||||
},
|
|
||||||
mem::MaybeUninit,
|
|
||||||
ptr::NonNull
|
|
||||||
};
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct BlobVec {
|
|
||||||
item_layout: Layout,
|
|
||||||
capacity: usize,
|
|
||||||
len: usize,
|
|
||||||
data: NonNull<u8>,
|
|
||||||
swap_scratch: NonNull<u8>,
|
|
||||||
drop: unsafe fn(*mut u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl BlobVec {
|
|
||||||
pub fn new(item_layout: Layout, drop: unsafe fn(*mut u8), capacity: usize) -> Self {
|
|
||||||
if item_layout.size() == 0 {
|
|
||||||
BlobVec {
|
|
||||||
swap_scratch: NonNull::dangling(),
|
|
||||||
data: NonNull::dangling(),
|
|
||||||
capacity: usize:: MAX,
|
|
||||||
len: 0,
|
|
||||||
item_layout,
|
|
||||||
drop,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let swap_scratch = NonNull::new(unsafe { std::alloc::alloc(item_layout) })
|
|
||||||
.unwrap_or_else(|| handle_alloc_error(item_layout));
|
|
||||||
|
|
||||||
let mut blob_vec = BlobVec {
|
|
||||||
swap_scratch,
|
|
||||||
data: NonNull::dangling(),
|
|
||||||
capacity: 0,
|
|
||||||
len: 0,
|
|
||||||
item_layout,
|
|
||||||
drop,
|
|
||||||
};
|
|
||||||
blob_vec.reserve_exact(capacity);
|
|
||||||
blob_vec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reserve_exact(&mut self, additional: usize) {
|
|
||||||
let available_space = self.capacity - self.len;
|
|
||||||
if available_space < additional {
|
|
||||||
self.grow_exact(additional - available_space);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grow_exact(&mut self, increment: usize) {
|
|
||||||
debug_assert!(self.item_layout.size() != 0);
|
|
||||||
|
|
||||||
let new_capacity = self.capacity + increment;
|
|
||||||
let new_layout =
|
|
||||||
array_layout(&self.item_layout, new_capacity).expect("array layout should be valid");
|
|
||||||
unsafe {
|
|
||||||
let new_data = if self.capacity == 0 {
|
|
||||||
std::alloc::alloc(new_layout)
|
|
||||||
} else {
|
|
||||||
std::alloc::realloc(
|
|
||||||
self.get_ptr().as_ptr(),
|
|
||||||
array_layout(&self.item_layout, self.capacity)
|
|
||||||
.expect("array layout should be valid"),
|
|
||||||
new_layout.size(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.data = NonNull::new(new_data).unwrap_or_else(|| handle_alloc_error(new_layout));
|
|
||||||
}
|
|
||||||
self.capacity = new_capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.len
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.len == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn capacity(&self) -> usize {
|
|
||||||
self.capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn get_ptr(&self) -> NonNull<u8> {
|
|
||||||
self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn push_uninit(&mut self) -> usize {
|
|
||||||
self.reserve_exact(1);
|
|
||||||
let index = self.len;
|
|
||||||
self.len += 1;
|
|
||||||
index
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn get_unchecked(&self, index: usize) -> *mut u8 {
|
|
||||||
debug_assert!(index < self.len());
|
|
||||||
self.get_ptr().as_ptr().add(index * self.item_layout.size())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> *mut u8 {
|
|
||||||
debug_assert!(index < self.len());
|
|
||||||
self.get_ptr().as_ptr().add(index * self.item_layout.size())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn push_element<T>(&mut self, element: T) {
|
|
||||||
let index = self.push_uninit();
|
|
||||||
let ptr = self.get_unchecked(index) as *mut T;
|
|
||||||
ptr::write(ptr,element);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
let len = self.len;
|
|
||||||
// We set len to 0 _before_ dropping elements for unwind safety. This ensures we don't
|
|
||||||
// accidentally drop elements twice in the event of a drop impl panicking.
|
|
||||||
self.len = 0;
|
|
||||||
for i in 0..len {
|
|
||||||
unsafe {
|
|
||||||
// NOTE: this doesn't use self.get_unchecked(i) because the debug_assert on index
|
|
||||||
// will panic here due to self.len being set to 0
|
|
||||||
let ptr = self.get_ptr().as_ptr().add(i * self.item_layout.size());
|
|
||||||
(self.drop)(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn swap_remove_and_forget_unchecked(&mut self, index: usize) -> *mut u8 {
|
|
||||||
debug_assert!(index < self.len());
|
|
||||||
let last = self.len - 1;
|
|
||||||
let swap_scratch = self.swap_scratch.as_ptr();
|
|
||||||
ptr::copy_nonoverlapping(
|
|
||||||
self.get_unchecked(index),
|
|
||||||
swap_scratch,
|
|
||||||
self.item_layout.size(),
|
|
||||||
);
|
|
||||||
ptr::copy(
|
|
||||||
self.get_unchecked(last),
|
|
||||||
self.get_unchecked(index),
|
|
||||||
self.item_layout.size(),
|
|
||||||
);
|
|
||||||
self.len -= 1;
|
|
||||||
swap_scratch
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn initialize_unchecked(&mut self, index: usize, value: *mut u8) {
|
|
||||||
debug_assert!(index < self.len());
|
|
||||||
let ptr = self.get_unchecked(index);
|
|
||||||
ptr::copy_nonoverlapping(value, ptr, self.item_layout.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for BlobVec {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.clear();
|
|
||||||
let array_layout =
|
|
||||||
array_layout(&self.item_layout, self.capacity).expect("array layout should be valid");
|
|
||||||
if array_layout.size() > 0 {
|
|
||||||
unsafe {
|
|
||||||
std::alloc::dealloc(self.get_ptr().as_ptr(), array_layout);
|
|
||||||
std::alloc::dealloc(self.swap_scratch.as_ptr(), self.item_layout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for BlobVec {}
|
|
||||||
unsafe impl Sync for BlobVec {}
|
|
||||||
|
|
||||||
fn array_layout(layout: &Layout, n: usize) -> Option<Layout> {
|
|
||||||
let (array_layout, offset) = repeat_layout(layout, n)?;
|
|
||||||
debug_assert_eq!(layout.size(), offset);
|
|
||||||
Some(array_layout)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn repeat_layout(layout: &Layout, n: usize) -> Option<(Layout, usize)> {
|
|
||||||
let padded_size = layout.size() + padding_needed_for(layout, layout.align());
|
|
||||||
let alloc_size = padded_size.checked_mul(n)?;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
Some((
|
|
||||||
Layout::from_size_align_unchecked(alloc_size, layout.align()),
|
|
||||||
padded_size,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
|
|
||||||
let len = layout.size();
|
|
||||||
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
|
|
||||||
len_rounded_up.wrapping_sub(len)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Column {
|
|
||||||
pub data: BlobVec
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Column {
|
|
||||||
pub fn new<T: 'static>(capacity: usize) -> Self {
|
|
||||||
let layout = Layout::new::<T>();
|
|
||||||
let drop_fn = |ptr: *mut u8| unsafe {
|
|
||||||
ptr::drop_in_place(ptr as *mut T);
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
data: BlobVec::new(layout, drop_fn, capacity),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data(&self) -> BlobVec {
|
|
||||||
self.data.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push<T: 'static>(&mut self, item: T) {
|
|
||||||
assert_eq!(TypeId::of::<T>(), TypeId::of::<T>(), "Type mismatch");
|
|
||||||
unsafe {
|
|
||||||
let index = self.data.push_uninit();
|
|
||||||
let ptr = self.data.get_unchecked(index);
|
|
||||||
ptr::write(ptr as *mut T, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<T: 'static>(&self, index: usize) -> Option<&T> {
|
|
||||||
assert_eq!(TypeId::of::<T>(), TypeId::of::<T>(), "Type mismatch");
|
|
||||||
if index >= self.data.len() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let ptr = self.data.get_unchecked(index);
|
|
||||||
Some(&*(ptr as *const T))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
|
|
||||||
assert_eq!(TypeId::of::<T>(), TypeId::of::<T>(), "Type mismatch");
|
|
||||||
|
|
||||||
if index >= self.data.len() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access the element at the given index
|
|
||||||
unsafe {
|
|
||||||
let ptr = self.data.get_unchecked(index);
|
|
||||||
// Convert the pointer to a mutable reference and return it
|
|
||||||
Some(&mut *(ptr as *mut T))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove<T: 'static>(&mut self, index: usize) -> Option<T> {
|
|
||||||
assert_eq!(TypeId::of::<T>(), TypeId::of::<T>(), "Type mismatch");
|
|
||||||
if index >= self.data.len() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let ptr = self.data.swap_remove_and_forget_unchecked(index);
|
|
||||||
Some(ptr::read(ptr as *const T))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn swap(&mut self, index1: usize, index2: usize) {
|
|
||||||
assert!(index1 < self.data.len() && index2 < self.data.len(), "Index out of bounds");
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let ptr1 = self.data.get_unchecked(index1);
|
|
||||||
let ptr2 = self.data.get_unchecked(index2);
|
|
||||||
|
|
||||||
let mut temp = MaybeUninit::<u8>::uninit();
|
|
||||||
|
|
||||||
// Swap the elements at index1 and index2
|
|
||||||
ptr::copy_nonoverlapping(ptr1, temp.as_mut_ptr(), self.data.item_layout.size());
|
|
||||||
ptr::copy_nonoverlapping(ptr2, ptr1, self.data.item_layout.size());
|
|
||||||
ptr::copy_nonoverlapping(temp.as_ptr(), ptr2, self.data.item_layout.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct IterMut<'a, K, V> {
|
|
||||||
keys_iter: std::slice::IterMut<'a, K>,
|
|
||||||
values_iter: std::slice::IterMut<'a, V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
|
|
||||||
type Item = (&'a mut K, &'a mut V);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
match (self.keys_iter.next(), self.values_iter.next()) {
|
|
||||||
(Some(key), Some(value)) => Some((key, value)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SparseSet {
|
|
||||||
sparse: Vec<Option<usize>>,
|
|
||||||
dense: Column,
|
|
||||||
len: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SparseSet {
|
|
||||||
pub fn new<T: 'static>(capacity: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
sparse: Vec::with_capacity(capacity),
|
|
||||||
dense: Column::new::<T>(capacity),
|
|
||||||
len: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set<T: 'static>(&mut self, index: usize, element: T) {
|
|
||||||
if index >= self.sparse.len() {
|
|
||||||
self.sparse.resize_with(index + 1, || None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(column_index) = self.sparse[index] {
|
|
||||||
// Explicitly drop the existing component before replacing it
|
|
||||||
unsafe {
|
|
||||||
let existing_ptr = self.dense.data.get_unchecked_mut(column_index) as *mut T;
|
|
||||||
ptr::drop_in_place(existing_ptr);
|
|
||||||
ptr::write(existing_ptr, element);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let column_index = unsafe { self.dense.data.push_uninit() };
|
|
||||||
unsafe {
|
|
||||||
self.dense.data.initialize_unchecked(column_index, &element as *const T as *mut u8);
|
|
||||||
}
|
|
||||||
self.sparse[index] = Some(column_index);
|
|
||||||
self.len += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove<T: 'static>(&mut self, index: usize) -> Option<T> {
|
|
||||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let column_index = self.sparse[index];
|
|
||||||
let element = unsafe {
|
|
||||||
self.dense.data.swap_remove_and_forget_unchecked(column_index.unwrap())
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sparse[index] = None;
|
|
||||||
self.len -= 1;
|
|
||||||
|
|
||||||
Some(unsafe { ptr::read(element as *const T) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<T: 'static>(&self, index: usize) -> Option<&T> {
|
|
||||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dense.get::<T>(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
|
|
||||||
if index >= self.sparse.len() || self.sparse[index] == None {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dense.get_mut::<T>(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ComponentStorage {
|
|
||||||
index_map: HashMap<TypeId, usize>,
|
|
||||||
pub(crate) keys: Vec<TypeId>,
|
|
||||||
components: Vec<SparseSet>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentStorage {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
index_map: HashMap::new(),
|
|
||||||
keys: Vec::new(),
|
|
||||||
components: Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn keys(&self) -> &Vec<TypeId> {
|
|
||||||
&self.keys
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_component(&self, type_id: &TypeId) -> bool {
|
|
||||||
self.keys.contains(type_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_components(&self, component_set: ComponentSet) -> bool {
|
|
||||||
let mut contains = true;
|
|
||||||
for type_id in component_set.set.iter() {
|
|
||||||
if !self.keys.contains(type_id) {
|
|
||||||
contains = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
contains
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<T: Component + 'static>(&self) -> Option<&SparseSet> {
|
|
||||||
self.components.get(*self.index_map.get(&T::type_id()).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set<T: Component + 'static>(&mut self, sparse_set: SparseSet) {
|
|
||||||
let _ = self.components.get_mut(*self.index_map.get(&T::type_id()).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_component<T: Component + 'static>(&mut self, capacity: usize) {
|
|
||||||
//self.storage.insert(T::type_id(), SparseSet::new::<T>(capacity));
|
|
||||||
assert!(!self.keys.contains(&T::type_id()), "This component ({}) is already registered!", T::type_name());
|
|
||||||
self.keys.push(T::type_id());
|
|
||||||
self.index_map.insert(T::type_id(), self.keys.len()-1);
|
|
||||||
self.components.push(SparseSet::new::<T>(capacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_component<T: Component + 'static>(&self, entity_id: usize) -> Option<&T> {
|
|
||||||
//self.storage.get(&T::type_id()).unwrap().get::<T>(*entity.id() as usize)
|
|
||||||
self.components.get(*self.index_map.get(&T::type_id()).unwrap()).unwrap().get::<T>(entity_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_component_mut<T: Component + 'static>(&mut self, entity_id: usize) -> Option<&mut T> {
|
|
||||||
self.components.get_mut(*self.index_map.get(&T::type_id()).unwrap()).unwrap().get_mut::<T>(entity_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_component<T: Component + 'static>(&mut self, entity_id: usize, component: T) {
|
|
||||||
let index = *self.index_map.get(&T::type_id()).unwrap();
|
|
||||||
let sparse_set = self.components.get_mut(index).unwrap();
|
|
||||||
|
|
||||||
// Check if a component already exists for this entity
|
|
||||||
if let Some(existing_component) = sparse_set.get_mut::<T>(entity_id) {
|
|
||||||
// Explicitly drop the existing component before overwriting it
|
|
||||||
std::mem::drop(existing_component);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new component
|
|
||||||
sparse_set.set(entity_id, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deregister_component<T: Component + 'static>(&mut self) {
|
|
||||||
let type_id = T::type_id();
|
|
||||||
if let Some(&index) = self.index_map.get(&type_id) {
|
|
||||||
// Before removing the SparseSet, ensure all elements are properly dropped
|
|
||||||
let sparse_set = self.components.get_mut(index).unwrap();
|
|
||||||
for i in 0..sparse_set.sparse.len() {
|
|
||||||
if sparse_set.sparse[i].is_some() {
|
|
||||||
sparse_set.remove::<T>(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.components.remove(index);
|
|
||||||
self.index_map.remove(&type_id);
|
|
||||||
self.keys.retain(|&k| k != type_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_component<T: Component + 'static>(&mut self, entity_id: usize) {
|
|
||||||
if let Some(index) = self.index_map.get(&T::type_id()) {
|
|
||||||
let sparse_set = self.components.get_mut(*index).unwrap();
|
|
||||||
sparse_set.remove::<T>(entity_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_dense_list_as_vec<T: Component + Clone + 'static>(&self) -> Option<Vec<T>> {
|
|
||||||
let mut resulting_vec: Vec<T> = Vec::new();
|
|
||||||
|
|
||||||
if let Some(sparse_set) = self.components.get(*self.index_map.get(&T::type_id()).unwrap()) {
|
|
||||||
for i in 0..sparse_set.dense.data.len() {
|
|
||||||
let item: T = sparse_set.dense.get::<T>(i)?.clone();
|
|
||||||
resulting_vec.push(item);
|
|
||||||
}
|
|
||||||
Some(resulting_vec)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_mut(&mut self) -> IterMut<'_, TypeId, SparseSet> {
|
|
||||||
IterMut {
|
|
||||||
keys_iter: self.keys.iter_mut(),
|
|
||||||
values_iter: self.components.iter_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct ComponentSet {
|
|
||||||
set: HashSet<TypeId>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentSet {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
set: HashSet::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_ids(ids: Vec<TypeId>) -> Self {
|
|
||||||
Self {
|
|
||||||
set: ids.into_iter().collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_subset(&self, other: &ComponentSet) -> bool {
|
|
||||||
self.set.is_subset(&other.set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for ComponentSet {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
let mut types: Vec<TypeId> = self.set.iter().cloned().collect();
|
|
||||||
types.sort();
|
|
||||||
types.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Archetypes {
|
|
||||||
archetypes: HashMap<ComponentSet, Vec<u32>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Archetypes {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
archetypes: HashMap::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn component_sets(&self) -> Vec<ComponentSet> {
|
|
||||||
self.archetypes.keys().cloned().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_archetype(&mut self, components: ComponentSet) {
|
|
||||||
self.archetypes.insert(components, Vec::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_archetype(&self, components: &ComponentSet) -> Option<&Vec<u32>> {
|
|
||||||
self.archetypes.get(components)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_archetype_mut(&mut self, components: &ComponentSet) -> Option<&mut Vec<u32>> {
|
|
||||||
self.archetypes.get_mut(components)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_entity_to_archetype(&mut self, components: &ComponentSet, entity: u32) {
|
|
||||||
if let Some(archetype) = self.archetypes.get_mut(components) {
|
|
||||||
archetype.push(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_entity_from_archetype(&mut self, components: &ComponentSet, entity: u32) {
|
|
||||||
if let Some(archetype) = self.archetypes.get_mut(components) {
|
|
||||||
archetype.retain(|&id| id != entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_archetype(&mut self, components: &ComponentSet) {
|
|
||||||
self.archetypes.remove(components);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_archetype(&self, components: &ComponentSet) -> bool {
|
|
||||||
self.archetypes.contains_key(components)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,13 +5,12 @@ use crate::{
|
||||||
Component,
|
Component,
|
||||||
Transform2D,
|
Transform2D,
|
||||||
Transform3D,
|
Transform3D,
|
||||||
ComponentStorage,
|
|
||||||
SparseSet,
|
|
||||||
IdQueue,
|
IdQueue,
|
||||||
Archetypes,
|
Archetypes,
|
||||||
ComponentSet
|
ComponentSet
|
||||||
};
|
};
|
||||||
use comet_log::*;
|
use comet_log::*;
|
||||||
|
use comet_structs::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
|
|
@ -144,7 +143,7 @@ impl World {
|
||||||
|
|
||||||
fn get_component_set(&self, entity_id: usize) -> ComponentSet {
|
fn get_component_set(&self, entity_id: usize) -> ComponentSet {
|
||||||
let components = self.entities.get(entity_id).unwrap().as_ref().unwrap().get_components().iter().collect::<Vec<usize>>();
|
let components = self.entities.get(entity_id).unwrap().as_ref().unwrap().get_components().iter().collect::<Vec<usize>>();
|
||||||
let type_ids = components.iter().map(|index| self.components.keys[*index]).collect::<Vec<TypeId>>();
|
let type_ids = components.iter().map(|index| self.components.keys()[*index]).collect::<Vec<TypeId>>();
|
||||||
ComponentSet::from_ids(type_ids)
|
ComponentSet::from_ids(type_ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +164,7 @@ impl World {
|
||||||
pub fn add_component<C: Component + 'static>(&mut self, entity_id: usize, component: C) {
|
pub fn add_component<C: Component + 'static>(&mut self, entity_id: usize, component: C) {
|
||||||
assert_ne!(self.entities.get(entity_id), None, "There is no entity with this ID ({}) in the world!", entity_id);
|
assert_ne!(self.entities.get(entity_id), None, "There is no entity with this ID ({}) in the world!", entity_id);
|
||||||
self.components.set_component(entity_id, component);
|
self.components.set_component(entity_id, component);
|
||||||
let component_index = self.components.keys.iter_mut().position(|x| *x == C::type_id()).unwrap();
|
let component_index = self.components.keys().iter_mut().position(|x| *x == *C::type_id()).unwrap();
|
||||||
|
|
||||||
self.get_entity_mut(entity_id).unwrap().add_component(component_index);
|
self.get_entity_mut(entity_id).unwrap().add_component(component_index);
|
||||||
|
|
||||||
|
|
|
||||||
74
crates/comet_structs/src/component_storage.rs
Normal file
74
crates/comet_structs/src/component_storage.rs
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
use std::any::{Any, TypeId};
|
||||||
|
use comet_log::*;
|
||||||
|
use crate::{FlatMap, IterMut, SparseSet};
|
||||||
|
|
||||||
|
pub type ComponentStorage = FlatMap<TypeId, SparseSet>;
|
||||||
|
|
||||||
|
impl ComponentStorage {
|
||||||
|
|
||||||
|
pub fn register_component<T: 'static>(&mut self, capacity: usize) {
|
||||||
|
if !self.contains(&TypeId::of::<T>()) {
|
||||||
|
self.insert(TypeId::of::<T>(), SparseSet::new::<T>(capacity));
|
||||||
|
info!("Component {:?} has been registered", TypeId::of::<T>());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error!("Component {:?} already exists", TypeId::of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deregister_component<T: 'static>(&mut self) {
|
||||||
|
if self.contains(&TypeId::of::<T>()) {
|
||||||
|
self.remove(&TypeId::of::<T>());
|
||||||
|
info!("Component {:?} has been deregistered", TypeId::of::<T>());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error!("Component {:?} does not exist", TypeId::of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_component<T: 'static>(&mut self, index: usize, element: T) {
|
||||||
|
if let Some(sparse_set) = self.get_mut(&TypeId::of::<T>()) {
|
||||||
|
sparse_set.set(index, element);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error!("Component {:?} is not registered", TypeId::of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_component<T: 'static>(&mut self, index: usize) -> Option<T> {
|
||||||
|
if let Some(sparse_set) = self.get_mut(&TypeId::of::<T>()) {
|
||||||
|
sparse_set.remove(index)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error!("Component {:?} is not registered", TypeId::of::<T>());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_component<T: 'static>(&self, index: usize) -> Option<&T> {
|
||||||
|
if let Some(sparse_set) = self.get(&TypeId::of::<T>()) {
|
||||||
|
sparse_set.get(index)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error!("Component {:?} is not registered", TypeId::of::<T>());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_component_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
|
||||||
|
if let Some(sparse_set) = self.get_mut(&TypeId::of::<T>()) {
|
||||||
|
sparse_set.get_mut(index)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error!("Component {:?} is not registered", TypeId::of::<T>());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_mut(&mut self) -> IterMut<'_, &TypeId, &SparseSet> {
|
||||||
|
IterMut {
|
||||||
|
keys_iter: self.keys_mut(),
|
||||||
|
values_iter: self.values_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct MapNode<K, V> {
|
pub struct MapNode<K, V> {
|
||||||
key: K,
|
key: K,
|
||||||
value: V
|
value: V
|
||||||
|
|
@ -20,6 +21,7 @@ impl<K, V> MapNode<K,V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct FlatMap<K: PartialEq, V> {
|
pub struct FlatMap<K: PartialEq, V> {
|
||||||
map: Vec<MapNode<K, V>>
|
map: Vec<MapNode<K, V>>
|
||||||
}
|
}
|
||||||
|
|
@ -31,34 +33,51 @@ impl<K: PartialEq, V> FlatMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn keys(&self) -> Vec<&K> {
|
||||||
|
self.map.iter().map(|node| node.key()).collect::<Vec<&K>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values(&self) -> Vec<&V> {
|
||||||
|
self.map.iter().map(|node| node.value()).collect::<Vec<&V>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keys_mut(&mut self) -> impl Iterator<Item = &mut K> {
|
||||||
|
self.map.iter_mut().map(|node| &mut node.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
|
||||||
|
self.map.iter_mut().map(|node| &mut node.value)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, key: K, value: V) {
|
pub fn insert(&mut self, key: K, value: V) {
|
||||||
let node = MapNode::new(key, value);
|
let node = MapNode::new(key, value);
|
||||||
self.map.push(node);
|
self.map.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, key: K) {
|
pub fn remove(&mut self, key: &K) {
|
||||||
for node in self.map {
|
self.map.retain(|node| node.key() != key);
|
||||||
if node.key() == *key {
|
|
||||||
self.map.retain(|&n| n.key() != *key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, key: K) -> Option<&V> {
|
pub fn get(&self, key: &K) -> Option<&V> {
|
||||||
for node in self.map {
|
self.map.iter()
|
||||||
if node.key() == *key {
|
.find(|node| node.key() == key)
|
||||||
return Some(&node.value);
|
.map(|node| node.value())
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, key: K) -> Option<&mut V> {
|
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
|
||||||
for mut node in self.map {
|
self.map.iter_mut()
|
||||||
if node.key() == *key {
|
.find(|node| node.key() == key)
|
||||||
return Some(node.value);
|
.map(|node| node.value_mut())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None
|
pub fn contains(&self, key: &K) -> bool {
|
||||||
|
self.map.iter().any(|node| node.key() == key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this method to MapNode to allow mutable access to value
|
||||||
|
impl<K, V> MapNode<K, V> {
|
||||||
|
fn value_mut(&mut self) -> &mut V {
|
||||||
|
&mut self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
15
crates/comet_structs/src/iter_mut.rs
Normal file
15
crates/comet_structs/src/iter_mut.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
pub struct IterMut<'a, K, V> {
|
||||||
|
pub(crate) keys_iter: std::slice::IterMut<'a, K>,
|
||||||
|
pub(crate) values_iter: std::slice::IterMut<'a, V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
|
||||||
|
type Item = (&'a mut K, &'a mut V);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match (self.keys_iter.next(), self.values_iter.next()) {
|
||||||
|
(Some(key), Some(value)) => Some((key, value)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
pub use column::Column;
|
pub use column::Column;
|
||||||
pub use sparse_set::SparseSet;
|
pub use sparse_set::SparseSet;
|
||||||
pub use flat_map::FlatMap;
|
pub use flat_map::FlatMap;
|
||||||
|
pub use component_storage::ComponentStorage;
|
||||||
|
pub use iter_mut::IterMut;
|
||||||
|
|
||||||
mod column;
|
mod column;
|
||||||
mod sparse_set;
|
mod sparse_set;
|
||||||
mod flat_map;
|
mod flat_map;
|
||||||
|
mod component_storage;
|
||||||
|
mod iter_mut;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue