From 815d04da602c58ed8b13eeb612fe73180204039d Mon Sep 17 00:00:00 2001 From: HampusM Date: Mon, 26 Feb 2024 20:05:27 +0100 Subject: fix(ecs): make Component trait not automatic & add derive macro --- Cargo.lock | 14 ++++++- Cargo.toml | 2 +- ecs-macros/Cargo.toml | 13 +++++++ ecs-macros/src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++++ ecs/Cargo.toml | 1 + ecs/examples/multiple_queries.rs | 5 ++- ecs/examples/simple.rs | 29 +++++++++++---- ecs/examples/with_local.rs | 23 +++++------- ecs/src/component.rs | 53 ++++++++++----------------- ecs/src/lib.rs | 2 + 10 files changed, 164 insertions(+), 57 deletions(-) create mode 100644 ecs-macros/Cargo.toml create mode 100644 ecs-macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0498fd9..e7a1b5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,12 +122,22 @@ dependencies = [ name = "ecs" version = "0.1.0" dependencies = [ + "ecs-macros", "itertools", "proc-macro2", "quote", "seq-macro", ] +[[package]] +name = "ecs-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.10.0" @@ -471,9 +481,9 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 373c4ab..fccec2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [workspace] -members = ["glfw", "engine", "ecs"] +members = ["glfw", "engine", "ecs", "ecs-macros"] [dependencies] engine = { path = "./engine", features = ["debug"] } diff --git a/ecs-macros/Cargo.toml b/ecs-macros/Cargo.toml new file mode 100644 index 0000000..792ca34 --- /dev/null +++ b/ecs-macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "ecs-macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.35" +syn = { version = "2.0.51", features = ["full"] } +proc-macro2 = "1.0.78" + diff --git a/ecs-macros/src/lib.rs b/ecs-macros/src/lib.rs new file mode 100644 index 0000000..e37d6a4 --- /dev/null +++ b/ecs-macros/src/lib.rs @@ -0,0 +1,79 @@ +use proc_macro::TokenStream; +use quote::{quote, ToTokens}; +use syn::spanned::Spanned; +use syn::{parse, Ident, Item, ItemEnum, ItemStruct, ItemUnion}; + +#[proc_macro_derive(Component)] +pub fn component_derive(input: TokenStream) -> TokenStream +{ + let item: TypeItem = parse::(input).unwrap().try_into().unwrap(); + + let item_ident = item.ident(); + + quote! { + impl ecs::component::Component for #item_ident + { + fn as_any_mut(&mut self) -> &mut dyn std::any::Any + { + self + } + + fn as_any(&self) -> &dyn std::any::Any + { + self + } + } + + impl ecs::system::Input for #item_ident {} + } + .into() +} + +enum TypeItem +{ + Struct(ItemStruct), + Enum(ItemEnum), + Union(ItemUnion), +} + +impl TypeItem +{ + fn ident(&self) -> &Ident + { + match self { + Self::Struct(struct_item) => &struct_item.ident, + Self::Enum(enum_item) => &enum_item.ident, + Self::Union(union_item) => &union_item.ident, + } + } +} + +impl TryFrom for TypeItem +{ + type Error = syn::Error; + + fn try_from(item: Item) -> Result + { + match item { + Item::Struct(struct_item) => Ok(Self::Struct(struct_item)), + Item::Enum(enum_item) => Ok(Self::Enum(enum_item)), + Item::Union(union_item) => Ok(Self::Union(union_item)), + _ => Err(syn::Error::new( + item.span(), + "Expected a struct, a enum or a union", + )), + } + } +} + +impl ToTokens for TypeItem +{ + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) + { + match self { + Self::Struct(struct_item) => struct_item.to_tokens(tokens), + Self::Enum(enum_item) => enum_item.to_tokens(tokens), + Self::Union(union_item) => union_item.to_tokens(tokens), + } + } +} diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml index 8cfdb71..ba31a1d 100644 --- a/ecs/Cargo.toml +++ b/ecs/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] seq-macro = "0.3.5" +ecs-macros = { path = "../ecs-macros" } [build-dependencies] quote = "1.0.35" diff --git a/ecs/examples/multiple_queries.rs b/ecs/examples/multiple_queries.rs index a4a5d2d..4f4198a 100644 --- a/ecs/examples/multiple_queries.rs +++ b/ecs/examples/multiple_queries.rs @@ -1,18 +1,21 @@ use std::fmt::Display; -use ecs::{Query, World}; +use ecs::{Component, Query, World}; +#[derive(Component)] struct Health { health: u32, } +#[derive(Component)] enum AttackStrength { Strong, Weak, } +#[derive(Component)] struct EnemyName { name: String, diff --git a/ecs/examples/simple.rs b/ecs/examples/simple.rs index b58d2ba..b8dc50f 100644 --- a/ecs/examples/simple.rs +++ b/ecs/examples/simple.rs @@ -1,14 +1,21 @@ -use ecs::{Query, World}; +use ecs::{Component, Query, World}; +#[derive(Component)] struct SomeData { num: u64, } -fn say_hello(mut query: Query<(SomeData, String)>) +#[derive(Component)] +struct Greeting { - for (data, text) in query.iter_mut() { - println!("Hello {}: {}", text, data.num); + greeting: String, +} + +fn say_hello(mut query: Query<(SomeData, Greeting)>) +{ + for (data, greeting) in query.iter_mut() { + println!("{}: {}", greeting.greeting, data.num); } } @@ -24,9 +31,17 @@ fn main() world.register_system(Event::Start, say_hello); - world.create_entity((SomeData { num: 987_654 }, "Yoo".to_string())); - - world.create_entity((SomeData { num: 345 }, "Haha".to_string())); + world.create_entity(( + SomeData { num: 987_654 }, + Greeting { + greeting: "Good afternoon".to_string(), + }, + )); + + world.create_entity(( + SomeData { num: 345 }, + Greeting { greeting: "Good evening".to_string() }, + )); world.emit(&Event::Start); } diff --git a/ecs/examples/with_local.rs b/ecs/examples/with_local.rs index 0bd8f66..334f129 100644 --- a/ecs/examples/with_local.rs +++ b/ecs/examples/with_local.rs @@ -1,28 +1,29 @@ use ecs::component::Local; -use ecs::system::{Input as SystemInput, Into, System}; -use ecs::{Query, World}; +use ecs::system::{Into, System}; +use ecs::{Component, Query, World}; +#[derive(Component)] struct SomeData { num: u64, } +#[derive(Component)] struct Name { name: String, } +#[derive(Component)] struct SayHelloState { cnt: usize, } -impl SystemInput for SayHelloState {} - -fn say_hello(mut query: Query<(SomeData, String)>, mut state: Local) +fn say_hello(mut query: Query<(SomeData,)>, mut state: Local) { - for (data, text) in query.iter_mut() { - println!("Hello there. Count {}: {}: {}", state.cnt, text, data.num); + for (data,) in query.iter_mut() { + println!("Hello there. Count {}: {}", state.cnt, data.num); state.cnt += 1; } @@ -64,13 +65,9 @@ fn main() .initialize((SayHelloState { cnt: 0 },)), ); - world.create_entity(( - SomeData { num: 987_654 }, - "Yoo".to_string(), - Name { name: "Bob".to_string() }, - )); + world.create_entity((SomeData { num: 987_654 }, Name { name: "Bob".to_string() })); - world.create_entity((SomeData { num: 345 }, "Haha".to_string())); + world.create_entity((SomeData { num: 345 },)); world.emit(&Event::Update); diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 59b737e..70ce9ba 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -7,7 +7,7 @@ use seq_macro::seq; use crate::system::{Input as SystemInput, Param as SystemParam, System}; use crate::ComponentStorage; -pub trait Component: Any +pub trait Component: SystemInput + Any { #[doc(hidden)] fn as_any_mut(&mut self) -> &mut dyn Any; @@ -16,19 +16,6 @@ pub trait Component: Any fn as_any(&self) -> &dyn Any; } -impl Component for Value -{ - fn as_any_mut(&mut self) -> &mut dyn Any - { - self - } - - fn as_any(&self) -> &dyn Any - { - self - } -} - impl dyn Component { pub fn downcast_mut(&mut self) -> Option<&mut Real> @@ -117,27 +104,27 @@ seq!(C in 0..=64 { /// Holds a component which is local to a single system. #[derive(Debug)] -pub struct Local<'world, Value: SystemInput> +pub struct Local<'world, LocalComponent: Component> { - value: &'world mut Value, + local_component: &'world mut LocalComponent, } -impl<'world, Value> Local<'world, Value> +impl<'world, LocalComponent> Local<'world, LocalComponent> where - Value: SystemInput, + LocalComponent: Component, { - fn new(value: &'world mut Value) -> Self + fn new(local_component: &'world mut LocalComponent) -> Self { - Self { value } + Self { local_component } } } -unsafe impl<'world, Value: 'static> SystemParam<'world> for Local<'world, Value> +unsafe impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent> where - Value: SystemInput, + LocalComponent: Component, { type Flags = (); - type Input = Value; + type Input = LocalComponent; fn initialize(system: &mut impl System, input: Self::Input) { @@ -150,7 +137,7 @@ where ) -> Self { let local_component = system - .get_local_component_mut::() + .get_local_component_mut::() .expect("Local component is uninitialized"); Self::new(local_component) @@ -164,33 +151,33 @@ where return true; }; - TypeId::of::() != *other_type_id + TypeId::of::() != *other_type_id } fn get_comparable() -> Box { - Box::new(TypeId::of::()) + Box::new(TypeId::of::()) } } -impl<'world, Value> Deref for Local<'world, Value> +impl<'world, LocalComponent> Deref for Local<'world, LocalComponent> where - Value: SystemInput, + LocalComponent: Component, { - type Target = Value; + type Target = LocalComponent; fn deref(&self) -> &Self::Target { - self.value + self.local_component } } -impl<'world, Value> DerefMut for Local<'world, Value> +impl<'world, LocalComponent> DerefMut for Local<'world, LocalComponent> where - Value: SystemInput, + LocalComponent: Component, { fn deref_mut(&mut self) -> &mut Self::Target { - self.value + self.local_component } } diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 84009e0..573aa41 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -18,6 +18,8 @@ use crate::system::{ pub mod component; pub mod system; +pub use ecs_macros::Component; + #[derive(Debug)] struct Entity { -- cgit v1.2.3-18-g5258