diff --git a/Cargo.toml b/Cargo.toml index 7c5baa122..fba9238d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ shred-derive = "0.6.0" tuple_utils = "0.3" rayon = { version = "1.0.0", optional = true } nonzero_signed = "1.0.1" +uuid = { version = "0.7.4", optional = true, features = ["v4", "serde"] } serde = { version = "1.0", optional = true, features = ["serde_derive"] } @@ -41,9 +42,12 @@ serde = { version = "1.0", optional = true, features = ["serde_derive"] } default = ["parallel"] parallel = ["rayon", "shred/parallel", "hibitset/parallel"] nightly = ["shred/nightly"] +uuid_entity = ["uuid", "serde"] +stdweb = ["uuid/stdweb"] +wasm-bindgen = ["uuid/wasm-bindgen"] [package.metadata.docs.rs] -features = ["parallel", "nightly"] +features = ["parallel", "nightly", "uuid_entity"] [dev-dependencies] cgmath = { version = "0.17" } diff --git a/benches/big_or_small.rs b/benches/big_or_small.rs index acc4469fb..e74afd0a1 100644 --- a/benches/big_or_small.rs +++ b/benches/big_or_small.rs @@ -39,10 +39,7 @@ impl Component for Big { struct SmallSystem; impl<'a> System<'a> for SmallSystem { - type SystemData = ( - ReadStorage<'a, Small>, - WriteStorage<'a, Small2>, - ); + type SystemData = (ReadStorage<'a, Small>, WriteStorage<'a, Small2>); fn run(&mut self, (small, mut small2): Self::SystemData) { for (s, mut s2) in (&small, &mut small2).join() { @@ -54,9 +51,7 @@ impl<'a> System<'a> for SmallSystem { struct BigSystem; impl<'a> System<'a> for BigSystem { - type SystemData = ( - WriteStorage<'a, Big>, - ); + type SystemData = (WriteStorage<'a, Big>,); fn run(&mut self, (mut big,): Self::SystemData) { for (mut b,) in (&mut big,).join() { @@ -72,8 +67,14 @@ fn bench_big(b: &mut Bencher) { world.register::(); for _ in 0..100000 { - world.create_entity() - .with(Big(Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 0.0, 0.0))) + world + .create_entity() + .with(Big( + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, 0.0, 0.0), + )) .build(); } @@ -95,7 +96,8 @@ fn bench_small(b: &mut Bencher) { world.register::(); for _ in 0..100000 { - world.create_entity() + world + .create_entity() .with(Small(Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 0.0, 0.0))) .with(Small2(Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 0.0, 0.0))) .build(); diff --git a/src/lib.rs b/src/lib.rs index d9a144530..6a8b4067c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -199,6 +199,8 @@ extern crate nonzero_signed; extern crate rayon; extern crate shrev; extern crate tuple_utils; +#[cfg(feature = "uuid_entity")] +pub extern crate uuid; #[cfg(feature = "serde")] #[macro_use] diff --git a/src/saveload/mod.rs b/src/saveload/mod.rs index a1d37399a..20bb5f53e 100644 --- a/src/saveload/mod.rs +++ b/src/saveload/mod.rs @@ -33,7 +33,11 @@ mod marker; mod ser; #[cfg(test)] mod tests; +#[cfg(feature = "uuid_entity")] +mod uuid; +#[cfg(feature = "uuid_entity")] +pub use self::uuid::{UuidMarker, UuidMarkerAllocator}; pub use self::{ de::DeserializeComponents, marker::{MarkedBuilder, Marker, MarkerAllocator, U64Marker, U64MarkerAllocator}, diff --git a/src/saveload/tests.rs b/src/saveload/tests.rs index 556aad1e9..bed7e1117 100644 --- a/src/saveload/tests.rs +++ b/src/saveload/tests.rs @@ -1,5 +1,7 @@ extern crate ron; +use std::hash::Hash; + use super::*; use crate::{ error::{Error, NoError}, @@ -28,27 +30,39 @@ mod marker_test { /// deserialization. #[test] fn bumps_index_after_reload() { + bumps_index_after_reload_internal::(U64MarkerAllocator::new()); + #[cfg(feature = "uuid_entity")] + bumps_index_after_reload_internal::(UuidMarkerAllocator::new()); + } + + fn bumps_index_after_reload_internal(allocator: M::Allocator) + where + M: Marker + Component + Clone, + ::Storage: Default, + ::Identifier: Clone + Hash + Eq, + ::Allocator: Default + Clone, + { let mut world = World::new(); - world.add_resource(U64MarkerAllocator::new()); + world.add_resource(allocator.clone()); world.register::(); world.register::(); - world.register::(); + world.register::(); world .create_entity() .with(A(32)) .with(B(true)) - .marked::() + .marked::() .build(); world .create_entity() .with(A(64)) .with(B(false)) - .marked::() + .marked::() .build(); - // Serialze all entities + // Serialize all entities let mut ser = ron::ser::Serializer::new(Some(Default::default()), true); world.exec( @@ -56,10 +70,10 @@ mod marker_test { Entities, ReadStorage, ReadStorage, - ReadStorage, - Read, + ReadStorage, + Read, )| { - SerializeComponents::::serialize( + SerializeComponents::::serialize( &(&comp_a, &comp_b), &ents, &markers, @@ -73,21 +87,21 @@ mod marker_test { let mut de = ron::de::Deserializer::from_str(&serial).unwrap(); - // Throw the old world away and deserialzie into a new world + // Throw the old world away and deserialize into a new world let mut world = World::new(); - world.add_resource(U64MarkerAllocator::new()); + world.add_resource(allocator); world.register::(); world.register::(); - world.register::(); + world.register::(); world.exec( |(ents, comp_a, comp_b, mut markers, mut alloc): ( Entities, WriteStorage, WriteStorage, - WriteStorage, - Write, + WriteStorage, + Write, )| { DeserializeComponents::::deserialize( &mut (comp_a, comp_b), @@ -101,19 +115,19 @@ mod marker_test { ); // Two marked entities should be deserialized - assert_marked_entity_count(&mut world, 2); + assert_marked_entity_count::(&mut world, 2); // Queue lazy creation of 2 more entities world.exec(|(ents, lazy): (Entities, Read)| { lazy.create_entity(&ents) .with(A(128)) .with(B(false)) - .marked::() + .marked::() .build(); lazy.create_entity(&ents) .with(A(256)) .with(B(true)) - .marked::() + .marked::() .build(); }); @@ -122,30 +136,30 @@ mod marker_test { .create_entity() .with(A(512)) .with(B(false)) - .marked::() + .marked::() .build(); world .create_entity() .with(A(1024)) .with(B(true)) - .marked::() + .marked::() .build(); // Check that markers of deserialized entities and newly created entities are // unique - assert_marked_entity_count(&mut world, 4); - assert_markers_are_unique(&mut world); + assert_marked_entity_count::(&mut world, 4); + assert_markers_are_unique::(&mut world); // Check that markers of lazily created entities are unique world.maintain(); - assert_marked_entity_count(&mut world, 6); - assert_markers_are_unique(&mut world); + assert_marked_entity_count::(&mut world, 6); + assert_markers_are_unique::(&mut world); } /// Assert that the number of entities marked with `U64Marker` is equal to /// `count` - fn assert_marked_entity_count(world: &mut World, count: usize) { - world.exec(|(ents, markers): (Entities, ReadStorage)| { + fn assert_marked_entity_count(world: &mut World, count: usize) { + world.exec(|(ents, markers): (Entities, ReadStorage)| { let marked_entity_count = (&ents, &markers).join().count(); assert_eq!(marked_entity_count, count); @@ -153,8 +167,11 @@ mod marker_test { } /// Ensure there are no duplicate marker .ids() in the world - fn assert_markers_are_unique(world: &mut World) { - world.exec(|(ents, markers): (Entities, ReadStorage)| { + fn assert_markers_are_unique(world: &mut World) + where + ::Identifier: Clone + Eq + Hash, + { + world.exec(|(ents, markers): (Entities, ReadStorage)| { use std::collections::HashSet; let marker_ids: Vec<_> = (&ents, &markers) diff --git a/src/saveload/uuid.rs b/src/saveload/uuid.rs new file mode 100644 index 000000000..901f91fda --- /dev/null +++ b/src/saveload/uuid.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; + +use uuid::Uuid; + +use crate::{ + join::Join, + saveload::{Marker, MarkerAllocator}, + storage::{ReadStorage, VecStorage}, + world::{Component, EntitiesRes, Entity}, +}; + +/// Basic marker uuid implementation usable for saving and loading. +#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub struct UuidMarker { + uuid: Uuid, +} + +impl Component for UuidMarker { + type Storage = VecStorage; +} + +impl Marker for UuidMarker { + type Allocator = UuidMarkerAllocator; + type Identifier = Uuid; + + fn id(&self) -> Uuid { + self.uuid().clone() + } +} + +impl UuidMarker { + /// Creates a new `UuidMarker` Component from the specified uuid. + pub fn new(uuid: Uuid) -> Self { + UuidMarker { uuid } + } + + /// Creates a new `UuidMarker` Component with a random uuid. + pub fn new_random() -> Self { + let uuid = Uuid::new_v4(); + UuidMarker { uuid } + } + + /// Get the current uuid. + pub fn uuid(&self) -> &Uuid { + &self.uuid + } +} + +/// Basic marker allocator for uuid. +#[derive(Clone, Debug)] +pub struct UuidMarkerAllocator { + mapping: HashMap, +} + +impl Default for UuidMarkerAllocator { + fn default() -> Self { + UuidMarkerAllocator::new() + } +} + +impl UuidMarkerAllocator { + /// Create new `UuidMarkerAllocator` which will yield `UuidMarker`s. + pub fn new() -> Self { + UuidMarkerAllocator { + mapping: HashMap::new(), + } + } +} + +impl MarkerAllocator for UuidMarkerAllocator { + fn allocate(&mut self, entity: Entity, id: Option) -> UuidMarker { + let marker = if let Some(id) = id { + UuidMarker::new(id) + } else { + UuidMarker::new_random() + }; + self.mapping.insert(marker.uuid().clone(), entity); + + marker + } + + fn retrieve_entity_internal(&self, id: Uuid) -> Option { + self.mapping.get(&id).cloned() + } + + fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage) { + // FIXME: may be too slow + self.mapping = (entities, storage) + .join() + .map(|(e, m)| (m.uuid().clone(), e)) + .collect(); + } +} diff --git a/tests/saveload.rs b/tests/saveload.rs index 1c4b36745..a663a9fc7 100644 --- a/tests/saveload.rs +++ b/tests/saveload.rs @@ -16,6 +16,8 @@ mod tests { saveload::{ConvertSaveload, Marker, U64Marker}, Builder, Entity, World, WorldExt, }; + #[cfg(feature = "uuid_entity")] + use saveload::UuidMarker; #[derive(ConvertSaveload)] struct OneFieldNamed { @@ -62,18 +64,23 @@ mod tests { fn type_check() { let mut world = World::new(); let entity = world.create_entity().build(); + type_check_internal::(entity); + #[cfg(feature = "uuid_entity")] + type_check_internal::(entity); + } - black_box::(OneFieldNamed { e: entity }); - black_box::(TwoField { a: 5, e: entity }); - black_box::(LevelTwo { + fn type_check_internal(entity: Entity) { + black_box::(OneFieldNamed { e: entity }); + black_box::(TwoField { a: 5, e: entity }); + black_box::(LevelTwo { owner: OneFieldNamed { e: entity }, }); - black_box::(OneFieldTuple(entity)); - black_box::(TwoFieldTuple(entity, 5)); + black_box::(OneFieldTuple(entity)); + black_box::(TwoFieldTuple(entity, 5)); // The derive will work for all variants // so no need to test anything but unit - black_box::(AnEnum::Unit); - black_box::(Generic(entity)); + black_box::(AnEnum::Unit); + black_box::(Generic(entity)); } fn black_box>(_item: T) {}