From a687fe708880a8299711e0dc416d420f66eca093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Lupien=20=28Jojolepro=29?= Date: Thu, 2 May 2019 17:49:33 -0400 Subject: [PATCH 1/6] Added Uuid marker, allocator and tests --- Cargo.toml | 6 ++- src/lib.rs | 2 + src/saveload/mod.rs | 4 ++ src/saveload/tests.rs | 67 ++++++++++++++++----------- src/saveload/uuid.rs | 104 ++++++++++++++++++++++++++++++++++++++++++ tests/saveload.rs | 21 +++++---- 6 files changed, 169 insertions(+), 35 deletions(-) create mode 100644 src/saveload/uuid.rs 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/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..ff5e0fab3 100644 --- a/src/saveload/mod.rs +++ b/src/saveload/mod.rs @@ -33,12 +33,16 @@ mod marker; mod ser; #[cfg(test)] mod tests; +#[cfg(feature = "uuid_entity")] +mod uuid; pub use self::{ de::DeserializeComponents, marker::{MarkedBuilder, Marker, MarkerAllocator, U64Marker, U64MarkerAllocator}, ser::SerializeComponents, }; +#[cfg(feature = "uuid_entity")] +pub use self::uuid::*; /// A struct used for deserializing entity data. #[derive(Serialize, Deserialize)] diff --git a/src/saveload/tests.rs b/src/saveload/tests.rs index 556aad1e9..53c6e6abe 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,9 @@ 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..3fd5fec1d --- /dev/null +++ b/src/saveload/uuid.rs @@ -0,0 +1,104 @@ +use std::collections::HashMap; + +use uuid::Uuid; + +use crate::{ + join::Join, + saveload::{Marker, MarkerAllocator}, + storage::{VecStorage, ReadStorage}, + 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 + } + + /// Set a new uuid. + pub fn set_uuid(&mut self, uuid: Uuid) { + self.uuid = 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..b2d73ae76 100644 --- a/tests/saveload.rs +++ b/tests/saveload.rs @@ -13,7 +13,7 @@ extern crate specs_derive; mod tests { use spocs::{ error::NoError, - saveload::{ConvertSaveload, Marker, U64Marker}, + saveload::{ConvertSaveload, Marker, U64Marker, UuidMarker}, Builder, Entity, World, WorldExt, }; @@ -62,18 +62,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) {} From 5df045999a63064fb1a03bee4a1329e31734a89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Lupien=20=28Jojolepro=29?= Date: Thu, 2 May 2019 18:12:03 -0400 Subject: [PATCH 2/6] rustfmt --- benches/big_or_small.rs | 22 ++++++++++++---------- src/saveload/mod.rs | 4 ++-- src/saveload/tests.rs | 18 ++++++++++-------- src/saveload/uuid.rs | 16 +++++----------- src/world/entity.rs | 2 +- 5 files changed, 30 insertions(+), 32 deletions(-) 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/saveload/mod.rs b/src/saveload/mod.rs index ff5e0fab3..dbd1efd4d 100644 --- a/src/saveload/mod.rs +++ b/src/saveload/mod.rs @@ -36,13 +36,13 @@ mod tests; #[cfg(feature = "uuid_entity")] mod uuid; +#[cfg(feature = "uuid_entity")] +pub use self::uuid::*; pub use self::{ de::DeserializeComponents, marker::{MarkedBuilder, Marker, MarkerAllocator, U64Marker, U64MarkerAllocator}, ser::SerializeComponents, }; -#[cfg(feature = "uuid_entity")] -pub use self::uuid::*; /// A struct used for deserializing entity data. #[derive(Serialize, Deserialize)] diff --git a/src/saveload/tests.rs b/src/saveload/tests.rs index 53c6e6abe..bed7e1117 100644 --- a/src/saveload/tests.rs +++ b/src/saveload/tests.rs @@ -36,12 +36,12 @@ mod marker_test { } fn bumps_index_after_reload_internal(allocator: M::Allocator) - where - M: Marker + Component + Clone, - ::Storage: Default, - ::Identifier: Clone + Hash + Eq, - ::Allocator: Default+Clone - { + where + M: Marker + Component + Clone, + ::Storage: Default, + ::Identifier: Clone + Hash + Eq, + ::Allocator: Default + Clone, + { let mut world = World::new(); world.add_resource(allocator.clone()); @@ -167,8 +167,10 @@ mod marker_test { } /// Ensure there are no duplicate marker .ids() in the world - fn assert_markers_are_unique(world: &mut World) - where ::Identifier: Clone + Eq + Hash { + fn assert_markers_are_unique(world: &mut World) + where + ::Identifier: Clone + Eq + Hash, + { world.exec(|(ents, markers): (Entities, ReadStorage)| { use std::collections::HashSet; diff --git a/src/saveload/uuid.rs b/src/saveload/uuid.rs index 3fd5fec1d..172d047d7 100644 --- a/src/saveload/uuid.rs +++ b/src/saveload/uuid.rs @@ -5,15 +5,13 @@ use uuid::Uuid; use crate::{ join::Join, saveload::{Marker, MarkerAllocator}, - storage::{VecStorage, ReadStorage}, - world::{ - Component, EntitiesRes, Entity, - }, + 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{ +pub struct UuidMarker { uuid: Uuid, } @@ -33,17 +31,13 @@ impl Marker for UuidMarker { impl UuidMarker { /// Creates a new `UuidMarker` Component from the specified uuid. pub fn new(uuid: Uuid) -> Self { - UuidMarker { - uuid, - } + UuidMarker { uuid } } /// Creates a new `UuidMarker` Component with a random uuid. pub fn new_random() -> Self { let uuid = Uuid::new_v4(); - UuidMarker { - uuid, - } + UuidMarker { uuid } } /// Get the current uuid. diff --git a/src/world/entity.rs b/src/world/entity.rs index 5552863d3..7feb794c1 100644 --- a/src/world/entity.rs +++ b/src/world/entity.rs @@ -3,8 +3,8 @@ use std::{ sync::atomic::{AtomicUsize, Ordering}, }; -use nonzero_signed::NonZeroI32; use hibitset::{AtomicBitSet, BitSet, BitSetOr}; +use nonzero_signed::NonZeroI32; use shred::Read; #[cfg(feature = "parallel")] From b934a0ab140b5b4162ab1805c8b6a1c3c7bf033e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Lupien=20=28Jojolepro=29?= Date: Thu, 2 May 2019 18:15:22 -0400 Subject: [PATCH 3/6] Cleanup --- src/saveload/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/saveload/mod.rs b/src/saveload/mod.rs index dbd1efd4d..b50edf2ec 100644 --- a/src/saveload/mod.rs +++ b/src/saveload/mod.rs @@ -37,7 +37,7 @@ mod tests; mod uuid; #[cfg(feature = "uuid_entity")] -pub use self::uuid::*; +pub use self::uuid::{UuidMarker, UuidMarkerAllocator); pub use self::{ de::DeserializeComponents, marker::{MarkedBuilder, Marker, MarkerAllocator, U64Marker, U64MarkerAllocator}, From c5ce9dad0432aee4a3df6dfc862f142da70fa9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Lupien=20=28Jojolepro=29?= Date: Fri, 3 May 2019 11:37:09 -0400 Subject: [PATCH 4/6] Remove uuid setter --- src/saveload/uuid.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/saveload/uuid.rs b/src/saveload/uuid.rs index 172d047d7..901f91fda 100644 --- a/src/saveload/uuid.rs +++ b/src/saveload/uuid.rs @@ -44,11 +44,6 @@ impl UuidMarker { pub fn uuid(&self) -> &Uuid { &self.uuid } - - /// Set a new uuid. - pub fn set_uuid(&mut self, uuid: Uuid) { - self.uuid = uuid; - } } /// Basic marker allocator for uuid. From a8d72f7d04b5c349086c0e5354721254e260b083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Lupien=20=28Jojolepro=29?= Date: Fri, 3 May 2019 14:33:45 -0400 Subject: [PATCH 5/6] Fix compilation issue --- src/saveload/mod.rs | 2 +- src/world/entity.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/saveload/mod.rs b/src/saveload/mod.rs index b50edf2ec..20bb5f53e 100644 --- a/src/saveload/mod.rs +++ b/src/saveload/mod.rs @@ -37,7 +37,7 @@ mod tests; mod uuid; #[cfg(feature = "uuid_entity")] -pub use self::uuid::{UuidMarker, UuidMarkerAllocator); +pub use self::uuid::{UuidMarker, UuidMarkerAllocator}; pub use self::{ de::DeserializeComponents, marker::{MarkedBuilder, Marker, MarkerAllocator, U64Marker, U64MarkerAllocator}, diff --git a/src/world/entity.rs b/src/world/entity.rs index 1ab5ffd61..7a8cd9be8 100644 --- a/src/world/entity.rs +++ b/src/world/entity.rs @@ -5,7 +5,6 @@ use std::{ }; use hibitset::{AtomicBitSet, BitSet, BitSetOr}; -use nonzero_signed::NonZeroI32; use shred::Read; #[cfg(feature = "parallel")] From 68f627025a2224ecb307fa64e186f08bb69e21b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Lupien=20=28Jojolepro=29?= Date: Fri, 3 May 2019 15:11:59 -0400 Subject: [PATCH 6/6] fix compilation in test --- tests/saveload.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/saveload.rs b/tests/saveload.rs index b2d73ae76..a663a9fc7 100644 --- a/tests/saveload.rs +++ b/tests/saveload.rs @@ -13,9 +13,11 @@ extern crate specs_derive; mod tests { use spocs::{ error::NoError, - saveload::{ConvertSaveload, Marker, U64Marker, UuidMarker}, + saveload::{ConvertSaveload, Marker, U64Marker}, Builder, Entity, World, WorldExt, }; + #[cfg(feature = "uuid_entity")] + use saveload::UuidMarker; #[derive(ConvertSaveload)] struct OneFieldNamed {