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

Add an ability to save and load entities. #275

Merged
merged 4 commits into from
Oct 4, 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
19 changes: 19 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,22 @@ impl StdError for WrongGeneration {
(e.g. because the entity has been deleted)"
}
}


/// An error type which cannot be instantiated.
/// Used as a placeholder for associated error types if
/// something cannot fail.
#[derive(Debug)]
pub enum NoError {}
Copy link
Member

Choose a reason for hiding this comment

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

Should be PhantomError

Copy link
Member Author

Choose a reason for hiding this comment

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

Should be ! but it isn't stable yet.
Why PhantomError anyway?

Copy link
Member

Choose a reason for hiding this comment

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

Well, first, because you are implementing Error for NoError, which already looks like a hack. Secondly, because the "phantom" is rather established for such things that can't be instantiated, so PhantomError is crystal clear here.

Copy link
Member Author

Choose a reason for hiding this comment

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

PhantomData is unit type and can be instantiated. And it is an example from std.
IDK any other examples.

Copy link
Member

Choose a reason for hiding this comment

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

I don't have a strong opinion here, @kvark do you insist on this request?

Copy link
Member

Choose a reason for hiding this comment

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

Nope. I think I might have confused a bit the semantics of phantom types. Pretty sure I saw them used in the context where one couldn't create instances (enum MyPhantomType {}), but I don't want to push hard on that here. It's fine to ship as is.


impl Display for NoError {
fn fmt(&self, _: &mut Formatter) -> FmtResult {
match *self {}
}
}

impl StdError for NoError {
fn description(&self) -> &str {
match *self {}
}
}
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ extern crate futures;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[cfg(feature = "serde")]
#[macro_use]
extern crate shred_derive;

#[cfg(feature = "rudy")]
extern crate rudy;
Expand All @@ -214,6 +217,9 @@ pub use world::{Component, CreateIter, CreateIterAtomic, EntitiesRes, Entity, En
#[cfg(feature = "common")]
pub mod common;

#[cfg(feature = "serde")]
pub mod saveload;

#[cfg(feature = "rudy")]
pub use storage::RudyStorage;

Expand Down
148 changes: 148 additions & 0 deletions src/saveload/de.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use std::fmt::{self, Display, Formatter};
use std::marker::PhantomData;

use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, SeqAccess, Visitor};

use {Entities, FetchMut, WriteStorage};

use saveload::{Components, EntityData, Storages};
use saveload::marker::{Marker, MarkerAllocator};


/// Wrapper for `Entity` and tuple of `WriteStorage`s that implements `serde::Deserialize`
struct DeserializeEntity<'a, 'b: 'a, M: Marker, E, T: Components<M::Identifier, E>> {
entities: &'a Entities<'b>,
storages: &'a mut <T as Storages<'b>>::WriteStorages,
markers: &'a mut WriteStorage<'b, M>,
allocator: &'a mut FetchMut<'b, M::Allocator>,
pd: PhantomData<(E, T)>,
}

impl<'de, 'a, 'b: 'a, M, E, T> DeserializeSeed<'de> for DeserializeEntity<'a, 'b, M, E, T>
where
M: Marker,
E: Display,
T: Components<M::Identifier, E>,
{
type Value = ();
fn deserialize<D>(self, deserializer: D) -> Result<(), D::Error>
where
D: Deserializer<'de>,
{
let DeserializeEntity {
entities,
storages,
markers,
allocator,
..
} = self;
Copy link
Member

Choose a reason for hiding this comment

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

nit: why do you need to deconstruct self here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Otherwise whole self gets borrowed by closure.

let data = EntityData::<M, E, T>::deserialize(deserializer)?;
let entity = allocator.get_marked(data.marker.id(), entities, markers);
markers
.get_mut(entity)
.ok_or("Allocator is broken")
.map_err(de::Error::custom)?
.update(data.marker);
let ids = |marker: M::Identifier| Some(allocator.get_marked(marker, entities, markers));
match T::load(entity, data.components, storages, ids) {
Ok(()) => Ok(()),
Err(err) => Err(de::Error::custom(err)),
}
}
}

/// Wrapper for `Entities` and tuple of `WriteStorage`s that implements `serde::de::Visitor`
struct VisitEntities<'a, 'b: 'a, M: Marker, E, T: Components<M::Identifier, E>> {
entities: &'a Entities<'b>,
storages: &'a mut <T as Storages<'b>>::WriteStorages,
markers: &'a mut WriteStorage<'b, M>,
allocator: &'a mut FetchMut<'b, M::Allocator>,
pd: PhantomData<(E, T)>,
}

impl<'de, 'a, 'b: 'a, M, E, T> Visitor<'de> for VisitEntities<'a, 'b, M, E, T>
where
M: Marker,
E: Display,
T: Components<M::Identifier, E>,
{
type Value = ();

fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "Sequence of serialized entities")
}

fn visit_seq<A>(mut self, mut seq: A) -> Result<(), A::Error>
where
A: SeqAccess<'de>,
{
while seq.next_element_seed(DeserializeEntity {
entities: self.entities,
storages: self.storages,
markers: self.markers,
allocator: self.allocator,
pd: self.pd,
})?
.is_some()
{}

Ok(())
}
}


/// Deserialize entities
pub fn deserialize<'a, 'de, D, M, E, T>(
entities: &Entities<'a>,
storages: &mut <T as Storages<'a>>::WriteStorages,
markers: &mut WriteStorage<'a, M>,
allocator: &mut FetchMut<'a, M::Allocator>,
deserializer: D,
) -> Result<(), D::Error>
where
M: Marker,
E: Display,
T: Components<M::Identifier, E>,
D: Deserializer<'de>,
{
deserializer.deserialize_seq(VisitEntities::<M, E, T> {
entities,
storages,
markers,
allocator,
pd: PhantomData,
})
}


/// `DeerializeSeed` implementation for `World`
#[derive(SystemData)]
pub struct WorldDeserialize<'a, M: Marker, E, T: Components<M::Identifier, E>> {
entities: Entities<'a>,
storages: <T as Storages<'a>>::WriteStorages,
markers: WriteStorage<'a, M>,
allocator: FetchMut<'a, M::Allocator>,
pd: PhantomData<E>,
}

impl<'de, 'a, M, E, T> DeserializeSeed<'de> for WorldDeserialize<'a, M, E, T>
where
M: Marker,
E: Display,
T: Components<M::Identifier, E>,
{
type Value = ();

fn deserialize<D>(mut self, deserializer: D) -> Result<(), D::Error>
where
D: Deserializer<'de>,
{
deserialize::<D, M, E, T>(
&mut self.entities,
&mut self.storages,
&mut self.markers,
&mut self.allocator,
deserializer,
)
}
}
171 changes: 171 additions & 0 deletions src/saveload/details.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use std::error::Error;

use serde::de::DeserializeOwned;
use serde::ser::Serialize;

use {Component, Entity, ReadStorage, SystemData, WriteStorage};

use error::NoError;
use saveload::marker::Marker;

#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
pub struct EntityData<M: Marker, E, T: Components<M::Identifier, E>> {
pub marker: M,
pub components: T::Data,
}

/// This trait should be implemented in order to allow component
/// to be serializeble with `SerializeSystem`.
/// It is automatically implemented for all
/// `Component + DeserializeOwned + Serialize + Copy`
pub trait SaveLoadComponent<M>: Component {
/// Serializable data representation for component
type Data: Serialize + DeserializeOwned;

/// Error may occur duing serialization or deserialization of component
type Error: Error;

/// Convert this component into serializable form (`Data`) using
/// entity to marker mapping function
fn save<F>(&self, ids: F) -> Result<Self::Data, Self::Error>
Copy link
Member

Choose a reason for hiding this comment

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

hmm, on the other hand, save/load methods do sound good :)

where
F: FnMut(Entity) -> Option<M>;

/// Convert this component into deserializable form (`Data`) using
/// marker to entity mapping function
fn load<F>(data: Self::Data, ids: F) -> Result<Self, Self::Error>
where
F: FnMut(M) -> Option<Entity>;
}

impl<C, M> SaveLoadComponent<M> for C
where
C: Component + DeserializeOwned + Serialize + Copy,
{
type Data = Self;
type Error = NoError;

fn save<F>(&self, _ids: F) -> Result<Self::Data, NoError> {
Ok(*self)
}

fn load<F>(data: Self, _ids: F) -> Result<Self, NoError> {
Ok(data)
}
}

/// Helper trait defines storages tuples for components tuple
pub trait Storages<'a> {
/// Storages for read
type ReadStorages: SystemData<'a> + 'a;
/// Storages for write
type WriteStorages: SystemData<'a> + 'a;
}

/// This trait is implemented by any tuple where all elements are
/// `Component + Serialize + DeserializeOwned`
pub trait Components<M, E>: for<'a> Storages<'a> {
/// Serializable and deserializable intermediate representation
type Data: Serialize + DeserializeOwned;

/// Saves `Component`s of entity into `Data` serializable representation
fn save<'a, F>(
entity: Entity,
storages: &<Self as Storages<'a>>::ReadStorages,
ids: F,
) -> Result<Self::Data, E>
where
F: FnMut(Entity) -> Option<M>;

/// Loads `Component`s to entity from `Data` deserializable representation
fn load<'a, F>(
entity: Entity,
components: Self::Data,
storages: &mut <Self as Storages<'a>>::WriteStorages,
ids: F,
) -> Result<(), E>
where
F: FnMut(M) -> Option<Entity>;
}

macro_rules! impl_components {
($($a:ident|$b:ident),*) => {
impl<'a, $($a),*> Storages<'a> for ($($a,)*)
where $(
$a: Component,
)*
{
type ReadStorages = ($(ReadStorage<'a, $a>,)*);
type WriteStorages = ($(WriteStorage<'a, $a>,)*);
}

impl<M, E $(,$a)*> Components<M, E> for ($($a,)*)
where $(
$a: SaveLoadComponent<M>,
E: From<$a::Error>,
)*
{
type Data = ($(Option<$a::Data>,)*);

#[allow(unused_variables, unused_mut, non_snake_case)]
fn save<'a, F>(entity: Entity, storages: &($(ReadStorage<'a, $a>,)*), mut ids: F)
-> Result<($(Option<$a::Data>,)*), E>
where F: FnMut(Entity) -> Option<M>
{
let ($(ref $b,)*) = *storages;
Ok(($(
$b.get(entity).map(|c| c.save(&mut ids).map(Some)).unwrap_or(Ok(None))?,
)*))
}

#[allow(unused_variables, unused_mut, non_snake_case)]
fn load<'a, F>(entity: Entity, components: ($(Option<$a::Data>,)*),
storages: &mut ($(WriteStorage<'a, $a>,)*), mut ids: F)
-> Result<(), E>
where F: FnMut(M) -> Option<Entity>
{
let ($($a,)*) = components;
let ($(ref mut $b,)*) = *storages;
$(
if let Some(a) = $a {
$b.insert(entity, $a::load(a, &mut ids)?);
} else {
$b.remove(entity);
}
)*
Ok(())
}
}

// Recursivly implement for smaller tuple
impl_components!(@ $($a|$b),*);
};

// List depleted. End of recursion
(@) => {};

// Cut head of the list and call macro again
(@ $ah:ident|$bh:ident $(,$a:ident|$b:ident)*) => {
// Call again for tail
impl_components!($($a|$b),*);
};
}

impl_components!(
LA | LB,
MA | MB,
NA | NB,
OA | OB,
PA | PB,
QA | QB,
RA | RB,
SA | SB,
TA | TB,
UA | UB,
VA | VB,
WA | WB,
XA | XB,
YA | YB,
ZA | ZB
);
Loading