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

[Proof of concept] derive specs system #499

Closed
wants to merge 2 commits into from
Closed
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
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ required-features = ["common"]
name = "saveload"
required-features = ["serde"]

[[example]]
name = "system_derive_simple"

[[example]]
name = "system_derive_with_components"

[[bench]]
name = "benches_main"
harness = false
Expand Down
17 changes: 9 additions & 8 deletions benches/storage_sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,15 @@ macro_rules! gap {
c.bench_function(
&format!("sparse insert {}/{}", $sparsity, stringify!($storage)),
|b| insert(b),
).bench_function(
&format!("sparse remove {}/{}", $sparsity, stringify!($storage)),
|b| remove(b),
)
.bench_function(
&format!("sparse get {}/{}", $sparsity, stringify!($storage)),
|b| get(b),
);
)
.bench_function(
&format!("sparse remove {}/{}", $sparsity, stringify!($storage)),
|b| remove(b),
)
.bench_function(
&format!("sparse get {}/{}", $sparsity, stringify!($storage)),
|b| get(b),
);
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion examples/cluster_bomb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ extern crate rand;
extern crate rayon;
extern crate specs;

use rand::Rng;
use rand::distributions::{Distribution, Range};
use rand::Rng;

use rayon::iter::ParallelIterator;

Expand Down
9 changes: 7 additions & 2 deletions examples/full.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,11 @@ impl<'a> System<'a> for SysStoreMax {
struct JoinParallel;

impl<'a> System<'a> for JoinParallel {
type SystemData = (ReadStorage<'a, CompBool>, ReadStorage<'a, CompInt>, WriteStorage<'a, CompFloat>);
type SystemData = (
ReadStorage<'a, CompBool>,
ReadStorage<'a, CompInt>,
WriteStorage<'a, CompFloat>,
);

fn run(&mut self, (comp_bool, comp_int, mut comp_float): Self::SystemData) {
use rayon::prelude::*;
Expand Down Expand Up @@ -239,7 +243,8 @@ fn main() {
.with(CompBool(false))
.build();
// build() returns an entity, we will use it later to perform a deletion
let e = w.create_entity()
let e = w
.create_entity()
.with(CompInt(9))
.with(CompBool(true))
.build();
Expand Down
4 changes: 2 additions & 2 deletions examples/ordered_track.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ impl<'a> System<'a> for SysA {
ComponentEvent::Modified(id) => {
let entity = entities.entity(*id);
if let Some(component) = tracked.get(entity) {
// This is safe because it can only occur after an `Inserted` event, not a
// This is safe because it can only occur after an `Inserted` event, not a
// `Removed` event.
*self.cache.get_mut(id).unwrap() = (entity, component.0);
*self.cache.get_mut(id).unwrap() = (entity, component.0);
println!("{:?} was changed to {:?}", entity, component.0);
} else {
println!(
Expand Down
20 changes: 12 additions & 8 deletions examples/saveload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use std::fmt;

use specs::error::NoError;
use specs::prelude::*;
use specs::saveload::{DeserializeComponents, MarkedBuilder, SerializeComponents, U64Marker, U64MarkerAllocator};
use specs::saveload::{
DeserializeComponents, MarkedBuilder, SerializeComponents, U64Marker, U64MarkerAllocator,
};

// This is an example of how the serialized data of two entities might look on disk.
//
Expand Down Expand Up @@ -82,7 +84,7 @@ impl From<ron::ser::Error> for Combined {
}
}

// This cannot be called.
// This cannot be called.
impl From<NoError> for Combined {
fn from(e: NoError) -> Self {
match e {}
Expand Down Expand Up @@ -142,11 +144,11 @@ fn main() {
let mut ser = ron::ser::Serializer::new(Some(Default::default()), true);

// For serialization we use the [`SerializeComponents`](struct.SerializeComponents.html)-trait's `serialize` function.
// It takes two generic parameters:
// It takes two generic parameters:
// * An unbound type -> `NoError` (However, the serialize function expects it to be bound by the `Display`-trait)
// * A type implementing the `Marker`-trait -> [U64Marker](struct.U64Marker.html) (a convenient, predefined marker)
//
// The first parameter resembles the `.join()` syntax from other specs-systems,
// The first parameter resembles the `.join()` syntax from other specs-systems,
// every component that should be serialized has to be put inside a tuple.
//
// The second and third parameters are just the entity-storage and marker-storage, which get `.join()`ed internally.
Expand All @@ -157,7 +159,8 @@ fn main() {
&ents,
&markers,
&mut ser,
).unwrap_or_else(|e| eprintln!("Error: {}", e));
)
.unwrap_or_else(|e| eprintln!("Error: {}", e));
// TODO: Specs should return an error which combines serialization
// and component errors.

Expand All @@ -167,7 +170,7 @@ fn main() {
}
}

// Running the system results in a print to the standard output channel, in `.ron`-format,
// Running the system results in a print to the standard output channel, in `.ron`-format,
// showing how the serialized dummy entities look like.
Serialize.run_now(&world.res);

Expand All @@ -189,7 +192,7 @@ fn main() {

fn run(&mut self, (ent, mut alloc, pos, mass, mut markers): Self::SystemData) {
// The `const ENTITIES: &str` at the top of this file was formatted according to the `.ron`-specs,
// therefore we need a `.ron`-deserializer.
// therefore we need a `.ron`-deserializer.
// Others can be used, as long as they implement the `serde::de::Deserializer`-trait.
use ron::de::Deserializer;

Expand All @@ -206,7 +209,8 @@ fn main() {
&mut markers,
&mut alloc,
&mut de,
).unwrap_or_else(|e| eprintln!("Error: {}", e));
)
.unwrap_or_else(|e| eprintln!("Error: {}", e));
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions examples/system_derive_simple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
extern crate specs;
#[macro_use]
extern crate specs_derive;

use specs::prelude::*;

#[specs_system(SysPrint)]
fn sys_print() {
println!("Yo! I'm SysPrint");
}

fn main() {
let mut world = World::new();

// Create a dispatcher with the generated system.
let mut dispatcher = DispatcherBuilder::new().with(SysPrint, "sys_print", &[]).build();

dispatcher.setup(&mut world.res);

// This dispatches all the systems in parallel (but blocking).
dispatcher.dispatch(&world.res);
}
70 changes: 70 additions & 0 deletions examples/system_derive_with_components.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
extern crate specs;
#[macro_use]
extern crate specs_derive;

use specs::prelude::*;

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

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

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

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

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

/// Increments entities' position by their velocity.
///
/// The function parameters are the resources required for execution.
#[specs_system(SysA)]
fn sys_a(mut pos: WriteStorage<Pos>, vel: ReadStorage<Vel>) {
for (pos, vel) in (&mut pos, &vel).join() {
pos.0 += vel.0;
}
}

#[specs_system(SysPrint)]
fn sys_print(entities: Entities, pos: ReadStorage<Pos>, vel: ReadStorage<Vel>) {
for (e, pos, vel) in (&entities, &pos, &vel).join() {
println!("{:?}: Pos: {:?} Vel: {:?}", e, pos, vel);
}
}

fn main() {
// The `World` is our
// container for components
// and other resources.

let mut world = World::new();

// This builds a dispatcher.
// The third parameter of `add` specifies
// logical dependencies on other systems.
// Since we only have one, we don't depend on anything.
// See the `full` example for dependencies.
let mut dispatcher = DispatcherBuilder::new()
.with(SysA, "sys_a", &[])
.with(SysPrint, "sys_print", &["sys_a"])
.build();

// setup() must be called before creating any entity, it will register
// all Components and Resources that Systems depend on
dispatcher.setup(&mut world.res);

// An entity may or may not contain some component.

world.create_entity().with(Vel(2.0)).with(Pos(0.0)).build();
world.create_entity().with(Vel(4.0)).with(Pos(1.6)).build();
world.create_entity().with(Vel(1.5)).with(Pos(5.4)).build();

// This dispatches all the systems in parallel (but blocking).
dispatcher.dispatch(&world.res);
dispatcher.dispatch(&world.res);
}
28 changes: 16 additions & 12 deletions specs-derive/src/impl_saveload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
// NOTE: All examples given in the documentation below are "cleaned up" into readable Rust,
// so it doesn't give an entirely accurate view of what's actually generated.

use syn::{DataEnum, DataStruct, DeriveInput, Field, Generics, Ident, Type, GenericParam, WhereClause, WherePredicate};
use proc_macro2::{Span, TokenStream};
use syn::{
DataEnum, DataStruct, DeriveInput, Field, GenericParam, Generics, Ident, Type, WhereClause,
WherePredicate,
};

/// Handy collection since tuples got unwieldy and
/// unclear in purpose
Expand All @@ -24,25 +27,25 @@ pub fn impl_saveload(ast: &mut DeriveInput) -> TokenStream {
add_where_clauses(
&mut ast.generics.where_clause,
&ast.generics.params,
|ty| parse_quote!(#ty: ConvertSaveload<MA, Error = NoError> + ConvertSaveload<MA, Error = NoError>)
|ty| parse_quote!(#ty: ConvertSaveload<MA, Error = NoError> + ConvertSaveload<MA, Error = NoError>),
);
add_where_clauses(
&mut ast.generics.where_clause,
&ast.generics.params,
|ty| parse_quote!(<#ty as ConvertSaveload<MA>>::Data: ::serde::Serialize + ::serde::de::DeserializeOwned + Clone)
|ty| parse_quote!(<#ty as ConvertSaveload<MA>>::Data: ::serde::Serialize + ::serde::de::DeserializeOwned + Clone),
);
add_where_clauses(
&mut ast.generics.where_clause,
&ast.generics.params,
|ty| parse_quote!(<#ty as ConvertSaveload<MA>>::Data: ::serde::Serialize + ::serde::de::DeserializeOwned + Clone)
|ty| parse_quote!(<#ty as ConvertSaveload<MA>>::Data: ::serde::Serialize + ::serde::de::DeserializeOwned + Clone),
);
add_where_clause(
&mut ast.generics.where_clause,
parse_quote!(MA: ::serde::Serialize + ::serde::de::DeserializeOwned + Marker)
parse_quote!(MA: ::serde::Serialize + ::serde::de::DeserializeOwned + Marker),
);
add_where_clause(
&mut ast.generics.where_clause,
parse_quote!(for <'deser> MA: ::serde::Deserialize<'deser>)
parse_quote!(for <'deser> MA: ::serde::Deserialize<'deser>),
);

let derive = match ast.data {
Expand Down Expand Up @@ -400,13 +403,12 @@ fn saveload_enum(data: &DataEnum, name: &Ident, generics: &Generics) -> Saveload

/// Adds where clause for each type parameter
fn add_where_clauses<'a, F, I>(where_clause: &mut Option<WhereClause>, generics: I, mut clause: F)
where F: FnMut(Ident) -> WherePredicate,
I: IntoIterator<Item = &'a GenericParam>,
where
F: FnMut(Ident) -> WherePredicate,
I: IntoIterator<Item = &'a GenericParam>,
{
use syn::GenericParam;
let preds = &mut where_clause
.get_or_insert(parse_quote!(where))
.predicates;
let preds = &mut where_clause.get_or_insert(parse_quote!(where)).predicates;
for generic in generics {
if let GenericParam::Type(ty_param) = generic {
let ty_param = ty_param.ident.clone();
Expand Down Expand Up @@ -441,7 +443,9 @@ fn replace_entity_type(ty: &mut Type) {

Type::TraitObject(_) => {}
Type::ImplTrait(_) => {}
Type::Slice(_) => panic!("Slices are unsupported, use owned types like Vecs or Arrays instead"),
Type::Slice(_) => {
panic!("Slices are unsupported, use owned types like Vecs or Arrays instead")
}
Type::Reference(_) => panic!("References are unsupported"),
Type::Ptr(_) => panic!("Raw pointer types are unsupported"),
Type::BareFn(_) => panic!("Function types are unsupported"),
Expand Down
Loading