Skip to content

Commit

Permalink
non cloneable dynamic storage
Browse files Browse the repository at this point in the history
  • Loading branch information
Nertsal committed Nov 15, 2024
1 parent d383175 commit c11bfe6
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 32 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ required-features = ["query_mut"]
[[example]]
name = "full"
doc-scrape-examples = true
required-features = ["query_mut"]
required-features = ["query_mut", "dynamic"]

[[example]]
name = "blogpost"
Expand Down
46 changes: 32 additions & 14 deletions crates/stecs-derive/src/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct SplitOpts {
data: ast::Data<(), FieldOpts>,
generics: syn::Generics,
debug: Option<()>,
to_owned: Option<()>,
clone: Option<()>,
}

Expand All @@ -30,6 +31,7 @@ struct Struct {
generics: syn::Generics,
debug: bool,
to_owned: bool,
archetype_clone: bool,
}

struct Field {
Expand Down Expand Up @@ -72,7 +74,8 @@ impl TryFrom<SplitOpts> for Struct {
fields,
generics: value.generics,
debug: value.debug.is_some(),
to_owned: value.clone.is_some(),
to_owned: value.to_owned.is_some(),
archetype_clone: value.clone.is_some(),
})
}
}
Expand All @@ -93,6 +96,7 @@ impl Struct {
generics: struct_generics,
debug: struct_debug,
to_owned: struct_to_owned,
archetype_clone,
} = self;

if struct_fields.iter().any(|field| field.name == "id") {
Expand Down Expand Up @@ -367,13 +371,20 @@ This struct is a version of [`{struct_name}`] that holds each field in its own [
#[cfg(not(feature = "dynamic"))]
let dynamic = quote! {};
#[cfg(feature = "dynamic")]
let dynamic = quote! {
/// Dynamic components attached to the archetype.
///
/// **Note**: not intended to be used directly,
/// but rather via methods and querying macros.
/// *It is only exposed to be accessible in queries*.
pub r#dyn: ::stecs::dynamic::DynamicStorage<#generic_family_name>,
let dynamic = {
let storage = if archetype_clone {
quote! { ::stecs::dynamic::DynamicCloneStorage<#generic_family_name> }
} else {
quote! { ::stecs::dynamic::DynamicStorage<#generic_family_name> }
};
quote! {
/// Dynamic components attached to the archetype.
///
/// **Note**: not intended to be used directly,
/// but rather via methods and querying macros.
/// *It is only exposed to be accessible in queries*.
pub r#dyn: #storage,
}
};

let struct_of_doc = format!(
Expand Down Expand Up @@ -440,13 +451,13 @@ This struct is a version of [`{struct_name}`] that holds each field in its own [
};

// impl Clone for StructOf
let struct_of_clone = {
let struct_of_clone = if archetype_clone {
#[cfg(not(feature = "dynamic"))]
let (dynamic, dynamic_constraint) = (quote! {}, quote! {});
#[cfg(feature = "dynamic")]
let (dynamic, dynamic_constraint) = (
quote! { r#dyn: self.r#dyn.clone(), },
quote! { ::stecs::dynamic::DynamicStorage<#generic_family_name>: ::std::clone::Clone, },
quote! { ::stecs::dynamic::DynamicCloneStorage<#generic_family_name>: ::std::clone::Clone, },
);

quote! {
Expand All @@ -465,6 +476,8 @@ This struct is a version of [`{struct_name}`] that holds each field in its own [
}
}
}
} else {
quote! {}
};

// impl StructSplit
Expand Down Expand Up @@ -739,21 +752,26 @@ The given `ids` must not repeat and must be valid and present id's in the storag
let insert_dyn_doc = r#"Insert a dynamic component into an entity."#.to_string();
let remove_dyn_doc = r#"Remove a dynamic component from an entity."#.to_string();

let mut constraints = vec![quote! {#generic_family_name::Id: 'static,}];
if archetype_clone {
constraints.push(quote! { __T: Clone + 'static });
} else {
constraints.push(quote! { __T: 'static });
}

quote! {
#[doc = #insert_dyn_doc]
pub fn insert_dyn<__T>(&mut self, id: #generic_family_name::Id, component: __T)
where
__T: Clone + 'static,
#generic_family_name::Id: 'static,
#(#constraints)*
{
self.r#dyn.insert(id, component)
}

#[doc = #remove_dyn_doc]
pub fn remove_dyn<__T>(&mut self, id: #generic_family_name::Id) -> Option<__T>
where
__T: Clone + 'static,
#generic_family_name::Id: 'static,
#(#constraints)*
{
self.r#dyn.remove(id)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/blogpost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use stecs::prelude::*;

// Define an Archetype
#[derive(SplitFields)]
#[split(debug, clone)] // derive Debug and Clone for generated reference types
#[split(debug, to_owned)] // derive Debug and Clone for generated reference types
pub struct Monster {
pub position: (f32, f32),
pub health: f32,
Expand Down
4 changes: 2 additions & 2 deletions examples/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ pub struct World {
pub units: StructOf<Dense<Unit>>,
}

#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Poisoned {
pub time: f32,
}

#[derive(SplitFields)]
#[split(debug, clone)]
#[split(debug)]
pub struct Unit {
pub name: String,
}
Expand Down
21 changes: 17 additions & 4 deletions examples/full.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
#![allow(dead_code)]
use stecs::prelude::*;

#[derive(Clone)] // `StructOf` implements Clone if possible
#[derive(Clone)]
// ^ `StructOf` implements Clone if possible, but requires annotating archetypes with `#[split(clone)]`
pub struct GameWorld {
pub units: StructOf<Dense<Unit>>, // UnitStructOf<DenseFamily>,
pub corpses: StructOf<Dense<Corpse>>, // CorpseStructOf<DenseFamily>,
pub particles: StructOf<Dense<Particle>>, // ParticleStructOf<DenseFamily>,
}

#[derive(SplitFields, Debug)]
#[split(debug)] // derive `Debug` for the `UnitRef` generated struct
#[split(debug, to_owned, clone)]
// ^(debug) derive `Debug` for the `UnitRef` generated struct
// ^(to_owned) implement clone method for the `ParticleRef` generated struct to clone the data into a `Particle`
// ^(clone) and allow the archetype to be cloned, if all components are Clone-able
pub struct Unit {
pub pos: (f32, f32),
pub health: f32,
Expand All @@ -18,6 +22,7 @@ pub struct Unit {
}

#[derive(SplitFields)]
#[split(clone)]
pub struct Corpse {
// Nest `Unit` to efficiently store the fields and to refer to them directly in the queries.
// But you can still access the whole `Unit` as a single component.
Expand All @@ -27,12 +32,17 @@ pub struct Corpse {
}

#[derive(SplitFields, Debug)]
#[split(clone)] // implement clone method for the `ParticleRef` generated struct to clone the data into a `Particle`
#[split(to_owned, clone)]
pub struct Particle {
pub pos: (f32, f32),
pub time: f32,
}

#[derive(Clone)]
pub struct Stunned {
pub timer: f32,
}

fn main() {
println!("Hello, example!");

Expand All @@ -48,13 +58,16 @@ fn main() {
tick: 7,
damage: None,
});
world.units.insert(Unit {
let enemy_id = world.units.insert(Unit {
pos: (1.0, -2.0),
health: 15.0,
tick: 3,
damage: Some(1.5),
});

// Insert a dynamic component
world.units.insert_dyn(enemy_id, Stunned { timer: 1.0 });

world.corpses.insert(Corpse {
unit: Unit {
pos: (-4.0, 3.0),
Expand Down
5 changes: 3 additions & 2 deletions examples/generics.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use stecs::prelude::*;

#[derive(Clone)]
pub struct World<'a> {
pub units: StructOf<Dense<Unit<'a>>>,
}

#[derive(SplitFields)]
#[split(debug, clone)]
#[split(debug, to_owned, clone)]
pub struct Position<F: 'static> {
pub x: F,
pub y: F,
}

#[derive(SplitFields)]
#[split(debug, clone)]
#[split(debug, to_owned, clone)]
pub struct Unit<'a> {
#[split(nested)]
pub position: Position<f32>,
Expand Down
91 changes: 83 additions & 8 deletions src/dynamic/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,98 @@
use crate::storage::{sparse::Sparse, Storage, StorageFamily};

use std::marker::PhantomData;
use std::{any::Any, marker::PhantomData};

use anymap3::{CloneAny, Map};

// TODO: optional clone
type InnerMap<F, T> = Sparse<T, <F as StorageFamily>::Id>;

/// A storage of dynamic components.
///
/// **NOTE**: you likely do not need to use it manually,
/// as it is used internally by the generated archetypes.
pub struct DynamicStorage<F> {
inner: Map<dyn CloneAny>,
inner: Map<dyn Any>,
id: PhantomData<F>,
}

type InnerMap<F, T> = Sparse<T, <F as StorageFamily>::Id>;
impl<F> Default for DynamicStorage<F> {
fn default() -> Self {
Self::new()
}
}

impl<Id> DynamicStorage<Id> {
pub fn new() -> Self {
Self {
inner: Map::new(),
id: PhantomData,
}
}
}

impl<F: StorageFamily> DynamicStorage<F> {
/// Insert a dynamic component into an entity.
pub fn insert<T>(&mut self, entity_id: F::Id, component: T)
where
InnerMap<F, T>: Any,
{
self.inner
.entry::<InnerMap<F, T>>()
.or_default()
.insert(entity_id, component)
}

impl<F> Clone for DynamicStorage<F> {
/// Remove a dynamic component from an entity.
pub fn remove<T>(&mut self, entity_id: F::Id) -> Option<T>
where
InnerMap<F, T>: Any,
{
self.inner.get_mut::<InnerMap<F, T>>()?.remove(entity_id)
}

/// Get a reference to a dynamic component from an entity.
pub fn get<T>(&self, entity_id: F::Id) -> Option<&T>
where
InnerMap<F, T>: Any,
{
self.inner.get::<InnerMap<F, T>>()?.get(entity_id)
}

/// Get a mutable reference a dynamic component from an entity.
pub fn get_mut<T>(&mut self, entity_id: F::Id) -> Option<&mut T>
where
InnerMap<F, T>: Any,
{
self.inner.get_mut::<InnerMap<F, T>>()?.get_mut(entity_id)
}

/// Get mutable references to components corresponding to the id's in the iterator.
///
/// # Safety
/// The given `ids` must not repeat.
///
pub unsafe fn get_many_mut<'a, T: 'a>(
&'a mut self,
ids: impl Iterator<Item = F::Id>,
) -> impl Iterator<Item = Option<&'a mut T>>
where
InnerMap<F, T>: Any,
{
let inner = self.inner.entry::<InnerMap<F, T>>().or_default();
ids.map(move |id| inner.get_mut(id).map(|r| unsafe { &mut *(r as *mut T) }))
}
}

/// A storage of dynamic Clone-able components.
///
/// **NOTE**: you likely do not need to use it manually,
/// as it is used internally by the generated archetypes.
pub struct DynamicCloneStorage<F> {
inner: Map<dyn CloneAny>,
id: PhantomData<F>,
}

impl<F> Clone for DynamicCloneStorage<F> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
Expand All @@ -26,13 +101,13 @@ impl<F> Clone for DynamicStorage<F> {
}
}

impl<F> Default for DynamicStorage<F> {
impl<F> Default for DynamicCloneStorage<F> {
fn default() -> Self {
Self::new()
}
}

impl<Id> DynamicStorage<Id> {
impl<Id> DynamicCloneStorage<Id> {
pub fn new() -> Self {
Self {
inner: Map::new(),
Expand All @@ -41,7 +116,7 @@ impl<Id> DynamicStorage<Id> {
}
}

impl<F: StorageFamily> DynamicStorage<F> {
impl<F: StorageFamily> DynamicCloneStorage<F> {
/// Insert a dynamic component into an entity.
pub fn insert<T>(&mut self, entity_id: F::Id, component: T)
where
Expand Down

0 comments on commit c11bfe6

Please sign in to comment.