Skip to content

Commit

Permalink
Merge #584
Browse files Browse the repository at this point in the history
584: Uuid Marker r=torkleyy a=jojolepro

## Checklist

* [X] I've added tests for all code changes and additions (where applicable)
* [ ] I've added a demonstration of the new feature to one or more examples
* [ ] I've updated the book to reflect my changes
* [X] Usage of new public items is shown in the API docs

## API changes

Added UuidMarker and UuidMarkerAllocator.
They work the same way their U64 counterparts do.

Added "uuid_entity" feature gate.



Co-authored-by: Joël Lupien (Jojolepro) <[email protected]>
  • Loading branch information
bors[bot] and AnneKitsune committed May 3, 2019
2 parents f4c8ab1 + 68f6270 commit fe12ec9
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 44 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,20 @@ 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"] }

[features]
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" }
Expand Down
22 changes: 12 additions & 10 deletions benches/big_or_small.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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() {
Expand All @@ -72,8 +67,14 @@ fn bench_big(b: &mut Bencher) {
world.register::<Big>();

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();
}

Expand All @@ -95,7 +96,8 @@ fn bench_small(b: &mut Bencher) {
world.register::<Small2>();

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();
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
4 changes: 4 additions & 0 deletions src/saveload/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
69 changes: 43 additions & 26 deletions src/saveload/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
extern crate ron;

use std::hash::Hash;

use super::*;
use crate::{
error::{Error, NoError},
Expand Down Expand Up @@ -28,38 +30,50 @@ mod marker_test {
/// deserialization.
#[test]
fn bumps_index_after_reload() {
bumps_index_after_reload_internal::<U64Marker>(U64MarkerAllocator::new());
#[cfg(feature = "uuid_entity")]
bumps_index_after_reload_internal::<UuidMarker>(UuidMarkerAllocator::new());
}

fn bumps_index_after_reload_internal<M>(allocator: M::Allocator)
where
M: Marker + Component + Clone,
<M as Component>::Storage: Default,
<M as Marker>::Identifier: Clone + Hash + Eq,
<M as Marker>::Allocator: Default + Clone,
{
let mut world = World::new();

world.add_resource(U64MarkerAllocator::new());
world.add_resource(allocator.clone());
world.register::<A>();
world.register::<B>();
world.register::<U64Marker>();
world.register::<M>();

world
.create_entity()
.with(A(32))
.with(B(true))
.marked::<U64Marker>()
.marked::<M>()
.build();
world
.create_entity()
.with(A(64))
.with(B(false))
.marked::<U64Marker>()
.marked::<M>()
.build();

// Serialze all entities
// Serialize all entities
let mut ser = ron::ser::Serializer::new(Some(Default::default()), true);

world.exec(
|(ents, comp_a, comp_b, markers, _alloc): (
Entities,
ReadStorage<A>,
ReadStorage<B>,
ReadStorage<U64Marker>,
Read<U64MarkerAllocator>,
ReadStorage<M>,
Read<M::Allocator>,
)| {
SerializeComponents::<NoError, U64Marker>::serialize(
SerializeComponents::<NoError, M>::serialize(
&(&comp_a, &comp_b),
&ents,
&markers,
Expand All @@ -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::<A>();
world.register::<B>();
world.register::<U64Marker>();
world.register::<M>();

world.exec(
|(ents, comp_a, comp_b, mut markers, mut alloc): (
Entities,
WriteStorage<A>,
WriteStorage<B>,
WriteStorage<U64Marker>,
Write<U64MarkerAllocator>,
WriteStorage<M>,
Write<M::Allocator>,
)| {
DeserializeComponents::<Error, _>::deserialize(
&mut (comp_a, comp_b),
Expand All @@ -101,19 +115,19 @@ mod marker_test {
);

// Two marked entities should be deserialized
assert_marked_entity_count(&mut world, 2);
assert_marked_entity_count::<M>(&mut world, 2);

// Queue lazy creation of 2 more entities
world.exec(|(ents, lazy): (Entities, Read<LazyUpdate>)| {
lazy.create_entity(&ents)
.with(A(128))
.with(B(false))
.marked::<U64Marker>()
.marked::<M>()
.build();
lazy.create_entity(&ents)
.with(A(256))
.with(B(true))
.marked::<U64Marker>()
.marked::<M>()
.build();
});

Expand All @@ -122,39 +136,42 @@ mod marker_test {
.create_entity()
.with(A(512))
.with(B(false))
.marked::<U64Marker>()
.marked::<M>()
.build();
world
.create_entity()
.with(A(1024))
.with(B(true))
.marked::<U64Marker>()
.marked::<M>()
.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::<M>(&mut world, 4);
assert_markers_are_unique::<M>(&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::<M>(&mut world, 6);
assert_markers_are_unique::<M>(&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<U64Marker>)| {
fn assert_marked_entity_count<M: Marker>(world: &mut World, count: usize) {
world.exec(|(ents, markers): (Entities, ReadStorage<M>)| {
let marked_entity_count = (&ents, &markers).join().count();

assert_eq!(marked_entity_count, count);
});
}

/// Ensure there are no duplicate marker .ids() in the world
fn assert_markers_are_unique(world: &mut World) {
world.exec(|(ents, markers): (Entities, ReadStorage<U64Marker>)| {
fn assert_markers_are_unique<M: Marker>(world: &mut World)
where
<M as Marker>::Identifier: Clone + Eq + Hash,
{
world.exec(|(ents, markers): (Entities, ReadStorage<M>)| {
use std::collections::HashSet;

let marker_ids: Vec<_> = (&ents, &markers)
Expand Down
93 changes: 93 additions & 0 deletions src/saveload/uuid.rs
Original file line number Diff line number Diff line change
@@ -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<Self>;
}

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<Uuid, Entity>,
}

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<UuidMarker> for UuidMarkerAllocator {
fn allocate(&mut self, entity: Entity, id: Option<Uuid>) -> 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<Entity> {
self.mapping.get(&id).cloned()
}

fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage<UuidMarker>) {
// FIXME: may be too slow
self.mapping = (entities, storage)
.join()
.map(|(e, m)| (m.uuid().clone(), e))
.collect();
}
}
21 changes: 14 additions & 7 deletions tests/saveload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -62,18 +64,23 @@ mod tests {
fn type_check() {
let mut world = World::new();
let entity = world.create_entity().build();
type_check_internal::<U64Marker>(entity);
#[cfg(feature = "uuid_entity")]
type_check_internal::<UuidMarker>(entity);
}

black_box::<U64Marker, _>(OneFieldNamed { e: entity });
black_box::<U64Marker, _>(TwoField { a: 5, e: entity });
black_box::<U64Marker, _>(LevelTwo {
fn type_check_internal<M: Marker>(entity: Entity) {
black_box::<M, _>(OneFieldNamed { e: entity });
black_box::<M, _>(TwoField { a: 5, e: entity });
black_box::<M, _>(LevelTwo {
owner: OneFieldNamed { e: entity },
});
black_box::<U64Marker, _>(OneFieldTuple(entity));
black_box::<U64Marker, _>(TwoFieldTuple(entity, 5));
black_box::<M, _>(OneFieldTuple(entity));
black_box::<M, _>(TwoFieldTuple(entity, 5));
// The derive will work for all variants
// so no need to test anything but unit
black_box::<U64Marker, _>(AnEnum::Unit);
black_box::<U64Marker, _>(Generic(entity));
black_box::<M, _>(AnEnum::Unit);
black_box::<M, _>(Generic(entity));
}

fn black_box<M, T: ConvertSaveload<M>>(_item: T) {}
Expand Down

0 comments on commit fe12ec9

Please sign in to comment.