Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom derive for Component trait #192

Merged
merged 7 commits into from
Jul 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Cargo.lock
# Generated by mdbook
/book/book/

# Generated by rustfmt
*.bk

# IDEs / Editor
*.iml
.idea
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## 0.9.3 (Unreleased)
* Add `specs-derive` crate, custom `#[derive]` for components ([#192])
* Add lazy updates: insert and remove components, execute closures on world ([#214], [#221])

[#192]: https://github.com/slide-rs/specs/pull/192
[#214]: https://github.com/slide-rs/specs/pull/214
[#221]: https://github.com/slide-rs/specs/pull/221

Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ required-features = ["common"]
[[example]]
name = "serialize"
required-features = ["serialize"]

[workspace]
members = ["specs-derive"]
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ Unlike most other ECS libraries out there, it provides
```rust
// A component contains data
// which is associated with an entity.

#[derive(Debug)]
struct Vel(f32);
#[derive(Debug)]
struct Pos(f32);

impl Component for Vel {
type Storage = VecStorage<Self>;
}

#[derive(Debug)]
struct Pos(f32);

impl Component for Pos {
type Storage = VecStorage<Self>;
}
Expand Down
8 changes: 3 additions & 5 deletions benches/parallel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
extern crate cgmath;
extern crate rand;
extern crate specs;

extern crate test;

use cgmath::Vector2;
use rand::thread_rng;
use specs::{Component, DenseVecStorage, DispatcherBuilder, Entities, Entity, Fetch,
HashMapStorage, Join, NullStorage, ReadStorage, RunningTime, System, VecStorage,
World, WriteStorage};

use specs::{Component, DenseVecStorage, DispatcherBuilder, Entities, Entity,
Fetch, HashMapStorage, Join, NullStorage, ReadStorage, RunningTime,
System, VecStorage, World, WriteStorage};
use test::Bencher;

type Vec2 = Vector2<f32>;
Expand Down
1 change: 0 additions & 1 deletion benches/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ extern crate cgmath;
extern crate rand;
extern crate rayon;
extern crate specs;

extern crate test;

use specs::{Component, HashMapStorage, Join, ParJoin, VecStorage, World};
Expand Down
41 changes: 30 additions & 11 deletions book/src/02_hello_world.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,31 @@ impl Component for Velocity {
}
```

These will be our components, stored in a `VecStorage`
(see [the storages chapter][sc] for more details), so we can associate some data
with an entity. Before doing that, we need to create a world, because this is
where the storage for all the components is located.
These will be our two component types. Optionally, the `specs-derive` crate
provides a convenient custom `#[derive]` you can use to define component types
more succinctly:

```rust,ignore
#[derive(Component, Debug)]
#[component(VecStorage)]
struct Position {
x: f32,
y: f32
}

#[derive(Component, Debug)]
#[component(VecStorage)]
struct Velocity {
x: f32,
y: f32,
}
```

If the `#[component(...)]` attribute is omitted, the given component will be
stored in a `DenseVecStorage` by default. But for this example, we are
explicitly asking for these components to be kept in a `VecStorage` instead (see
the later [storages chapter][sc] for more details). But before we move on, we
need to create a world in which to store all of our components.

[sc]: ./05_storages.html

Expand Down Expand Up @@ -114,9 +135,8 @@ Note that all components that a system accesses must be registered with
`world.register::<Component>()` before that system is run, or you will get a
panic.

> There are many other types you can use as system
data. Please see the [System Data Chapter][cs] for more
information.
> There are many other types you can use as system data. Please see the
> [System Data Chapter][cs] for more information.

[cs]: ./06_system_data.html

Expand Down Expand Up @@ -186,10 +206,9 @@ fn main() {

---

This was a pretty basic example so far. A key feature we
haven't seen is the `Dispatcher`, which allows to
configure run systems in parallel (and it offers some other
nice features, too).
This was a pretty basic example so far. A key feature we haven't seen is the
`Dispatcher`, which allows to configure run systems in parallel (and it offers
some other nice features, too).

Let's see how that works in [Chapter 3: Dispatcher][c3].

Expand Down
3 changes: 1 addition & 2 deletions book/src/04_resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ Note that all resources that a system accesses must be registered with
`world.add_resource(resource)` before that system is run, or you will get a
panic.

For more information on `SystemData`, see [the
system data chapter][cs].
For more information on `SystemData`, see [the system data chapter][cs].

[cs]: ./06_system_data.html

Expand Down
12 changes: 6 additions & 6 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
extern crate specs;

use specs::{Component, DispatcherBuilder, Join, ReadStorage, System, VecStorage, World,
WriteStorage};
use specs::{Component, DispatcherBuilder, Join, ReadStorage, System, VecStorage,
World, WriteStorage};

// A component contains data
// which is associated with an entity.
// A component contains data which is associated with an entity.

#[derive(Debug)]
struct Vel(f32);
#[derive(Debug)]
struct Pos(f32);

impl Component for Vel {
type Storage = VecStorage<Self>;
}

#[derive(Debug)]
struct Pos(f32);

impl Component for Pos {
type Storage = VecStorage<Self>;
}
Expand Down
16 changes: 16 additions & 0 deletions specs-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "specs-derive"
version = "0.1.0"
authors = ["Eyal Kalderon <[email protected]>"]
description = "Custom derive macro for Specs components"
documentation = "https://docs.rs/specs-derive"
repository = "https://github.com/slide-rs/specs/specs-derive"
keywords = ["gamedev", "parallel", "specs", "ecs", "derive"]
license = "MIT/Apache-2.0"

[dependencies]
syn = "0.11"
quote = "0.3"

[lib]
proc-macro = true
65 changes: 65 additions & 0 deletions specs-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! Implements the `#[derive(Component)]` macro and `#[component]` attribute for
//! [Specs][sp].
//!
//! [sp]: https://slide-rs.github.io/specs-website/

extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use syn::{Ident, MacroInput, MetaItem, NestedMetaItem};
use quote::Tokens;

/// Custom derive macro for the `Component` trait.
///
/// ## Example
///
/// ```
/// #[derive(Component, Debug)]
/// struct Pos(f32, f32, f32);
/// ```
///
/// The macro will store components in `DenseVecStorage`s by default. To specify
/// a different storage type, you may use the `#[component]` attribute.
///
/// ```
/// #[derive(Component, Debug)]
/// #[component(HashMapStorage)]
/// struct Pos(f32, f32, f32);
/// ```
#[proc_macro_derive(Component, attributes(component))]
pub fn component(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = impl_component(&ast);
gen.parse().unwrap()
}

fn impl_component(ast: &MacroInput) -> Tokens {
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let storage = ast.attrs.first()
.and_then(|attr| match attr.value {
MetaItem::List(ref ident, ref items) if ident == "component" => items.first(),
_ => None,
})
.and_then(|attr| match *attr {
NestedMetaItem::MetaItem(ref item) => Some(item),
_ => None,
})
.and_then(|attr| match *attr {
MetaItem::Word(ref ident) => Some(ident),
_ => None,
})
.cloned()
.unwrap_or(Ident::new("::specs::DenseVecStorage"));

quote! {
impl #impl_generics ::specs::Component for #name #ty_generics #where_clause {
type Storage = #storage<#name>;
}
}
}
23 changes: 11 additions & 12 deletions src/join.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std;
use std::cell::UnsafeCell;

use hibitset::{BitSetAnd, BitIter, BitSetLike, BitProducer};
use hibitset::{BitIter, BitProducer, BitSetAnd, BitSetLike};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, forgot to reorder this one.

use rayon::iter::ParallelIterator;
use rayon::iter::internal::{Folder, UnindexedConsumer, UnindexedProducer, bridge_unindexed};
use tuple_utils::Split;
Expand Down Expand Up @@ -135,7 +135,7 @@ impl<J> ParallelIterator for JoinParIter<J>
where J: Join + Send,
J::Mask: Send + Sync,
J::Type: Send,
J::Value: Send,
J::Value: Send
{
type Item = J::Type;

Expand All @@ -154,7 +154,7 @@ struct JoinProducer<'a, J>
where J: Join + Send,
J::Mask: Send + Sync + 'a,
J::Type: Send,
J::Value: Send + 'a,
J::Value: Send + 'a
{
keys: BitProducer<'a, J::Mask>,
values: &'a UnsafeCell<J::Value>,
Expand Down Expand Up @@ -202,15 +202,14 @@ impl<'a, J> UnindexedProducer for JoinProducer<'a, J>
where F: Folder<Self::Item>
{
let JoinProducer { values, keys, .. } = self;
let iter = keys.0
.map(|idx| unsafe {
// This unsafe block should be safe if the `J::get`
// can be safely called from different threads with distinct indices.

// The indices here are guaranteed to be distinct because of the fact
// that the bit set is split.
J::get(&mut *values.get(), idx)
});
let iter = keys.0.map(|idx| unsafe {
// This unsafe block should be safe if the `J::get`
// can be safely called from different threads with distinct indices.

// The indices here are guaranteed to be distinct because of the fact
// that the bit set is split.
J::get(&mut *values.get(), idx)
});

folder.consume_iter(iter)
}
Expand Down
23 changes: 17 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@
//! }
//! ```
//!
//! Or alternatively, if you import the `specs-derive` crate, you can use a
//! custom `#[derive]` macro:
//!
//! ```rust,ignore
//! #[derive(Component)]
//! #[component(VecStorage)]
//! struct MyComp;
//! ```
//!
//! You can choose different storages according to your needs.
//!
//! These storages can be [`join`]ed together, for example joining a `Velocity`
Expand Down Expand Up @@ -77,18 +86,20 @@
//! ```rust
//! extern crate specs;
//!
//! use specs::{DispatcherBuilder, Component, Join, ReadStorage, System, VecStorage, WriteStorage,
//! World};
//! use specs::{Component, DispatcherBuilder, Join, ReadStorage, System, VecStorage,
//! WriteStorage, World};
//!
//! // A component contains data which is
//! // associated with an entity.
//!
//! // A component contains data
//! // which is associated with an entity.
//! struct Vel(f32);
//! struct Pos(f32);
//!
//! impl Component for Vel {
//! type Storage = VecStorage<Self>;
//! }
//!
//! struct Pos(f32);
//!
//! impl Component for Pos {
//! type Storage = VecStorage<Self>;
//! }
Expand Down Expand Up @@ -172,9 +183,9 @@ extern crate crossbeam;
extern crate fnv;
extern crate hibitset;
extern crate mopa;
extern crate rayon;
extern crate shred;
extern crate tuple_utils;
extern crate rayon;

#[cfg(feature = "common")]
extern crate futures;
Expand Down
6 changes: 3 additions & 3 deletions src/storage/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ mod test {

for i in 0..1_000 {
*s.get_mut(Entity::new(i, Generation::new(1)))
.unwrap()
.as_mut() -= 718;
.unwrap()
.as_mut() -= 718;
}

for i in 0..1_000 {
Expand Down Expand Up @@ -497,7 +497,7 @@ mod test {
}
}

#[cfg(feature="serialize")]
#[cfg(feature = "serialize")]
mod serialize_test {
extern crate serde_json;

Expand Down
Loading